From e02c9e1086fb78b0e78039fbd62d5980c1bd0c8b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 5 Jan 2023 17:51:38 +0000 Subject: [PATCH 001/657] Beta-reduce directly applied PolymorphicFunction --- .../tools/dotc/inlines/InlineReducer.scala | 5 +- .../tools/dotc/transform/BetaReduce.scala | 53 ++++++++++++++----- .../tools/dotc/transform/InlinePatterns.scala | 3 +- .../tools/dotc/transform/PickleQuotes.scala | 2 +- .../quoted/runtime/impl/QuotesImpl.scala | 3 +- .../backend/jvm/InlineBytecodeTests.scala | 26 +++++++++ 6 files changed, 73 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala index 42e86b71eff8..80a1546e0356 100644 --- a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala +++ b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala @@ -165,12 +165,13 @@ class InlineReducer(inliner: Inliner)(using Context): */ def betaReduce(tree: Tree)(using Context): Tree = tree match { case Apply(Select(cl, nme.apply), args) if defn.isFunctionType(cl.tpe) => - val bindingsBuf = new mutable.ListBuffer[ValDef] + val bindingsBuf = new mutable.ListBuffer[DefTree] def recur(cl: Tree): Option[Tree] = cl match case Block((ddef : DefDef) :: Nil, closure: Closure) if ddef.symbol == closure.meth.symbol => ddef.tpe.widen match case mt: MethodType if ddef.paramss.head.length == args.length => - Some(BetaReduce.reduceApplication(ddef, args, bindingsBuf)) + // TODO beta reduce PolyType + Some(BetaReduce.reduceApplication(ddef, List(args), bindingsBuf)) case _ => None case Block(stats, expr) if stats.forall(isPureBinding) => recur(expr).map(cpy.Block(cl)(stats, _)) diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index 7ac3dc972ad1..00bb83175627 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -40,13 +40,16 @@ class BetaReduce extends MiniPhase: override def transformApply(app: Apply)(using Context): Tree = app.fun match case Select(fn, nme.apply) if defn.isFunctionType(fn.tpe) => - val app1 = BetaReduce(app, fn, app.args) + val app1 = BetaReduce(app, fn, List(app.args)) + if app1 ne app then report.log(i"beta reduce $app -> $app1") + app1 + case TypeApply(Select(fn, nme.apply), targs) if fn.tpe.typeSymbol eq defn.PolyFunctionClass => + val app1 = BetaReduce(app, fn, List(targs, app.args)) if app1 ne app then report.log(i"beta reduce $app -> $app1") app1 case _ => app - object BetaReduce: import ast.tpd._ @@ -54,18 +57,24 @@ object BetaReduce: val description: String = "reduce closure applications" /** Beta-reduces a call to `fn` with arguments `argSyms` or returns `tree` */ - def apply(original: Tree, fn: Tree, args: List[Tree])(using Context): Tree = + def apply(original: Tree, fn: Tree, argss: List[List[Tree]])(using Context): Tree = fn match case Typed(expr, _) => - BetaReduce(original, expr, args) + BetaReduce(original, expr, argss) case Block((anonFun: DefDef) :: Nil, closure: Closure) => - BetaReduce(anonFun, args) + BetaReduce(anonFun, argss) + case Block((TypeDef(_, template: Template)) :: Nil, Typed(Apply(Select(New(_), _), _), _)) if template.constr.rhs.isEmpty => + template.body match + case (anonFun: DefDef) :: Nil => + BetaReduce(anonFun, argss) + case _ => + original case Block(stats, expr) => - val tree = BetaReduce(original, expr, args) + val tree = BetaReduce(original, expr, argss) if tree eq original then original else cpy.Block(fn)(stats, tree) case Inlined(call, bindings, expr) => - val tree = BetaReduce(original, expr, args) + val tree = BetaReduce(original, expr, argss) if tree eq original then original else cpy.Inlined(fn)(call, bindings, tree) case _ => @@ -73,16 +82,32 @@ object BetaReduce: end apply /** Beta-reduces a call to `ddef` with arguments `args` */ - def apply(ddef: DefDef, args: List[Tree])(using Context) = - val bindings = new ListBuffer[ValDef]() - val expansion1 = reduceApplication(ddef, args, bindings) + def apply(ddef: DefDef, argss: List[List[Tree]])(using Context) = + val bindings = new ListBuffer[DefTree]() + val expansion1 = reduceApplication(ddef, argss, bindings) val bindings1 = bindings.result() seq(bindings1, expansion1) /** Beta-reduces a call to `ddef` with arguments `args` and registers new bindings */ - def reduceApplication(ddef: DefDef, args: List[Tree], bindings: ListBuffer[ValDef])(using Context): Tree = - val vparams = ddef.termParamss.iterator.flatten.toList + def reduceApplication(ddef: DefDef, argss: List[List[Tree]], bindings: ListBuffer[DefTree])(using Context): Tree = + assert(argss.size == 1 || argss.size == 2) + val targs = if argss.size == 2 then argss.head else Nil + val args = argss.last + val tparams = ddef.leadingTypeParams + val vparams = ddef.termParamss.flatten + assert(targs.hasSameLengthAs(tparams)) assert(args.hasSameLengthAs(vparams)) + + val targSyms = + for (targ, tparam) <- targs.zip(tparams) yield + targ.tpe.dealias match + case ref @ TypeRef(NoPrefix, _) => + ref.symbol + case _ => + val binding = TypeDef(newSymbol(ctx.owner, tparam.name, EmptyFlags, targ.tpe, coord = targ.span)).withSpan(targ.span) + bindings += binding + binding.symbol + val argSyms = for (arg, param) <- args.zip(vparams) yield arg.tpe.dealias match @@ -99,8 +124,8 @@ object BetaReduce: val expansion = TreeTypeMap( oldOwners = ddef.symbol :: Nil, newOwners = ctx.owner :: Nil, - substFrom = vparams.map(_.symbol), - substTo = argSyms + substFrom = (tparams ::: vparams).map(_.symbol), + substTo = targSyms ::: argSyms ).transform(ddef.rhs) val expansion1 = new TreeMap { diff --git a/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala b/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala index 6edb60a77245..b1d3bb68cf76 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala @@ -51,11 +51,12 @@ class InlinePatterns extends MiniPhase: case Apply(App(fn, argss), args) => (fn, argss :+ args) case _ => (app, Nil) + // TODO merge with BetaReduce.scala private def betaReduce(tree: Apply, fn: Tree, name: Name, args: List[Tree])(using Context): Tree = fn match case Block(TypeDef(_, template: Template) :: Nil, Apply(Select(New(_),_), Nil)) if template.constr.rhs.isEmpty => template.body match - case List(ddef @ DefDef(`name`, _, _, _)) => BetaReduce(ddef, args) + case List(ddef @ DefDef(`name`, _, _, _)) => BetaReduce(ddef, List(args)) case _ => tree case _ => tree diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 21fc27cec0dd..732d8d41b8d6 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -322,7 +322,7 @@ object PickleQuotes { } val Block(List(ddef: DefDef), _) = splice: @unchecked // TODO: beta reduce inner closure? Or wait until BetaReduce phase? - BetaReduce(ddef, spliceArgs).select(nme.apply).appliedTo(args(2).asInstance(quotesType)) + BetaReduce(ddef, List(spliceArgs)).select(nme.apply).appliedTo(args(2).asInstance(quotesType)) } CaseDef(Literal(Constant(idx)), EmptyTree, rhs) } diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 1ec13ba832c9..7cbf05871e08 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -362,8 +362,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Term extends TermModule: def betaReduce(tree: Term): Option[Term] = tree match + // TODO support TypeApply. Would fix #15968. case app @ tpd.Apply(tpd.Select(fn, nme.apply), args) if dotc.core.Symbols.defn.isFunctionType(fn.tpe) => - val app1 = dotc.transform.BetaReduce(app, fn, args) + val app1 = dotc.transform.BetaReduce(app, fn, List(args)) if app1 eq app then None else Some(app1.withSpan(tree.span)) case tpd.Block(Nil, expr) => diff --git a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala index 33e898718b33..a9cec06880a2 100644 --- a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala @@ -582,6 +582,32 @@ class InlineBytecodeTests extends DottyBytecodeTest { } } + @Test def beta_reduce_polymorphic_function = { + val source = """class Test: + | def test = + | ([Z] => (arg: Z) => { val a: Z = arg; a }).apply[Int](2) + """.stripMargin + + checkBCode(source) { dir => + val clsIn = dir.lookupName("Test.class", directory = false).input + val clsNode = loadClassNode(clsIn) + + val fun = getMethod(clsNode, "test") + val instructions = instructionsFromMethod(fun) + val expected = + List( + Op(ICONST_2), + VarOp(ISTORE, 1), + VarOp(ILOAD, 1), + Op(IRETURN) + ) + + assert(instructions == expected, + "`i was not properly beta-reduced in `test`\n" + diffInstructions(instructions, expected)) + + } + } + @Test def i9456 = { val source = """class Foo { | def test: Int = inline2(inline1(2.+)) From 223c8b7d697638b9a1fd971552753b78d78da0e7 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 9 Jan 2023 09:32:33 +0100 Subject: [PATCH 002/657] `Expr.betaReduce` support for polymorphic function Fixes #15968 --- .../scala/quoted/runtime/impl/QuotesImpl.scala | 5 ++++- tests/run-macros/i15968.check | 5 +++++ tests/run-macros/i15968/Macro_1.scala | 15 +++++++++++++++ tests/run-macros/i15968/Test_2.scala | 3 +++ 4 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/run-macros/i15968.check create mode 100644 tests/run-macros/i15968/Macro_1.scala create mode 100644 tests/run-macros/i15968/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 7cbf05871e08..277cfd9f8cfb 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -362,11 +362,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Term extends TermModule: def betaReduce(tree: Term): Option[Term] = tree match - // TODO support TypeApply. Would fix #15968. case app @ tpd.Apply(tpd.Select(fn, nme.apply), args) if dotc.core.Symbols.defn.isFunctionType(fn.tpe) => val app1 = dotc.transform.BetaReduce(app, fn, List(args)) if app1 eq app then None else Some(app1.withSpan(tree.span)) + case app @ tpd.Apply(tpd.TypeApply(tpd.Select(fn, nme.apply), targs), args) if fn.tpe.typeSymbol eq dotc.core.Symbols.defn.PolyFunctionClass => + val app1 = dotc.transform.BetaReduce(app, fn, List(targs, app.args)) + if app1 eq app then None + else Some(app1.withSpan(tree.span)) case tpd.Block(Nil, expr) => for e <- betaReduce(expr) yield tpd.cpy.Block(tree)(Nil, e) case tpd.Inlined(_, Nil, expr) => diff --git a/tests/run-macros/i15968.check b/tests/run-macros/i15968.check new file mode 100644 index 000000000000..c7f3847d404c --- /dev/null +++ b/tests/run-macros/i15968.check @@ -0,0 +1,5 @@ +{ + type Z = java.lang.String + "foo".toString() +} +"foo".toString() diff --git a/tests/run-macros/i15968/Macro_1.scala b/tests/run-macros/i15968/Macro_1.scala new file mode 100644 index 000000000000..ea2728840d6e --- /dev/null +++ b/tests/run-macros/i15968/Macro_1.scala @@ -0,0 +1,15 @@ +import scala.quoted.* + +inline def macroPolyFun[A](inline arg: A, inline f: [Z] => Z => String): String = + ${ macroPolyFunImpl[A]('arg, 'f) } + +private def macroPolyFunImpl[A: Type](arg: Expr[A], f: Expr[[Z] => Z => String])(using Quotes): Expr[String] = + Expr(Expr.betaReduce('{ $f($arg) }).show) + + +inline def macroFun[A](inline arg: A, inline f: A => String): String = + ${ macroFunImpl[A]('arg, 'f) } + +private def macroFunImpl[A: Type](arg: Expr[A], f: Expr[A => String])(using Quotes): Expr[String] = + Expr(Expr.betaReduce('{ $f($arg) }).show) + diff --git a/tests/run-macros/i15968/Test_2.scala b/tests/run-macros/i15968/Test_2.scala new file mode 100644 index 000000000000..6c6826f96b34 --- /dev/null +++ b/tests/run-macros/i15968/Test_2.scala @@ -0,0 +1,3 @@ +@main def Test: Unit = + println(macroPolyFun("foo", [Z] => (arg: Z) => arg.toString)) + println(macroFun("foo", arg => arg.toString)) From d37902d9983397e1bf6ce6ad34bdb00788fbb661 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 9 Jan 2023 10:21:46 +0100 Subject: [PATCH 003/657] Support polymorphic beta-reduction while inlining --- .../tools/dotc/inlines/InlineReducer.scala | 41 ------------ .../dotty/tools/dotc/inlines/Inliner.scala | 7 ++- .../tools/dotc/transform/BetaReduce.scala | 62 +++++++++++++++++++ .../inline-beta-reduce-polyfunction.check | 7 +++ .../inline-beta-reduce-polyfunction.scala | 5 ++ 5 files changed, 78 insertions(+), 44 deletions(-) create mode 100644 tests/run-macros/inline-beta-reduce-polyfunction.check create mode 100644 tests/run-macros/inline-beta-reduce-polyfunction.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala index 80a1546e0356..547160340238 100644 --- a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala +++ b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala @@ -12,8 +12,6 @@ import NameKinds.{InlineAccessorName, InlineBinderName, InlineScrutineeName} import config.Printers.inlining import util.SimpleIdentityMap -import dotty.tools.dotc.transform.BetaReduce - import collection.mutable /** A utility class offering methods for rewriting inlined code */ @@ -150,45 +148,6 @@ class InlineReducer(inliner: Inliner)(using Context): binding1.withSpan(call.span) } - /** Rewrite an application - * - * ((x1, ..., xn) => b)(e1, ..., en) - * - * to - * - * val/def x1 = e1; ...; val/def xn = en; b - * - * where `def` is used for call-by-name parameters. However, we shortcut any NoPrefix - * refs among the ei's directly without creating an intermediate binding. - * - * This variant of beta-reduction preserves the integrity of `Inlined` tree nodes. - */ - def betaReduce(tree: Tree)(using Context): Tree = tree match { - case Apply(Select(cl, nme.apply), args) if defn.isFunctionType(cl.tpe) => - val bindingsBuf = new mutable.ListBuffer[DefTree] - def recur(cl: Tree): Option[Tree] = cl match - case Block((ddef : DefDef) :: Nil, closure: Closure) if ddef.symbol == closure.meth.symbol => - ddef.tpe.widen match - case mt: MethodType if ddef.paramss.head.length == args.length => - // TODO beta reduce PolyType - Some(BetaReduce.reduceApplication(ddef, List(args), bindingsBuf)) - case _ => None - case Block(stats, expr) if stats.forall(isPureBinding) => - recur(expr).map(cpy.Block(cl)(stats, _)) - case Inlined(call, bindings, expr) if bindings.forall(isPureBinding) => - recur(expr).map(cpy.Inlined(cl)(call, bindings, _)) - case Typed(expr, tpt) => - recur(expr) - case _ => None - recur(cl) match - case Some(reduced) => - seq(bindingsBuf.result(), reduced).withSpan(tree.span) - case None => - tree - case _ => - tree - } - /** The result type of reducing a match. It consists optionally of a list of bindings * for the pattern-bound variables and the RHS of the selected case. * Returns `None` if no case was selected. diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index c71648984664..40b0b383a4e3 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -21,6 +21,7 @@ import collection.mutable import reporting.trace import util.Spans.Span import dotty.tools.dotc.transform.Splicer +import dotty.tools.dotc.transform.BetaReduce import quoted.QuoteUtils import scala.annotation.constructorOnly @@ -811,7 +812,7 @@ class Inliner(val call: tpd.Tree)(using Context): case Quoted(Spliced(inner)) => inner case _ => tree val locked = ctx.typerState.ownedVars - val res = cancelQuotes(constToLiteral(betaReduce(super.typedApply(tree, pt)))) match { + val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match { case res: Apply if res.symbol == defn.QuotedRuntime_exprSplice && StagingContext.level == 0 && !hasInliningErrors => @@ -824,7 +825,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree = val locked = ctx.typerState.ownedVars - val tree1 = inlineIfNeeded(constToLiteral(betaReduce(super.typedTypeApply(tree, pt))), pt, locked) + val tree1 = inlineIfNeeded(constToLiteral(BetaReduce(super.typedTypeApply(tree, pt))), pt, locked) if tree1.symbol.isQuote then ctx.compilationUnit.needsStaging = true tree1 @@ -1005,7 +1006,7 @@ class Inliner(val call: tpd.Tree)(using Context): super.transform(t1) case t: Apply => val t1 = super.transform(t) - if (t1 `eq` t) t else reducer.betaReduce(t1) + if (t1 `eq` t) t else BetaReduce(t1) case Block(Nil, expr) => super.transform(expr) case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index 00bb83175627..32e26bcbc883 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -56,6 +56,68 @@ object BetaReduce: val name: String = "betaReduce" val description: String = "reduce closure applications" + /** Rewrite an application + * + * ((x1, ..., xn) => b)(e1, ..., en) + * + * to + * + * val/def x1 = e1; ...; val/def xn = en; b + * + * where `def` is used for call-by-name parameters. However, we shortcut any NoPrefix + * refs among the ei's directly without creating an intermediate binding. + * + * Similarly, rewrites type applications + * + * ([X1, ..., Xm] => (x1, ..., xn) => b).apply[T1, .., Tm](e1, ..., en) + * + * to + * + * type X1 = T1; ...; type Xm = Tm;val/def x1 = e1; ...; val/def xn = en; b + * + * This beta-reduction preserves the integrity of `Inlined` tree nodes. + */ + def apply(tree: Tree)(using Context): Tree = + val bindingsBuf = new ListBuffer[DefTree] + def recur(fn: Tree, argss: List[List[Tree]]): Option[Tree] = fn match + case Block((ddef : DefDef) :: Nil, closure: Closure) if ddef.symbol == closure.meth.symbol => + ddef.tpe.widen match // TODO can these guards be removed? + case mt: MethodType if ddef.paramss.head.length == argss.head.length => + Some(reduceApplication(ddef, argss, bindingsBuf)) + case _ => None + case Block((TypeDef(_, template: Template)) :: Nil, Typed(Apply(Select(New(_), _), _), _)) if template.constr.rhs.isEmpty => + template.body match + case (ddef: DefDef) :: Nil => + ddef.tpe.widen match // TODO can these guards be removed? + case mt: MethodType if ddef.paramss.head.length == argss.head.length => + Some(reduceApplication(ddef, argss, bindingsBuf)) + case mt: PolyType if ddef.paramss.head.length == argss.head.length && ddef.paramss.last.length == argss.last.length => + Some(reduceApplication(ddef, argss, bindingsBuf)) + case _ => None + case _ => None + case Block(stats, expr) if stats.forall(isPureBinding) => + recur(expr, argss).map(cpy.Block(fn)(stats, _)) + case Inlined(call, bindings, expr) if bindings.forall(isPureBinding) => + recur(expr, argss).map(cpy.Inlined(fn)(call, bindings, _)) + case Typed(expr, tpt) => + recur(expr, argss) + case _ => None + tree match + case Apply(Select(fn, nme.apply), args) if defn.isFunctionType(fn.tpe) => + recur(fn, List(args)) match + case Some(reduced) => + seq(bindingsBuf.result(), reduced).withSpan(tree.span) + case None => + tree + case Apply(TypeApply(Select(fn, nme.apply), targs), args) if fn.tpe.typeSymbol eq dotc.core.Symbols.defn.PolyFunctionClass => + recur(fn, List(targs, args)) match + case Some(reduced) => + seq(bindingsBuf.result(), reduced).withSpan(tree.span) + case None => + tree + case _ => + tree + /** Beta-reduces a call to `fn` with arguments `argSyms` or returns `tree` */ def apply(original: Tree, fn: Tree, argss: List[List[Tree]])(using Context): Tree = fn match diff --git a/tests/run-macros/inline-beta-reduce-polyfunction.check b/tests/run-macros/inline-beta-reduce-polyfunction.check new file mode 100644 index 000000000000..7793e273864f --- /dev/null +++ b/tests/run-macros/inline-beta-reduce-polyfunction.check @@ -0,0 +1,7 @@ +{ + type X = Int + { + println(1) + 1 + } +} diff --git a/tests/run-macros/inline-beta-reduce-polyfunction.scala b/tests/run-macros/inline-beta-reduce-polyfunction.scala new file mode 100644 index 000000000000..60ef889e7260 --- /dev/null +++ b/tests/run-macros/inline-beta-reduce-polyfunction.scala @@ -0,0 +1,5 @@ +transparent inline def foo(inline f: [X] => X => X): Int = f[Int](1) + +@main def Test: Unit = + val code = compiletime.codeOf(foo([X] => (x: X) => { println(x); x })) + println(code) \ No newline at end of file From a6de5e4e7eae9d550dd07962b9efddb62075e6a0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 9 Jan 2023 10:36:58 +0100 Subject: [PATCH 004/657] Reuse beta-reduction logic --- .../tools/dotc/transform/BetaReduce.scala | 40 ++----------------- .../quoted/runtime/impl/QuotesImpl.scala | 13 ++---- 2 files changed, 8 insertions(+), 45 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index 32e26bcbc883..565d04599ca5 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -38,17 +38,10 @@ class BetaReduce extends MiniPhase: override def description: String = BetaReduce.description - override def transformApply(app: Apply)(using Context): Tree = app.fun match - case Select(fn, nme.apply) if defn.isFunctionType(fn.tpe) => - val app1 = BetaReduce(app, fn, List(app.args)) - if app1 ne app then report.log(i"beta reduce $app -> $app1") - app1 - case TypeApply(Select(fn, nme.apply), targs) if fn.tpe.typeSymbol eq defn.PolyFunctionClass => - val app1 = BetaReduce(app, fn, List(targs, app.args)) - if app1 ne app then report.log(i"beta reduce $app -> $app1") - app1 - case _ => - app + override def transformApply(app: Apply)(using Context): Tree = + val app1 = BetaReduce(app) + if app1 ne app then report.log(i"beta reduce $app -> $app1") + app1 object BetaReduce: import ast.tpd._ @@ -118,31 +111,6 @@ object BetaReduce: case _ => tree - /** Beta-reduces a call to `fn` with arguments `argSyms` or returns `tree` */ - def apply(original: Tree, fn: Tree, argss: List[List[Tree]])(using Context): Tree = - fn match - case Typed(expr, _) => - BetaReduce(original, expr, argss) - case Block((anonFun: DefDef) :: Nil, closure: Closure) => - BetaReduce(anonFun, argss) - case Block((TypeDef(_, template: Template)) :: Nil, Typed(Apply(Select(New(_), _), _), _)) if template.constr.rhs.isEmpty => - template.body match - case (anonFun: DefDef) :: Nil => - BetaReduce(anonFun, argss) - case _ => - original - case Block(stats, expr) => - val tree = BetaReduce(original, expr, argss) - if tree eq original then original - else cpy.Block(fn)(stats, tree) - case Inlined(call, bindings, expr) => - val tree = BetaReduce(original, expr, argss) - if tree eq original then original - else cpy.Inlined(fn)(call, bindings, tree) - case _ => - original - end apply - /** Beta-reduces a call to `ddef` with arguments `args` */ def apply(ddef: DefDef, argss: List[List[Tree]])(using Context) = val bindings = new ListBuffer[DefTree]() diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 277cfd9f8cfb..15ed447fd680 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -362,20 +362,15 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Term extends TermModule: def betaReduce(tree: Term): Option[Term] = tree match - case app @ tpd.Apply(tpd.Select(fn, nme.apply), args) if dotc.core.Symbols.defn.isFunctionType(fn.tpe) => - val app1 = dotc.transform.BetaReduce(app, fn, List(args)) - if app1 eq app then None - else Some(app1.withSpan(tree.span)) - case app @ tpd.Apply(tpd.TypeApply(tpd.Select(fn, nme.apply), targs), args) if fn.tpe.typeSymbol eq dotc.core.Symbols.defn.PolyFunctionClass => - val app1 = dotc.transform.BetaReduce(app, fn, List(targs, app.args)) - if app1 eq app then None - else Some(app1.withSpan(tree.span)) case tpd.Block(Nil, expr) => for e <- betaReduce(expr) yield tpd.cpy.Block(tree)(Nil, e) case tpd.Inlined(_, Nil, expr) => betaReduce(expr) case _ => - None + val tree1 = dotc.transform.BetaReduce(tree) + if tree1 eq tree then None + else Some(tree1.withSpan(tree.span)) + end Term given TermMethods: TermMethods with From 43c6ba7d05d3f16e16c650ec54b531402aa641bd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 9 Jan 2023 11:04:56 +0100 Subject: [PATCH 005/657] Refactor BetaReduce logic --- .../src/dotty/tools/dotc/transform/BetaReduce.scala | 13 +------------ .../dotty/tools/dotc/transform/InlinePatterns.scala | 12 +++++++++--- .../dotty/tools/dotc/transform/PickleQuotes.scala | 5 ++++- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index 565d04599ca5..c33a1d6d8b10 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -111,22 +111,11 @@ object BetaReduce: case _ => tree - /** Beta-reduces a call to `ddef` with arguments `args` */ - def apply(ddef: DefDef, argss: List[List[Tree]])(using Context) = - val bindings = new ListBuffer[DefTree]() - val expansion1 = reduceApplication(ddef, argss, bindings) - val bindings1 = bindings.result() - seq(bindings1, expansion1) - /** Beta-reduces a call to `ddef` with arguments `args` and registers new bindings */ def reduceApplication(ddef: DefDef, argss: List[List[Tree]], bindings: ListBuffer[DefTree])(using Context): Tree = - assert(argss.size == 1 || argss.size == 2) - val targs = if argss.size == 2 then argss.head else Nil - val args = argss.last + val (targs, args) = argss.flatten.partition(_.isType) val tparams = ddef.leadingTypeParams val vparams = ddef.termParamss.flatten - assert(targs.hasSameLengthAs(tparams)) - assert(args.hasSameLengthAs(vparams)) val targSyms = for (targ, tparam) <- targs.zip(tparams) yield diff --git a/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala b/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala index b1d3bb68cf76..798f34757b35 100644 --- a/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala +++ b/compiler/src/dotty/tools/dotc/transform/InlinePatterns.scala @@ -8,6 +8,8 @@ import Symbols._, Contexts._, Types._, Decorators._ import NameOps._ import Names._ +import scala.collection.mutable.ListBuffer + /** Rewrite an application * * {new { def unapply(x0: X0)(x1: X1,..., xn: Xn) = b }}.unapply(y0)(y1, ..., yn) @@ -38,7 +40,7 @@ class InlinePatterns extends MiniPhase: if app.symbol.name.isUnapplyName && !app.tpe.isInstanceOf[MethodicType] then app match case App(Select(fn, name), argss) => - val app1 = betaReduce(app, fn, name, argss.flatten) + val app1 = betaReduce(app, fn, name, argss) if app1 ne app then report.log(i"beta reduce $app -> $app1") app1 case _ => @@ -52,11 +54,15 @@ class InlinePatterns extends MiniPhase: case _ => (app, Nil) // TODO merge with BetaReduce.scala - private def betaReduce(tree: Apply, fn: Tree, name: Name, args: List[Tree])(using Context): Tree = + private def betaReduce(tree: Apply, fn: Tree, name: Name, argss: List[List[Tree]])(using Context): Tree = fn match case Block(TypeDef(_, template: Template) :: Nil, Apply(Select(New(_),_), Nil)) if template.constr.rhs.isEmpty => template.body match - case List(ddef @ DefDef(`name`, _, _, _)) => BetaReduce(ddef, List(args)) + case List(ddef @ DefDef(`name`, _, _, _)) => + val bindings = new ListBuffer[DefTree]() + val expansion1 = BetaReduce.reduceApplication(ddef, argss, bindings) + val bindings1 = bindings.result() + seq(bindings1, expansion1) case _ => tree case _ => tree diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 732d8d41b8d6..c56bac4d66af 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -322,7 +322,10 @@ object PickleQuotes { } val Block(List(ddef: DefDef), _) = splice: @unchecked // TODO: beta reduce inner closure? Or wait until BetaReduce phase? - BetaReduce(ddef, List(spliceArgs)).select(nme.apply).appliedTo(args(2).asInstance(quotesType)) + BetaReduce( + splice + .select(nme.apply).appliedToArgs(spliceArgs)) + .select(nme.apply).appliedTo(args(2).asInstance(quotesType)) } CaseDef(Literal(Constant(idx)), EmptyTree, rhs) } From f7f61c088d41297277993fc15586a81b3dfb531a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 9 Jan 2023 11:29:03 +0100 Subject: [PATCH 006/657] Update documentation --- compiler/src/dotty/tools/dotc/transform/BetaReduce.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index c33a1d6d8b10..2a9cb7c8e9a7 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -13,13 +13,14 @@ import scala.collection.mutable.ListBuffer /** Rewrite an application * - * (((x1, ..., xn) => b): T)(y1, ..., yn) + * (([X1, ..., Xm] => (x1, ..., xn) => b): T)[T1, ..., Tm](y1, ..., yn) * * where * * - all yi are pure references without a prefix * - the closure can also be contextual or erased, but cannot be a SAM type - * _ the type ascription ...: T is optional + * - the type parameters Xi and type arguments Ti are optional + * - the type ascription ...: T is optional * * to * From 60c2d70aadcb620e495832a630dda0670fa93f0f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 9 Jan 2023 11:39:35 +0100 Subject: [PATCH 007/657] Remove unnecessary guards Number of arguments should be correct or it would not typecheck. --- .../src/dotty/tools/dotc/transform/BetaReduce.scala | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index 2a9cb7c8e9a7..bdfd4ec76515 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -75,19 +75,10 @@ object BetaReduce: val bindingsBuf = new ListBuffer[DefTree] def recur(fn: Tree, argss: List[List[Tree]]): Option[Tree] = fn match case Block((ddef : DefDef) :: Nil, closure: Closure) if ddef.symbol == closure.meth.symbol => - ddef.tpe.widen match // TODO can these guards be removed? - case mt: MethodType if ddef.paramss.head.length == argss.head.length => - Some(reduceApplication(ddef, argss, bindingsBuf)) - case _ => None + Some(reduceApplication(ddef, argss, bindingsBuf)) case Block((TypeDef(_, template: Template)) :: Nil, Typed(Apply(Select(New(_), _), _), _)) if template.constr.rhs.isEmpty => template.body match - case (ddef: DefDef) :: Nil => - ddef.tpe.widen match // TODO can these guards be removed? - case mt: MethodType if ddef.paramss.head.length == argss.head.length => - Some(reduceApplication(ddef, argss, bindingsBuf)) - case mt: PolyType if ddef.paramss.head.length == argss.head.length && ddef.paramss.last.length == argss.last.length => - Some(reduceApplication(ddef, argss, bindingsBuf)) - case _ => None + case (ddef: DefDef) :: Nil => Some(reduceApplication(ddef, argss, bindingsBuf)) case _ => None case Block(stats, expr) if stats.forall(isPureBinding) => recur(expr, argss).map(cpy.Block(fn)(stats, _)) From 4523a6f4c7acc2811da3643d95b27768c08635b5 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Thu, 5 Jan 2023 13:23:55 +0100 Subject: [PATCH 008/657] Fix #16405 - wildcards prematurely resolving to Nothing This was a problem because it could it get in the way of some metaprogramming techniques. The main issue was the fact that when typing functions, the type inference would first look at the types from the source method (resolving type wildcards to Nothing) and only after that, it could look at the target method. Now, in the case of wildcards we save that fact for later (while still resolving the prototype parameter to Nothing) and we in that case we prioritize according to the target method, after which we fallback to the default procedure. --- .../src/dotty/tools/dotc/typer/Typer.scala | 47 +++++++++++++------ tests/run/16405.scala | 31 ++++++++++++ 2 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 tests/run/16405.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 79a4e9afbc7f..44ae2ac7a46c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1197,7 +1197,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer ) end typedIf - /** Decompose function prototype into a list of parameter prototypes and a result prototype + /** Decompose function prototype into a list of parameter prototypes, an optional list + * describing whether the parameter prototypes come from WildcardTypes, and a result prototype * tree, using WildcardTypes where a type is not known. * For the result type we do this even if the expected type is not fully * defined, which is a bit of a hack. But it's needed to make the following work @@ -1206,7 +1207,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * def double(x: Char): String = s"$x$x" * "abc" flatMap double */ - private def decomposeProtoFunction(pt: Type, defaultArity: Int, pos: SrcPos)(using Context): (List[Type], untpd.Tree) = { + private def decomposeProtoFunction(pt: Type, defaultArity: Int, pos: SrcPos)(using Context): (List[Type], Option[List[Boolean]], untpd.Tree) = { def typeTree(tp: Type) = tp match { case _: WildcardType => new untpd.InferredTypeTree() case _ => untpd.InferredTypeTree(tp) @@ -1234,18 +1235,26 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // if expected parameter type(s) are wildcards, approximate from below. // if expected result type is a wildcard, approximate from above. // this can type the greatest set of admissible closures. - (pt1.argTypesLo.init, typeTree(interpolateWildcards(pt1.argTypesHi.last))) + // However, we still keep the information on whether expected parameter types were + // wildcards, in case of types inferred from target being more specific + + val fromWildcards = pt1.argInfos.init.map{ + case bounds @ TypeBounds(nt, at) if nt == defn.NothingType && at == defn.AnyType => true + case bounds => false + } + + (pt1.argTypesLo.init, Some(fromWildcards), typeTree(interpolateWildcards(pt1.argTypesHi.last))) case RefinedType(parent, nme.apply, mt @ MethodTpe(_, formals, restpe)) if defn.isNonRefinedFunction(parent) && formals.length == defaultArity => - (formals, untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef)))) + (formals, None, untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef)))) case SAMType(mt @ MethodTpe(_, formals, restpe)) => - (formals, + (formals, None, if (mt.isResultDependent) untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef))) else typeTree(restpe)) case _ => - (List.tabulate(defaultArity)(alwaysWildcardType), untpd.TypeTree()) + (List.tabulate(defaultArity)(alwaysWildcardType), None, untpd.TypeTree()) } } } @@ -1267,7 +1276,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * If both attempts fail, return `NoType`. */ def inferredFromTarget( - param: untpd.ValDef, formal: Type, calleeType: Type, paramIndex: Name => Int)(using Context): Type = + param: untpd.ValDef, formal: Type, calleeType: Type, paramIndex: Name => Int, isWildcardParam: Boolean)(using Context): Type = val target = calleeType.widen match case mtpe: MethodType => val pos = paramIndex(param.name) @@ -1280,7 +1289,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else NoType case _ => NoType if target.exists then formal <:< target - if isFullyDefined(formal, ForceDegree.flipBottom) then formal + if !isWildcardParam && isFullyDefined(formal, ForceDegree.flipBottom) then formal else if target.exists && isFullyDefined(target, ForceDegree.flipBottom) then target else NoType @@ -1457,7 +1466,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => } - val (protoFormals, resultTpt) = decomposeProtoFunction(pt, params.length, tree.srcPos) + val (protoFormals, areWildcardParams, resultTpt) = decomposeProtoFunction(pt, params.length, tree.srcPos) def protoFormal(i: Int): Type = if (protoFormals.length == params.length) protoFormals(i) @@ -1500,13 +1509,21 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (!param.tpt.isEmpty) param else val formal = protoFormal(i) + val isWildcardParam = areWildcardParams.map(list => if i < list.length then list(i) else false).getOrElse(false) val knownFormal = isFullyDefined(formal, ForceDegree.failBottom) - val paramType = - if knownFormal then formal - else inferredFromTarget(param, formal, calleeType, paramIndex) - .orElse(errorType(AnonymousFunctionMissingParamType(param, tree, formal), param.srcPos)) + // Since decomposeProtoFunction eagerly approximates function arguments + // from below, then in the case that the argument was also identified as + // a wildcard type we try to prioritize inferring from target, if possible. + // See issue 16405 (tests/run/16405.scala) + val (usingFormal, paramType) = + if !isWildcardParam && knownFormal then (true, formal) + else + val fromTarget = inferredFromTarget(param, formal, calleeType, paramIndex, isWildcardParam) + if fromTarget.exists then (false, fromTarget) + else if knownFormal then (true, formal) + else (false, errorType(AnonymousFunctionMissingParamType(param, tree, formal), param.srcPos)) val paramTpt = untpd.TypedSplice( - (if knownFormal then InferredTypeTree() else untpd.TypeTree()) + (if usingFormal then InferredTypeTree() else untpd.TypeTree()) .withType(paramType.translateFromRepeated(toArray = false)) .withSpan(param.span.endPos) ) @@ -1577,7 +1594,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer typedMatchFinish(tree, tpd.EmptyTree, defn.ImplicitScrutineeTypeRef, cases1, pt) } else { - val (protoFormals, _) = decomposeProtoFunction(pt, 1, tree.srcPos) + val (protoFormals, _, _) = decomposeProtoFunction(pt, 1, tree.srcPos) val checkMode = if (pt.isRef(defn.PartialFunctionClass)) desugar.MatchCheck.None else desugar.MatchCheck.Exhaustive diff --git a/tests/run/16405.scala b/tests/run/16405.scala new file mode 100644 index 000000000000..fa0681683c42 --- /dev/null +++ b/tests/run/16405.scala @@ -0,0 +1,31 @@ +import scala.compiletime.summonInline + +case class TypeDesc[T](tpe: String) +object TypeDesc { + given nothing: TypeDesc[Nothing] = TypeDesc("Nothing") + given string: TypeDesc[String] = TypeDesc("String") + given int: TypeDesc[Int] = TypeDesc("Int") +} + +def exampleFn(s: String, i: Int): Unit = () + +inline def argumentTypesOf[R](fun: (_, _) => R): (TypeDesc[?], TypeDesc[?]) = { + inline fun match { + case x: ((a, b) => R) => + (scala.compiletime.summonInline[TypeDesc[a]], scala.compiletime.summonInline[TypeDesc[b]]) + } +} +inline def argumentTypesOfNoWildCard[A, B, R](fun: (A, B) => R): (TypeDesc[?], TypeDesc[?]) = argumentTypesOf(fun) +inline def argumentTypesOfAllWildCard(fun: (?, ?) => ?): (TypeDesc[?], TypeDesc[?]) = argumentTypesOf(fun) + +object Test { + def main(args: Array[String]): Unit = { + val expected = (TypeDesc.string, TypeDesc.int) + assert(argumentTypesOf(exampleFn) == expected) + assert(argumentTypesOf(exampleFn(_, _)) == expected) + assert(argumentTypesOfNoWildCard(exampleFn) == expected) + assert(argumentTypesOfNoWildCard(exampleFn(_, _)) == expected) + assert(argumentTypesOfAllWildCard(exampleFn) == expected) + assert(argumentTypesOfAllWildCard(exampleFn(_, _)) == expected) + } +} \ No newline at end of file From 80c155691ea996d708448fd44c01bec29caf6be6 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 11 Jan 2023 14:29:33 +0100 Subject: [PATCH 009/657] Use `Object.toString` for `quoted.{Expr, Type}` --- compiler/src/scala/quoted/runtime/impl/ExprImpl.scala | 2 -- compiler/src/scala/quoted/runtime/impl/TypeImpl.scala | 2 -- 2 files changed, 4 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/ExprImpl.scala b/compiler/src/scala/quoted/runtime/impl/ExprImpl.scala index b33ba14b9e70..5fac91124187 100644 --- a/compiler/src/scala/quoted/runtime/impl/ExprImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/ExprImpl.scala @@ -20,6 +20,4 @@ final class ExprImpl(val tree: tpd.Tree, val scope: Scope) extends Expr[Any] { } override def hashCode(): Int = tree.hashCode() - - override def toString: String = "'{ ... }" } diff --git a/compiler/src/scala/quoted/runtime/impl/TypeImpl.scala b/compiler/src/scala/quoted/runtime/impl/TypeImpl.scala index 36da30e112c8..d4cea83efde8 100644 --- a/compiler/src/scala/quoted/runtime/impl/TypeImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/TypeImpl.scala @@ -14,6 +14,4 @@ final class TypeImpl(val typeTree: tpd.Tree, val scope: Scope) extends Type[?] { } override def hashCode(): Int = typeTree.hashCode() - - override def toString: String = "Type.of[...]" } From 027923ce325ef1c4b487c2aeaf1c41a9771de5e3 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 17 Jan 2023 12:22:47 +0100 Subject: [PATCH 010/657] Fix check whether classTag can be generated for match types The previous check, meant to address #15618, tested whether all alternatives of a match type had the same class tag and only then permitted a classtag for the match type. This was the wrong test. It did not work for recursive match types, because it could lead to stack overflow for them. And it did not take into account that match types could be reduced. A better test is to simply declare that match types themselves don't have a stable erasure, just like TypeBounds don't have a stable erasure. If we find an applied type with a match type as definition, we proceed to its translucent superytype, which will try a match type reduction. If that succeeds we produce the classtag of the redux. If not, we end up with an unreduced matchtype and refuse to generate a classtag for it. Fixes #16706 Fixes #16707 --- .../src/dotty/tools/dotc/core/TypeErasure.scala | 5 +++-- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- .../dotty/tools/dotc/typer/Synthesizer.scala | 9 +-------- tests/neg/i15618.check | 6 ++++++ tests/neg/i15618.scala | 8 ++++++++ tests/pos/i16706.scala | 17 +++++++++++++++++ tests/pos/i16707.scala | 11 +++++++++++ 7 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 tests/pos/i16706.scala create mode 100644 tests/pos/i16707.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 0e67fd40991b..67839d10c8cd 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -520,8 +520,9 @@ object TypeErasure { case _: ClassInfo => true case _ => false } - case tp: TypeParamRef => false - case tp: TypeBounds => false + case _: TypeParamRef => false + case _: TypeBounds => false + case _: MatchType => false case tp: TypeProxy => hasStableErasure(tp.translucentSuperType) case tp: AndType => hasStableErasure(tp.tp1) && hasStableErasure(tp.tp2) case tp: OrType => hasStableErasure(tp.tp1) && hasStableErasure(tp.tp2) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 0c5614e29a60..78364a3d21f0 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1071,7 +1071,7 @@ object Types { * @param relaxedCheck if true type `Null` becomes a subtype of non-primitive value types in TypeComparer. * @param matchLoosely if true the types `=> T` and `()T` are seen as overriding each other. * @param checkClassInfo if true we check that ClassInfos are within bounds of abstract types - * + * * @param isSubType a function used for checking subtype relationships. */ final def overrides(that: Type, relaxedCheck: Boolean, matchLoosely: => Boolean, checkClassInfo: Boolean = true, diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 71efc27bf673..1a7a4b97855b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -52,14 +52,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): if defn.SpecialClassTagClasses.contains(sym) then classTagModul.select(sym.name.toTermName).withSpan(span) else - def clsOfType(tp: Type): Type = tp.dealias.underlyingMatchType match - case matchTp: MatchType => - matchTp.alternatives.map(clsOfType) match - case ct1 :: cts if cts.forall(ct1 == _) => ct1 - case _ => NoType - case _ => - escapeJavaArray(erasure(tp)) - val ctype = clsOfType(tp) + val ctype = escapeJavaArray(erasure(tp)) if ctype.exists then classTagModul.select(nme.apply) .appliedToType(tp) diff --git a/tests/neg/i15618.check b/tests/neg/i15618.check index 91f557b12dcf..16fadcd9b886 100644 --- a/tests/neg/i15618.check +++ b/tests/neg/i15618.check @@ -16,3 +16,9 @@ | | case Float32 => Float | case Int32 => Int +-- [E172] Type Error: tests/neg/i15618.scala:21:33 --------------------------------------------------------------------- +21 | def toArray: Array[T] = Array() // error + | ^ + | No ClassTag available for T + | + | where: T is a type in class Tensor2 with bounds <: Int | Float diff --git a/tests/neg/i15618.scala b/tests/neg/i15618.scala index fd38c8c48f6b..087bc462b211 100644 --- a/tests/neg/i15618.scala +++ b/tests/neg/i15618.scala @@ -16,8 +16,16 @@ class Tensor[T <: DType](dtype: T): def toSeq: Seq[ScalaType[T]] = Seq() def toArray: Array[ScalaType[T]] = Array() // error +class Tensor2[T <: Int | Float](dtype: T): + def toSeq: Seq[T] = Seq() + def toArray: Array[T] = Array() // error + @main def Test = val t = Tensor(Float32) // Tensor[Float32] println(t.toSeq.headOption) // works, Seq[Float] println(t.toArray.headOption) // ClassCastException + + val t2 = Tensor2(0.0f) // Tensor2[Float] + println(t.toSeq.headOption) + println(t.toArray.headOption) diff --git a/tests/pos/i16706.scala b/tests/pos/i16706.scala new file mode 100644 index 000000000000..87fd015c69bb --- /dev/null +++ b/tests/pos/i16706.scala @@ -0,0 +1,17 @@ +import scala.deriving.Mirror +import scala.reflect.ClassTag + +type TupleUnionLub[T <: Tuple, Lub, Acc <: Lub] <: Lub = T match { + case (h & Lub) *: t => TupleUnionLub[t, Lub, Acc | h] + case EmptyTuple => Acc +} + +transparent inline given derived[A]( + using m: Mirror.SumOf[A], + idClassTag: ClassTag[TupleUnionLub[m.MirroredElemTypes, A, Nothing]] +): Unit = () + +sealed trait Foo +case class FooA(a: Int) extends Foo + +val instance = derived[Foo] // error \ No newline at end of file diff --git a/tests/pos/i16707.scala b/tests/pos/i16707.scala new file mode 100644 index 000000000000..6181471f5246 --- /dev/null +++ b/tests/pos/i16707.scala @@ -0,0 +1,11 @@ +import scala.deriving.Mirror +import scala.reflect.ClassTag + +transparent inline given derived[A]( + using m: Mirror.ProductOf[A], + idClassTag: ClassTag[Tuple.Union[m.MirroredElemTypes]] + ): Unit = ??? + +case class Foo(a: Int) + +val instance = derived[Foo] // error From db2d3eb9d04dc10f8db8649541d73059b2d91f3b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 17 Jan 2023 14:04:17 +0100 Subject: [PATCH 011/657] Support beta reduction of functions with opaque types --- .../tools/dotc/transform/BetaReduce.scala | 2 ++ .../backend/jvm/InlineBytecodeTests.scala | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index bdfd4ec76515..97dc4697db6d 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -86,6 +86,8 @@ object BetaReduce: recur(expr, argss).map(cpy.Inlined(fn)(call, bindings, _)) case Typed(expr, tpt) => recur(expr, argss) + case TypeApply(Select(expr, nme.asInstanceOfPM), List(tpt)) => + recur(expr, argss) case _ => None tree match case Apply(Select(fn, nme.apply), args) if defn.isFunctionType(fn.tpe) => diff --git a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala index a9cec06880a2..5f9318c0c3f0 100644 --- a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala @@ -608,6 +608,37 @@ class InlineBytecodeTests extends DottyBytecodeTest { } } + @Test def beta_reduce_function_of_opaque_types = { + val source = """object foo: + | opaque type T = Int + | inline def apply(inline op: T => T): T = op(2) + | + |class Test: + | def test = foo { n => n } + """.stripMargin + + checkBCode(source) { dir => + val clsIn = dir.lookupName("Test.class", directory = false).input + val clsNode = loadClassNode(clsIn) + + val fun = getMethod(clsNode, "test") + val instructions = instructionsFromMethod(fun) + val expected = + List( + Field(GETSTATIC, "foo$", "MODULE$", "Lfoo$;"), + VarOp(ASTORE, 1), + VarOp(ALOAD, 1), + VarOp(ASTORE, 2), + Op(ICONST_2), + Op(IRETURN), + ) + + assert(instructions == expected, + "`i was not properly beta-reduced in `test`\n" + diffInstructions(instructions, expected)) + + } + } + @Test def i9456 = { val source = """class Foo { | def test: Int = inline2(inline1(2.+)) From b439deb62cf76f56ea9edbbcd709b9411dda2b98 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 19 Jan 2023 21:56:53 +0000 Subject: [PATCH 012/657] Don't capture wildcards if in closure or by-name --- .../src/dotty/tools/dotc/core/Types.scala | 2 ++ .../dotty/tools/dotc/typer/ProtoTypes.scala | 10 ++++++++- .../src/dotty/tools/dotc/typer/Typer.scala | 4 ++++ tests/neg/t9419.scala | 22 +++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/neg/t9419.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 0c5614e29a60..403fe7021152 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4470,6 +4470,8 @@ object Types { def hasWildcardArg(using Context): Boolean = args.exists(isBounds) + def hasCaptureConversionArg(using Context): Boolean = args.exists(_.typeSymbol == defn.TypeBox_CAP) + def derivedAppliedType(tycon: Type, args: List[Type])(using Context): Type = if ((tycon eq this.tycon) && (args eq this.args)) this else tycon.appliedTo(args) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 8ba842ad695f..53c4022417a3 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -13,6 +13,7 @@ import Decorators._ import Uniques._ import inlines.Inlines import config.Printers.typr +import ErrorReporting.* import util.SourceFile import TypeComparer.necessarySubType @@ -492,7 +493,14 @@ object ProtoTypes { val targ = cacheTypedArg(arg, typer.typedUnadapted(_, wideFormal, locked)(using argCtx), force = true) - typer.adapt(targ, wideFormal, locked) + val targ1 = typer.adapt(targ, wideFormal, locked) + if wideFormal eq formal then targ1 + else targ1.tpe match + case tp: AppliedType if tp.hasCaptureConversionArg => + errorTree(targ1, + em"""argument for by-name parameter contains capture conversion skolem types: + |$tp""") + case _ => targ1 } /** The type of the argument `arg`, or `NoType` if `arg` has not been typed before diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3384c27c0194..54eb8bc2ab1e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1568,6 +1568,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else if ((tree.tpt `eq` untpd.ContextualEmptyTree) && mt.paramNames.isEmpty) // Note implicitness of function in target type since there are no method parameters that indicate it. TypeTree(defn.FunctionOf(Nil, mt.resType, isContextual = true, isErased = false)) + else if mt.resType.match { case tp: AppliedType => tp.hasCaptureConversionArg case _ => false } then + errorTree(tree, + em"""cannot turn method type $mt into closure + |because it has capture conversion skolem types""") else EmptyTree } diff --git a/tests/neg/t9419.scala b/tests/neg/t9419.scala new file mode 100644 index 000000000000..75534e8311c6 --- /dev/null +++ b/tests/neg/t9419.scala @@ -0,0 +1,22 @@ +trait Magic[S]: + def init: S + def step(s: S): String + +object IntMagic extends Magic[Int]: + def init = 0 + def step(s: Int): String = (s - 1).toString + +object StrMagic extends Magic[String]: + def init = "hi" + def step(s: String): String = s.reverse + +object Main: + def onestep[T](m: () => Magic[T]): String = m().step(m().init) + def unostep[T](m: => Magic[T]): String = m.step(m.init) + + val iter: Iterator[Magic[?]] = Iterator(IntMagic, StrMagic) + + // was: class java.lang.String cannot be cast to class java.lang.Integer + def main(args: Array[String]): Unit = + onestep(() => iter.next()) // error + unostep(iter.next()) // error From df0d9eadf789b1315a1c73522c5642127cdbdeff Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 17 Nov 2022 15:51:15 +0100 Subject: [PATCH 013/657] Disallow local term references in staged types Fixes #16355 Fixes #15917 --- .../dotc/transform/PCPCheckAndHeal.scala | 8 +++-- tests/neg-macros/i15917.scala | 6 ++++ tests/neg-macros/i16355a.scala | 35 +++++++++++++++++++ tests/neg-macros/i16355b.scala | 4 +++ 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 tests/neg-macros/i15917.scala create mode 100644 tests/neg-macros/i16355a.scala create mode 100644 tests/neg-macros/i16355b.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index 1d0ed035df09..4669dc86982d 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -176,8 +176,10 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( /** If the type refers to a locally defined symbol (either directly, or in a pickled type), * check that its staging level matches the current level. * - Static types and term are allowed at any level. - * - If a type reference is used a higher level, then it is inconsistent. Will attempt to heal before failing. - * - If a term reference is used a different level, then it is inconsistent. + * - If a type reference is used a higher level, then it is inconsistent. + * Will attempt to heal before failing. + * - If a term reference is used a higher level, then it is inconsistent. + * It can not be healed because the term will not exists in the any future stage. * * If `T` is a reference to a type at the wrong level, try to heal it by replacing it with * a type tag of type `quoted.Type[T]`. @@ -206,6 +208,8 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( tryHeal(prefix.symbol, tp, pos) case _ => mapOver(tp) + case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) => + levelError(tp.symbol, tp, pos) case tp: ThisType if level != -1 && level != levelOf(tp.cls) => levelError(tp.cls, tp, pos) case tp: AnnotatedType => diff --git a/tests/neg-macros/i15917.scala b/tests/neg-macros/i15917.scala new file mode 100644 index 000000000000..3eecc38b21f9 --- /dev/null +++ b/tests/neg-macros/i15917.scala @@ -0,0 +1,6 @@ +import scala.quoted.* + +def m(using Quotes): Expr[Option[_]] = + val s = 3 + type st = s.type + '{ Some(${ Expr(s) }: st) } // error diff --git a/tests/neg-macros/i16355a.scala b/tests/neg-macros/i16355a.scala new file mode 100644 index 000000000000..8870b7777263 --- /dev/null +++ b/tests/neg-macros/i16355a.scala @@ -0,0 +1,35 @@ +//> using scala "3.2.1" +import scala.quoted.Expr +import scala.quoted.Type +import scala.quoted.quotes +import scala.quoted.Quotes + +object macros { + + inline transparent def mkNames[A]: List[Any] = ${ mkNamesImpl[A] } + + def mkNamesImpl[A: Type](using Quotes): Expr[List[Any]] = { + import quotes.reflect._ + + val fieldNames = TypeRepr.of[A].typeSymbol.declaredFields.map(_.name) + + val types = fieldNames + .map { f => + val t1 = ConstantType(StringConstant(f)) + t1.asType match { + case '[t1Type] => TypeRepr.of[(t1Type, "aa")] + } + } + .reduceLeft[TypeRepr](OrType(_, _)) + + types.asType match { + case '[ttt] => + Expr.ofList[ttt]( + fieldNames.map { v => + Expr[(v.type, "aa")](v -> "aa").asExprOf[ttt] // error + } + ) + } + } + +} diff --git a/tests/neg-macros/i16355b.scala b/tests/neg-macros/i16355b.scala new file mode 100644 index 000000000000..763810979ddf --- /dev/null +++ b/tests/neg-macros/i16355b.scala @@ -0,0 +1,4 @@ +import scala.quoted._ +def test(v: String)(using Quotes): Any = + Type.of : Type[v.type] // error + Type.of[v.type] // error From 0893dc39cb243d95bbb7337b25f53fce48c316a1 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 20 Jan 2023 10:05:03 +0000 Subject: [PATCH 014/657] Allow wildcard capture of by-name values --- .../dotty/tools/dotc/typer/ProtoTypes.scala | 12 ++++++++--- tests/neg/t9419.scala | 4 +++- tests/pos/t9419.jackson.scala | 20 +++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/pos/t9419.jackson.scala diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 53c4022417a3..170d12b998de 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -497,9 +497,15 @@ object ProtoTypes { if wideFormal eq formal then targ1 else targ1.tpe match case tp: AppliedType if tp.hasCaptureConversionArg => - errorTree(targ1, - em"""argument for by-name parameter contains capture conversion skolem types: - |$tp""") + stripCast(targ1).tpe match + case tp: AppliedType if tp.hasWildcardArg => + errorTree(targ1, + em"""argument for by-name parameter is not a value + |and contains wildcard arguments: $tp + | + |Assign it to a val and pass that instead. + |""") + case _ => targ1 case _ => targ1 } diff --git a/tests/neg/t9419.scala b/tests/neg/t9419.scala index 75534e8311c6..e9358c0ba641 100644 --- a/tests/neg/t9419.scala +++ b/tests/neg/t9419.scala @@ -14,9 +14,11 @@ object Main: def onestep[T](m: () => Magic[T]): String = m().step(m().init) def unostep[T](m: => Magic[T]): String = m.step(m.init) - val iter: Iterator[Magic[?]] = Iterator(IntMagic, StrMagic) + val iter: Iterator[Magic[?]] = Iterator.tabulate(Int.MaxValue)(i => if i % 2 == 0 then IntMagic else StrMagic) // was: class java.lang.String cannot be cast to class java.lang.Integer def main(args: Array[String]): Unit = onestep(() => iter.next()) // error unostep(iter.next()) // error + val m = iter.next() + unostep(m) // ok, because m is a value diff --git a/tests/pos/t9419.jackson.scala b/tests/pos/t9419.jackson.scala new file mode 100644 index 000000000000..bf26c7e4c672 --- /dev/null +++ b/tests/pos/t9419.jackson.scala @@ -0,0 +1,20 @@ +// from failure in the community project +// jackson-module-scala +// in ScalaAnnotationIntrospectorModule.scala:139:12 + +import scala.language.implicitConversions + +trait EnrichedType[X]: + def value: X + +trait ClassW extends EnrichedType[Class[_]]: + def extendsScalaClass = false + +class Test: + implicit def mkClassW(c: => Class[_]): ClassW = new ClassW: + lazy val value = c + + def test1(c1: Class[_]) = c1.extendsScalaClass // ok: c1 is a value + def test2(c2: Class[_]) = mkClassW(c2).extendsScalaClass // ok: c2 is a value + // c1 in test1 goes throw adapting to find the extension method and gains the wildcard capture cast then + // c2 in test2 goes straight to typedArg, as it's already an arg, so it never gets wildcard captured From d3f4770c2e9157e96fa8a0f755457615681d1cd1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 20 Jan 2023 15:38:13 +0100 Subject: [PATCH 015/657] -Xcheck-macros: add hint when a symbol in created twice Closes #16363 --- .../src/scala/quoted/runtime/impl/QuotesImpl.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index dd6471a882bd..7f90b018bea0 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2973,12 +2973,16 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler t match case t: tpd.DefTree => val defOwner = t.symbol.owner - assert(defOwner == owner, + assert(defOwner == owner, { + val ownerName = owner.fullName + val defOwnerName = defOwner.fullName + val duplicateSymbolHint = + if ownerName == defOwnerName then "These are two different symbols instances with the same name. The symbol should have been instantiated only once.\n" + else "" s"""Tree had an unexpected owner for ${t.symbol} |Expected: $owner (${owner.fullName}) |But was: $defOwner (${defOwner.fullName}) - | - | + |$duplicateSymbolHint |The code of the definition of ${t.symbol} is |${Printer.TreeCode.show(t)} | @@ -2992,7 +2996,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler | |Tip: The owner of a tree can be changed using method `Tree.changeOwner`. |Tip: The default owner of definitions created in quotes can be changed using method `Symbol.asQuotes`. - |""".stripMargin) + |""".stripMargin + }) case _ => traverseChildren(t) }.traverse(tree) From ea13cad629f85cb3156753dc784e4af0438d0167 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 20 Jan 2023 15:53:55 -0500 Subject: [PATCH 016/657] Search for Conversions if implicit search fails --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 8 +++++++- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 9 ++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index e8029d790d0a..692e72d504ad 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2554,7 +2554,8 @@ class MissingImplicitArgument( pt: Type, where: String, paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, - ignoredInstanceNormalImport: => Option[SearchSuccess] + ignoredInstanceNormalImport: => Option[SearchSuccess], + ignoredConversions: => Set[SearchSuccess] )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): arg.tpe match @@ -2743,8 +2744,13 @@ class MissingImplicitArgument( // show all available additional info def hiddenImplicitNote(s: SearchSuccess) = i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." + def noChainConversionsNote(s: Set[SearchSuccess]): Option[String] = + Option.when(s.isEmpty)( + i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: ${s.map(_.ref.symbol.showLocated).mkString("\n")}" + ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) + .orElse(noChainConversionsNote(ignoredConversions)) .getOrElse(ctx.typer.importSuggestionAddendum(pt)) def explain(using Context) = "" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 3e0e7dd5879d..f373e0563154 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -923,7 +923,14 @@ trait Implicits: // example where searching for a nested type causes an infinite loop. None - MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport) + def ignoredConversions = arg.tpe match + case fail: SearchFailureType => + if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then + ImplicitSearch(fail.expectedType, dummyTreeOfType(WildcardType), arg.span).allImplicits + else + Set.empty + + MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, ignoredConversions) } /** A string indicating the formal parameter corresponding to a missing argument */ From fc9acd7fbd37c1e84bc178dab86f1cd0779f2caf Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 20 Jan 2023 18:47:16 -0500 Subject: [PATCH 017/657] Use ctx.implicits.eligible directly --- .../src/dotty/tools/dotc/reporting/messages.scala | 8 ++++---- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 5 +++-- compiler/test/dotty/tools/dotc/CompilationTests.scala | 1 + tests/neg/i16453.check | 7 +++++++ tests/neg/i16453.scala | 11 +++++++++++ 5 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 tests/neg/i16453.check create mode 100644 tests/neg/i16453.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 692e72d504ad..cca5dae293e2 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2555,7 +2555,7 @@ class MissingImplicitArgument( where: String, paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, ignoredInstanceNormalImport: => Option[SearchSuccess], - ignoredConversions: => Set[SearchSuccess] + ignoredConversions: => Iterable[TermRef] )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): arg.tpe match @@ -2744,9 +2744,9 @@ class MissingImplicitArgument( // show all available additional info def hiddenImplicitNote(s: SearchSuccess) = i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." - def noChainConversionsNote(s: Set[SearchSuccess]): Option[String] = - Option.when(s.isEmpty)( - i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: ${s.map(_.ref.symbol.showLocated).mkString("\n")}" + def noChainConversionsNote(s: Iterable[TermRef]): Option[String] = + Option.when(s.nonEmpty)( + i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored:${s.map(g => "\n - " + g.symbol.showLocated).mkString}" ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f373e0563154..f723a18eb241 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -926,9 +926,10 @@ trait Implicits: def ignoredConversions = arg.tpe match case fail: SearchFailureType => if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then - ImplicitSearch(fail.expectedType, dummyTreeOfType(WildcardType), arg.span).allImplicits + ctx.implicits.eligible(ViewProto(WildcardType, wildApprox(fail.expectedType))) + .collect { case c if c.isConversion => c.ref } else - Set.empty + Nil MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, ignoredConversions) } diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index b8b38cce92e4..0346662b5434 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -188,6 +188,7 @@ class CompilationTests { compileFile("tests/neg-custom-args/i13026.scala", defaultOptions.and("-print-lines")), compileFile("tests/neg-custom-args/i13838.scala", defaultOptions.and("-Ximplicit-search-limit", "1000")), compileFile("tests/neg-custom-args/jdk-9-app.scala", defaultOptions.and("-release:8")), + compileFile("tests/neg/i16453.scala", defaultOptions), ).checkExpectedErrors() } diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check new file mode 100644 index 000000000000..76fe7330e3b9 --- /dev/null +++ b/tests/neg/i16453.check @@ -0,0 +1,7 @@ +-- [E172] Type Error: tests/neg/i16453.scala:10:43 --------------------------------------------------------------------- +10 | val fails = summon[String => Option[Int]] // error + | ^ + | No given instance of type String => Option[Int] was found for parameter x of method summon in object Predef + | + | Note: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: + | - given instance given_Conversion_Function_Function diff --git a/tests/neg/i16453.scala b/tests/neg/i16453.scala new file mode 100644 index 000000000000..b60c174e4077 --- /dev/null +++ b/tests/neg/i16453.scala @@ -0,0 +1,11 @@ +import scala.language.implicitConversions + +given [T]: Conversion[String => T, String => Option[T]] = ??? +// This one is irrelevant, shouldn't be included in error message +given irrelevant[T]: Conversion[String => T, String => Byte] = ??? + +def test() = { + given foo: (String => Int) = _ => 42 + + val fails = summon[String => Option[Int]] // error +} From 393dab4f93c64766149abfa822c8a8947d3eb766 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 20 Jan 2023 19:23:12 -0500 Subject: [PATCH 018/657] Use symbol.showDcl, add sentence on explicitly passing arg --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 4 +++- tests/neg/i16453.check | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index cca5dae293e2..be5b4c16d170 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2746,7 +2746,9 @@ class MissingImplicitArgument( i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." def noChainConversionsNote(s: Iterable[TermRef]): Option[String] = Option.when(s.nonEmpty)( - i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored:${s.map(g => "\n - " + g.symbol.showLocated).mkString}" + i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. " + + i"The following conversions were ignored:${s.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + + i"\nIf there is an implicit `Conversion[A, ${pt.show}]` and an implicit `A` in scope, you need to explicitly pass the argument, e.g. `(using summon[A])`" ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index 76fe7330e3b9..871df3b6d7d2 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -1,7 +1,8 @@ -- [E172] Type Error: tests/neg/i16453.scala:10:43 --------------------------------------------------------------------- 10 | val fails = summon[String => Option[Int]] // error | ^ - | No given instance of type String => Option[Int] was found for parameter x of method summon in object Predef + |No given instance of type String => Option[Int] was found for parameter x of method summon in object Predef | - | Note: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: - | - given instance given_Conversion_Function_Function + |Note: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: + | - final given def given_Conversion_Function_Function[T]: Conversion[String => T, String => Option[T]] + |If there is an implicit `Conversion[A, String => Option[Int]]` and an implicit `A` in scope, you need to explicitly pass the argument, e.g. `(using summon[A])` From c73c47aabc7130098fc1712a7c7732f2d5392718 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 22 Jan 2023 18:21:23 +0000 Subject: [PATCH 019/657] Fix upper bound constraints, that are higher-kinded When recording an upper bound, when it's a some higher-kinded type variable applied to some type arguments, TypeComparer was re-applying the type arguments to the type parameter (the type variable's origin). Meaning that the instantiation of the type variable won't reflect in the upper bound. See the error message: `F$1[Int]` correctly appears as the lower bound, but `F$1[Int]` isn't the upper bound, `F[Int]` is, which is the original type parameter, in `Foo.Bar.apply[F](..)`. -- [E007] Type Mismatch Error: i12478.scala:8:13 ------------------------------- 8 | Foo.Bar(fu1) | ^^^^^^^^^^^^ |Found: Foo.Bar[F$1] |Required: Foo[T1] | |where: F$1 is a type in method id1 with bounds <: [_] =>> Any | T1 is a type in method id1 with bounds >: F$1[Int] and <: F[Int] | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: i12478.scala:18:13 ------------------------------ 18 | Foo.Bar(fu3) | ^^^^^^^^^^^^ |Found: Foo.Bar[F$2] |Required: Foo[T3] | |where: F$2 is a type in method id3 with bounds <: [_] =>> Any | T3 is a type in method id3 with bounds >: F$2[Int] and <: F[Int] | | longer explanation available when compiling with `-explain` --- .../dotty/tools/dotc/core/TypeComparer.scala | 7 +++++-- tests/pos/i12478.scala | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i12478.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index a0eb5139eb07..cf2507aa1724 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1338,8 +1338,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling } } || tryLiftedToThis2 - case _: TypeVar => - recur(tp1, tp2.superType) + case tv: TypeVar => + if tv.isInstantiated then + recur(tp1, tp2.superType) + else + compareAppliedType2(tp2, tv.origin, args2) case tycon2: AnnotatedType if !tycon2.isRefining => recur(tp1, tp2.superType) case tycon2: AppliedType => diff --git a/tests/pos/i12478.scala b/tests/pos/i12478.scala new file mode 100644 index 000000000000..d1e247ae4e68 --- /dev/null +++ b/tests/pos/i12478.scala @@ -0,0 +1,19 @@ +sealed trait Foo[T] + +object Foo: + case class Bar[F[_]](fu: List[F[Unit]]) extends Foo[F[Unit]] + +class Test: + def id1[T1](foo1: Foo[T1]): Foo[T1] = foo1 match + case Foo.Bar(fu) => + Foo.Bar(fu) + + def id2[T2](foo2: Foo[T2]): Foo[T2] = foo2 match + case bar2 @ (_: Foo.Bar[f]) => + val fu2 = bar2.fu + Foo.Bar(fu2) + + def id3[T3](foo3: Foo[T3]): Foo[T3] = foo3 match + case bar3 @ Foo.Bar(_) => + val fu3 = bar3.fu + Foo.Bar(fu3) From 3e9bb9f2361c52e8cc56254d9af6f6051dfcba15 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 23 Jan 2023 09:22:22 +0100 Subject: [PATCH 020/657] Improve `New`/`Select` -Ycheck message We mention that the constructor must be selected. This should be all the information needed in case someone writes a buggy macro that does not include the `Select` around the `New`. Closed #16357 --- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 4573c40df78b..2daac7cdd604 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -141,7 +141,7 @@ class TreeChecker extends Phase with SymTransformer { override def apply(parent: Tree, tree: Tree)(using Context): Tree = { tree match { case tree: New if !parent.isInstanceOf[tpd.Select] => - assert(assertion = false, i"`New` node must be wrapped in a `Select`:\n parent = ${parent.show}\n child = ${tree.show}") + assert(assertion = false, i"`New` node must be wrapped in a `Select` of the constructor:\n parent = ${parent.show}\n child = ${tree.show}") case _: Annotated => // Don't check inside annotations, since they're allowed to contain // somewhat invalid trees. From ad4abbafe5e05901df49518f3f99c8dba9a079b3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 23 Jan 2023 11:15:53 +0100 Subject: [PATCH 021/657] Improve error message for CyclicReference in macros Closes #16582 --- .../dotty/tools/dotc/quoted/Interpreter.scala | 9 +++++- tests/neg-macros/i16582.check | 15 ++++++++++ tests/neg-macros/i16582/Macro_1.scala | 29 +++++++++++++++++++ tests/neg-macros/i16582/Test_2.scala | 6 ++++ 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 tests/neg-macros/i16582.check create mode 100644 tests/neg-macros/i16582/Macro_1.scala create mode 100644 tests/neg-macros/i16582/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index 38cecb7953b8..8920b99b902c 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -30,6 +30,7 @@ import dotty.tools.dotc.typer.ImportInfo.withRootImports import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.reporting.Message import dotty.tools.repl.AbstractFileClassLoader +import dotty.tools.dotc.core.CyclicReference /** Tree interpreter for metaprogramming constructs */ class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context): @@ -253,8 +254,14 @@ class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context): } val shortStackTrace = targetException.getStackTrace.take(end + 1) targetException.setStackTrace(shortStackTrace) + targetException.printStackTrace(new PrintWriter(sw)) + + targetException match + case _: CyclicReference => sw.write("\nSee full stack trace using -Ydebug") + case _ => + } else { + targetException.printStackTrace(new PrintWriter(sw)) } - targetException.printStackTrace(new PrintWriter(sw)) sw.write("\n") throw new StopInterpretation(sw.toString.toMessage, pos) } diff --git a/tests/neg-macros/i16582.check b/tests/neg-macros/i16582.check new file mode 100644 index 000000000000..c06fe0d9829f --- /dev/null +++ b/tests/neg-macros/i16582.check @@ -0,0 +1,15 @@ + +-- Error: tests/neg-macros/i16582/Test_2.scala:5:27 -------------------------------------------------------------------- +5 | val o2 = ownerDoesNotWork(2) // error + | ^^^^^^^^^^^^^^^^^^^ + | Exception occurred while executing macro expansion. + | dotty.tools.dotc.core.CyclicReference: Recursive value o2 needs type + | + | See full stack trace using -Ydebug + |--------------------------------------------------------------------------------------------------------------------- + |Inline stack trace + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |This location contains code that was inlined from Macro_1.scala:7 +7 | ${ownerWorksImpl('in)} + | ^^^^^^^^^^^^^^^^^^^^^^ + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg-macros/i16582/Macro_1.scala b/tests/neg-macros/i16582/Macro_1.scala new file mode 100644 index 000000000000..dea58b89f017 --- /dev/null +++ b/tests/neg-macros/i16582/Macro_1.scala @@ -0,0 +1,29 @@ +import scala.quoted.* + +inline def ownerWorks(in: Int): Any = + ${ownerWorksImpl('in)} + +transparent inline def ownerDoesNotWork(in: Int): Any = + ${ownerWorksImpl('in)} + +def ownerWorksImpl(in: Expr[Int])(using Quotes): Expr[String] = + import quotes.reflect.* + val position = Position.ofMacroExpansion + val file = position.sourceFile + val owner0 = Symbol.spliceOwner.maybeOwner + println("owner0 = " + owner0) + val ownerName = owner0.tree match { + case ValDef(name, _, _) => + name + case DefDef(name, _, _, _) => + name + case t => report.errorAndAbort(s"unexpected tree shape: ${t.show}") + } + val path = file.path + val line = position.startLine + val column = position.startColumn + val v = in.valueOrAbort + val out = Expr(s"val $ownerName $v: $file @ ${position.startLine}") + out + + diff --git a/tests/neg-macros/i16582/Test_2.scala b/tests/neg-macros/i16582/Test_2.scala new file mode 100644 index 000000000000..7cfd65febd00 --- /dev/null +++ b/tests/neg-macros/i16582/Test_2.scala @@ -0,0 +1,6 @@ +def test= + val o1 = ownerWorks(1) + println(o1) + + val o2 = ownerDoesNotWork(2) // error + println(o2) From 94ff3c8a0d81de3ced9b60c4e50b63970f309094 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 23 Jan 2023 11:35:50 +0000 Subject: [PATCH 022/657] Fix & reuse printing of dummyTreeOfType The main fix is the unsplice, which was making the pattern not match for FunProto. But also, when the tree is applied to the dummy, print the dummy in the Apply node. And print null as a constant literal. Also, given AnyFunctionProto and AnyTypeConstructorProto shorter toStrings. --- .../dotty/tools/dotc/printing/RefinedPrinter.scala | 13 +++++++------ .../src/dotty/tools/dotc/typer/ProtoTypes.scala | 10 ++++++---- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 9a4b53d4112c..f94ff68d0698 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -3,6 +3,7 @@ package dotc package printing import core._ +import Constants.* import Texts._ import Types._ import Flags._ @@ -286,14 +287,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case tp: ViewProto => toText(tp.argType) ~ " ?=>? " ~ toText(tp.resultType) case tp @ FunProto(args, resultType) => - val argsText = args match { - case dummyTreeOfType(tp) :: Nil if !(tp isRef defn.NullClass) => "null: " ~ toText(tp) - case _ => toTextGlobal(args, ", ") - } "[applied to (" ~ keywordText("using ").provided(tp.isContextualMethod) ~ keywordText("erased ").provided(tp.isErasedMethod) - ~ argsText + ~ argsTreeText(args) ~ ") returning " ~ toText(resultType) ~ "]" @@ -309,6 +306,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { protected def exprToText(tp: ExprType): Text = "=> " ~ toText(tp.resType) + protected def argsTreeText(args: List[untpd.Tree]): Text = args match + case dummyTreeOfType(tp) :: Nil if !tp.isRef(defn.NullClass) && !homogenizedView => toText(Constant(null)) ~ ": " ~ toText(tp) + case _ => toTextGlobal(args, ", ") + protected def blockToText[T <: Untyped](block: Block[T]): Text = blockText(block.stats :+ block.expr) @@ -442,7 +443,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { toTextLocal(fun) ~ "(" ~ Str("using ").provided(app.applyKind == ApplyKind.Using && !homogenizedView) - ~ toTextGlobal(args, ", ") + ~ argsTreeText(args) ~ ")" case tree: TypeApply => typeApplyText(tree) diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 8ba842ad695f..15da3c2ccc22 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -671,10 +671,12 @@ object ProtoTypes { * * [] _ */ - @sharable object AnyFunctionProto extends UncachedGroundType with MatchAlways + @sharable object AnyFunctionProto extends UncachedGroundType with MatchAlways: + override def toString = "AnyFunctionProto" /** A prototype for type constructors that are followed by a type application */ - @sharable object AnyTypeConstructorProto extends UncachedGroundType with MatchAlways + @sharable object AnyTypeConstructorProto extends UncachedGroundType with MatchAlways: + override def toString = "AnyTypeConstructorProto" extension (pt: Type) def isExtensionApplyProto: Boolean = pt match @@ -946,8 +948,8 @@ object ProtoTypes { object dummyTreeOfType { def apply(tp: Type)(implicit src: SourceFile): Tree = untpd.Literal(Constant(null)) withTypeUnchecked tp - def unapply(tree: untpd.Tree): Option[Type] = tree match { - case Literal(Constant(null)) => Some(tree.typeOpt) + def unapply(tree: untpd.Tree): Option[Type] = untpd.unsplice(tree) match { + case tree @ Literal(Constant(null)) => Some(tree.typeOpt) case _ => None } } From ee44db864654d85bedbb380aa9ee62d74483a59e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 23 Jan 2023 11:47:29 +0000 Subject: [PATCH 023/657] Fix early returns nested in trace Found by enabling the tracing config. --- .../src/dotty/tools/dotc/transform/patmat/Space.scala | 5 +++-- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 11 +++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 90310a385d0c..f4c4863d073d 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -489,9 +489,10 @@ class SpaceEngine(using Context) extends SpaceLogic { private def erase(tp: Type, inArray: Boolean = false, isValue: Boolean = false): Type = trace(i"$tp erased to", debug) { tp match { - case tp @ AppliedType(tycon, args) => - if tycon.typeSymbol.isPatternBound then return WildcardType + case tp @ AppliedType(tycon, args) if tycon.typeSymbol.isPatternBound => + WildcardType + case tp @ AppliedType(tycon, args) => val args2 = if (tycon.isRef(defn.ArrayClass)) args.map(arg => erase(arg, inArray = true, isValue = false)) else args.map(arg => erase(arg, inArray = false, isValue = false)) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 3e0e7dd5879d..c04f9b990bc7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1002,11 +1002,10 @@ trait Implicits: if (argument.isEmpty) i"missing implicit parameter of type $pt after typer at phase ${ctx.phase.phaseName}" else i"type error: ${argument.tpe} does not conform to $pt${err.whyNoMatchStr(argument.tpe, pt)}") - if pt.unusableForInference - || !argument.isEmpty && argument.tpe.unusableForInference - then return NoMatchingImplicitsFailure + val usableForInference = !pt.unusableForInference + && (argument.isEmpty || !argument.tpe.unusableForInference) - val result0 = + val result0 = if usableForInference then // If we are searching implicits when resolving an import symbol, start the search // in the first enclosing context that does not have the same scope and owner as the current // context. Without that precaution, an eligible implicit in the current scope @@ -1023,7 +1022,7 @@ trait Implicits: catch case ce: CyclicReference => ce.inImplicitSearch = true throw ce - end result0 + else NoMatchingImplicitsFailure val result = result0 match { @@ -1052,7 +1051,7 @@ trait Implicits: result } else result - case NoMatchingImplicitsFailure => + case NoMatchingImplicitsFailure if usableForInference => SearchFailure(new NoMatchingImplicits(pt, argument, ctx.typerState.constraint), span) case _ => result0 From 4e4bf207fd521f16d743d4bbe64d11f7cf076c9e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 23 Jan 2023 17:18:16 +0100 Subject: [PATCH 024/657] Update compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala Co-authored-by: Guillaume Martres --- compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index 4669dc86982d..e2e494a95074 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -179,7 +179,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( * - If a type reference is used a higher level, then it is inconsistent. * Will attempt to heal before failing. * - If a term reference is used a higher level, then it is inconsistent. - * It can not be healed because the term will not exists in the any future stage. + * It cannot be healed because the term will not exist in any future stage. * * If `T` is a reference to a type at the wrong level, try to heal it by replacing it with * a type tag of type `quoted.Type[T]`. From 29dc069a5cbf1b7ffadba03f1766e5205f795735 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 23 Jan 2023 16:31:28 +0000 Subject: [PATCH 025/657] Redefine hasCaptureConversionArg --- .../src/dotty/tools/dotc/core/Types.scala | 2 -- .../dotty/tools/dotc/typer/Inferencing.scala | 4 +++ .../dotty/tools/dotc/typer/ProtoTypes.scala | 28 +++++++++++-------- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 403fe7021152..0c5614e29a60 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4470,8 +4470,6 @@ object Types { def hasWildcardArg(using Context): Boolean = args.exists(isBounds) - def hasCaptureConversionArg(using Context): Boolean = args.exists(_.typeSymbol == defn.TypeBox_CAP) - def derivedAppliedType(tycon: Type, args: List[Type])(using Context): Type = if ((tycon eq this.tycon) && (args eq this.args)) this else tycon.appliedTo(args) diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 656a1266d5ab..434e1c01849d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -546,6 +546,10 @@ object Inferencing { case tp: AnnotatedType => tp.derivedAnnotatedType(captureWildcards(tp.parent), tp.annot) case _ => tp } + + def hasCaptureConversionArg(tp: Type)(using Context): Boolean = tp match + case tp: AppliedType => tp.args.exists(_.typeSymbol == defn.TypeBox_CAP) + case _ => false } trait Inferencing { this: Typer => diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 170d12b998de..31e862ecd569 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -13,6 +13,7 @@ import Decorators._ import Uniques._ import inlines.Inlines import config.Printers.typr +import Inferencing.* import ErrorReporting.* import util.SourceFile import TypeComparer.necessarySubType @@ -495,18 +496,21 @@ object ProtoTypes { force = true) val targ1 = typer.adapt(targ, wideFormal, locked) if wideFormal eq formal then targ1 - else targ1.tpe match - case tp: AppliedType if tp.hasCaptureConversionArg => - stripCast(targ1).tpe match - case tp: AppliedType if tp.hasWildcardArg => - errorTree(targ1, - em"""argument for by-name parameter is not a value - |and contains wildcard arguments: $tp - | - |Assign it to a val and pass that instead. - |""") - case _ => targ1 - case _ => targ1 + else checkNoWildcardCaptureForCBN(targ1) + } + + def checkNoWildcardCaptureForCBN(targ1: Tree)(using Context): Tree = { + if hasCaptureConversionArg(targ1.tpe) then + stripCast(targ1).tpe match + case tp: AppliedType if tp.hasWildcardArg => + errorTree(targ1, + em"""argument for by-name parameter is not a value + |and contains wildcard arguments: $tp + | + |Assign it to a val and pass that instead. + |""") + case _ => targ1 + else targ1 } /** The type of the argument `arg`, or `NoType` if `arg` has not been typed before diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 54eb8bc2ab1e..40e488dc7336 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1568,7 +1568,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else if ((tree.tpt `eq` untpd.ContextualEmptyTree) && mt.paramNames.isEmpty) // Note implicitness of function in target type since there are no method parameters that indicate it. TypeTree(defn.FunctionOf(Nil, mt.resType, isContextual = true, isErased = false)) - else if mt.resType.match { case tp: AppliedType => tp.hasCaptureConversionArg case _ => false } then + else if hasCaptureConversionArg(mt.resType) then errorTree(tree, em"""cannot turn method type $mt into closure |because it has capture conversion skolem types""") From d91f66ff783682e473909299f3775db07b784101 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 23 Jan 2023 14:51:54 +0100 Subject: [PATCH 026/657] Don't generate a Select for a TermRef with NoPrefix In TreeTypeMap, don't generate a Select node if the new type of the tree is a TermRef with NoPrefix. Generate an Ident node instead. Fixes #16740 --- .../dotty/tools/dotc/ast/TreeTypeMap.scala | 40 ++++++++++++------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 ++ .../dotty/tools/dotc/transform/Erasure.scala | 2 +- tests/pos/i16740.scala | 8 ++++ 4 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 tests/pos/i16740.scala diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index f5bf55802adf..faeafae97f5e 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -43,7 +43,7 @@ class TreeTypeMap( def copy( typeMap: Type => Type, - treeMap: tpd.Tree => tpd.Tree, + treeMap: Tree => Tree, oldOwners: List[Symbol], newOwners: List[Symbol], substFrom: List[Symbol], @@ -85,13 +85,13 @@ class TreeTypeMap( updateDecls(prevStats.tail, newStats.tail) } - def transformInlined(tree: tpd.Inlined)(using Context): tpd.Tree = + def transformInlined(tree: Inlined)(using Context): Tree = val Inlined(call, bindings, expanded) = tree val (tmap1, bindings1) = transformDefs(bindings) val expanded1 = tmap1.transform(expanded) cpy.Inlined(tree)(call, bindings1, expanded1) - override def transform(tree: tpd.Tree)(using Context): tpd.Tree = treeMap(tree) match { + override def transform(tree: Tree)(using Context): Tree = treeMap(tree) match { case impl @ Template(constr, _, self, _) => val tmap = withMappedSyms(localSyms(impl :: self :: Nil)) cpy.Template(impl)( @@ -103,8 +103,24 @@ class TreeTypeMap( ).withType(tmap.mapType(impl.tpe)) case tree1 => tree1.withType(mapType(tree1.tpe)) match { - case id: Ident if tpd.needsSelect(id.tpe) => - ref(id.tpe.asInstanceOf[TermRef]).withSpan(id.span) + case id: Ident => + if needsSelect(id.tpe) then + ref(id.tpe.asInstanceOf[TermRef]).withSpan(id.span) + else + super.transform(id) + case sel: Select => + if needsIdent(sel.tpe) then + ref(sel.tpe.asInstanceOf[TermRef]).withSpan(sel.span) + else + super.transform(sel) + case app: Apply => + super.transform(app) + case blk @ Block(stats, expr) => + val (tmap1, stats1) = transformDefs(stats) + val expr1 = tmap1.transform(expr) + cpy.Block(blk)(stats1, expr1) + case lit @ Literal(Constant(tpe: Type)) => + cpy.Literal(lit)(Constant(mapType(tpe))) case ddef @ DefDef(name, paramss, tpt, _) => val (tmap1, paramss1) = transformAllParamss(paramss) val res = cpy.DefDef(ddef)(name, paramss1, tmap1.transform(tpt), tmap1.transform(ddef.rhs)) @@ -117,10 +133,6 @@ class TreeTypeMap( case tdef @ LambdaTypeTree(tparams, body) => val (tmap1, tparams1) = transformDefs(tparams) cpy.LambdaTypeTree(tdef)(tparams1, tmap1.transform(body)) - case blk @ Block(stats, expr) => - val (tmap1, stats1) = transformDefs(stats) - val expr1 = tmap1.transform(expr) - cpy.Block(blk)(stats1, expr1) case inlined: Inlined => transformInlined(inlined) case cdef @ CaseDef(pat, guard, rhs) => @@ -139,18 +151,16 @@ class TreeTypeMap( val content1 = transform(content) val tpt1 = transform(tpt) cpy.Hole(tree)(args = args1, content = content1, tpt = tpt1) - case lit @ Literal(Constant(tpe: Type)) => - cpy.Literal(lit)(Constant(mapType(tpe))) case tree1 => super.transform(tree1) } } - override def transformStats(trees: List[tpd.Tree], exprOwner: Symbol)(using Context): List[Tree] = + override def transformStats(trees: List[Tree], exprOwner: Symbol)(using Context): List[Tree] = transformDefs(trees)._2 - def transformDefs[TT <: tpd.Tree](trees: List[TT])(using Context): (TreeTypeMap, List[TT]) = { - val tmap = withMappedSyms(tpd.localSyms(trees)) + def transformDefs[TT <: Tree](trees: List[TT])(using Context): (TreeTypeMap, List[TT]) = { + val tmap = withMappedSyms(localSyms(trees)) (tmap, tmap.transformSub(trees)) } @@ -165,7 +175,7 @@ class TreeTypeMap( case nil => (this, paramss) - def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree] + def apply[ThisTree <: Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree] def apply(annot: Annotation): Annotation = annot.derivedAnnotation(apply(annot.tree)) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index dd1e46c62223..1a202ece1e66 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -414,6 +414,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case _ => false } + def needsIdent(tp: Type)(using Context): Boolean = tp match + case tp: TermRef => tp.prefix eq NoPrefix + case _ => false + /** A tree representing the same reference as the given type */ def ref(tp: NamedType, needLoad: Boolean = true)(using Context): Tree = if (tp.isType) TypeTree(tp) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 129964557995..60affae38ad5 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -774,7 +774,7 @@ object Erasure { select(qual1, sym) else val castTarget = // Avoid inaccessible cast targets, see i8661 - if isJvmAccessible(sym.owner) + if isJvmAccessible(sym.owner) && sym.owner.isType then sym.owner.typeRef else diff --git a/tests/pos/i16740.scala b/tests/pos/i16740.scala new file mode 100644 index 000000000000..398518ee77ef --- /dev/null +++ b/tests/pos/i16740.scala @@ -0,0 +1,8 @@ +class Enclosing: + object Tags: + opaque type Ref[T, S <: String & Singleton] = S + inline def require[T, S <: String & Singleton]: Ref[T, S] = ??? + import Tags.* + + val t1 = require[Int, "t1"] + val t2 = require[Double, "t2"] From 1d7f8dc06706d2d53a1f16088c1fda79d558ee53 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 24 Jan 2023 04:15:20 -0500 Subject: [PATCH 027/657] Remove test from negAll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michał Pałka --- compiler/test/dotty/tools/dotc/CompilationTests.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 0346662b5434..b8b38cce92e4 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -188,7 +188,6 @@ class CompilationTests { compileFile("tests/neg-custom-args/i13026.scala", defaultOptions.and("-print-lines")), compileFile("tests/neg-custom-args/i13838.scala", defaultOptions.and("-Ximplicit-search-limit", "1000")), compileFile("tests/neg-custom-args/jdk-9-app.scala", defaultOptions.and("-release:8")), - compileFile("tests/neg/i16453.scala", defaultOptions), ).checkExpectedErrors() } From 8d1e5dfa35f2e61c369e5105625f73913219cfab Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 30 Dec 2022 17:58:05 +0000 Subject: [PATCH 028/657] Split out immutable GadtConstraint --- .../dotty/tools/dotc/core/Constraint.scala | 3 + .../tools/dotc/core/ConstraintHandling.scala | 11 +- .../src/dotty/tools/dotc/core/Contexts.scala | 10 +- .../tools/dotc/core/GadtConstraint.scala | 351 +++++++++--------- .../tools/dotc/core/OrderingConstraint.scala | 11 + .../dotc/core/PatternTypeConstrainer.scala | 2 +- .../dotty/tools/dotc/core/TypeComparer.scala | 26 +- .../tools/dotc/transform/PostTyper.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 8 +- .../src/dotty/tools/dotc/typer/Typer.scala | 4 +- 10 files changed, 213 insertions(+), 215 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Constraint.scala b/compiler/src/dotty/tools/dotc/core/Constraint.scala index b849c7aa7093..c634f847e510 100644 --- a/compiler/src/dotty/tools/dotc/core/Constraint.scala +++ b/compiler/src/dotty/tools/dotc/core/Constraint.scala @@ -71,6 +71,9 @@ abstract class Constraint extends Showable { */ def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds + /** The current bounds of type parameter `param` */ + def bounds(param: TypeParamRef)(using Context): TypeBounds + /** A new constraint which is derived from this constraint by adding * entries for all type parameters of `poly`. * @param tvars A list of type variables associated with the params, diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 6207e0a3d728..9ffe2bda73cb 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -749,16 +749,7 @@ trait ConstraintHandling { } /** The current bounds of type parameter `param` */ - def bounds(param: TypeParamRef)(using Context): TypeBounds = { - val e = constraint.entry(param) - if (e.exists) e.bounds - else { - // TODO: should we change the type of paramInfos to nullable? - val pinfos: List[param.binder.PInfo] | Null = param.binder.paramInfos - if (pinfos != null) pinfos(param.paramNum) // pinfos == null happens in pos/i536.scala - else TypeBounds.empty - } - } + def bounds(param: TypeParamRef)(using Context): TypeBounds = constraint.bounds(param) /** Add type lambda `tl`, possibly with type variables `tvars`, to current constraint * and propagate all bounds. diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 5f82e8c8b6ce..398af92e494d 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -141,7 +141,7 @@ object Contexts { def tree: Tree[?] def scope: Scope def typerState: TyperState - def gadt: GadtConstraint + def gadt: GadtConstraintHandling def searchHistory: SearchHistory def source: SourceFile @@ -541,8 +541,8 @@ object Contexts { private var _typerState: TyperState = uninitialized final def typerState: TyperState = _typerState - private var _gadt: GadtConstraint = uninitialized - final def gadt: GadtConstraint = _gadt + private var _gadt: GadtConstraintHandling = uninitialized + final def gadt: GadtConstraintHandling = _gadt private var _searchHistory: SearchHistory = uninitialized final def searchHistory: SearchHistory = _searchHistory @@ -624,7 +624,7 @@ object Contexts { this._scope = typer.scope setTypeAssigner(typer) - def setGadt(gadt: GadtConstraint): this.type = + def setGadt(gadt: GadtConstraintHandling): this.type = util.Stats.record("Context.setGadt") this._gadt = gadt this @@ -721,7 +721,7 @@ object Contexts { .updated(notNullInfosLoc, Nil) .updated(compilationUnitLoc, NoCompilationUnit) c._searchHistory = new SearchRoot - c._gadt = GadtConstraint.empty + c._gadt = GadtConstraintHandling(GadtConstraint.empty) c end FreshContext diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index 7515898a36df..d683c15a9241 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -2,34 +2,145 @@ package dotty.tools package dotc package core -import Decorators._ -import Contexts._ -import Types._ -import Symbols._ +import Contexts.*, Decorators.*, Symbols.*, Types.* +import config.Printers.{gadts, gadtsConstr} import util.{SimpleIdentitySet, SimpleIdentityMap} -import collection.mutable import printing._ +import scala.annotation.tailrec +import scala.annotation.internal.sharable +import scala.collection.mutable + object GadtConstraint: - def apply(): GadtConstraint = empty - def empty: GadtConstraint = - new ProperGadtConstraint(OrderingConstraint.empty, SimpleIdentityMap.empty, SimpleIdentityMap.empty, false) + @sharable val empty: GadtConstraint = + GadtConstraint(OrderingConstraint.empty, SimpleIdentityMap.empty, SimpleIdentityMap.empty, false) /** Represents GADT constraints currently in scope */ -sealed trait GadtConstraint ( - private var myConstraint: Constraint, - private var mapping: SimpleIdentityMap[Symbol, TypeVar], - private var reverseMapping: SimpleIdentityMap[TypeParamRef, Symbol], - private var wasConstrained: Boolean -) extends Showable { - this: ConstraintHandling => +class GadtConstraint private ( + private val myConstraint: Constraint, + private val mapping: SimpleIdentityMap[Symbol, TypeVar], + private val reverseMapping: SimpleIdentityMap[TypeParamRef, Symbol], + private val wasConstrained: Boolean, +) extends Showable: + def constraint: Constraint = myConstraint + def symbols: List[Symbol] = mapping.keys + def withConstraint(c: Constraint) = copy(myConstraint = c) + def withWasConstrained = copy(wasConstrained = true) + + def add(sym: Symbol, tv: TypeVar): GadtConstraint = copy( + mapping = mapping.updated(sym, tv), + reverseMapping = reverseMapping.updated(tv.origin, sym), + ) + + /** Is `sym1` ordered to be less than `sym2`? */ + def isLess(sym1: Symbol, sym2: Symbol)(using Context): Boolean = + constraint.isLess(tvarOrError(sym1).origin, tvarOrError(sym2).origin) + + /** Full bounds of `sym`, including TypeRefs to other lower/upper symbols. + * + * @note this performs subtype checks between ordered symbols. + * Using this in isSubType can lead to infinite recursion. Consider `bounds` instead. + */ + def fullBounds(sym: Symbol)(using Context): TypeBounds | Null = mapping(sym) match + case null => null + case tv: TypeVar => fullBounds(tv.origin) // .ensuring(containsNoInternalTypes(_)) + + /** Immediate bounds of `sym`. Does not contain lower/upper symbols (see [[fullBounds]]). */ + def bounds(sym: Symbol)(using Context): TypeBounds | Null = + mapping(sym) match + case null => null + case tv: TypeVar => + def retrieveBounds: TypeBounds = externalize(constraint.bounds(tv.origin)).bounds + retrieveBounds + //.showing(i"gadt bounds $sym: $result", gadts) + //.ensuring(containsNoInternalTypes(_)) + + /** Is the symbol registered in the constraint? + * + * @note this is true even if the symbol is constrained to be equal to another type, unlike [[Constraint.contains]]. + */ + def contains(sym: Symbol)(using Context): Boolean = mapping(sym) != null + + /** GADT constraint narrows bounds of at least one variable */ + def isNarrowing: Boolean = wasConstrained + + def fullBounds(param: TypeParamRef)(using Context): TypeBounds = + nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param)) + + def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds = + externalize(constraint.nonParamBounds(param)).bounds + + def fullLowerBound(param: TypeParamRef)(using Context): Type = + constraint.minLower(param).foldLeft(nonParamBounds(param).lo) { + (t, u) => t | externalize(u) + } + + def fullUpperBound(param: TypeParamRef)(using Context): Type = + constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (t, u) => + val eu = externalize(u) + // Any as the upper bound means "no bound", but if F is higher-kinded, + // Any & F = F[_]; this is wrong for us so we need to short-circuit + if t.isAny then eu else t & eu + } + + def externalize(tp: Type, theMap: TypeMap | Null = null)(using Context): Type = tp match + case param: TypeParamRef => reverseMapping(param) match + case sym: Symbol => sym.typeRef + case null => param + case tp: TypeAlias => tp.derivedAlias(externalize(tp.alias, theMap)) + case tp => (if theMap == null then ExternalizeMap() else theMap).mapOver(tp) + + private class ExternalizeMap(using Context) extends TypeMap: + def apply(tp: Type): Type = externalize(tp, this)(using mapCtx) + + def tvarOrError(sym: Symbol)(using Context): TypeVar = + mapping(sym).ensuring(_ != null, i"not a constrainable symbol: $sym").uncheckedNN + + @tailrec final def stripInternalTypeVar(tp: Type): Type = tp match + case tv: TypeVar => + val inst = constraint.instType(tv) + if inst.exists then stripInternalTypeVar(inst) else tv + case _ => tp + + def internalize(tp: Type)(using Context): Type = tp match + case nt: NamedType => + val ntTvar = mapping(nt.symbol) + if ntTvar == null then tp + else ntTvar + case _ => tp + + private def containsNoInternalTypes(tp: Type, theAcc: TypeAccumulator[Boolean] | Null = null)(using Context): Boolean = tp match { + case tpr: TypeParamRef => !reverseMapping.contains(tpr) + case tv: TypeVar => !reverseMapping.contains(tv.origin) + case tp => + (if (theAcc != null) theAcc else new ContainsNoInternalTypesAccumulator()).foldOver(true, tp) + } - import dotty.tools.dotc.config.Printers.{gadts, gadtsConstr} + private class ContainsNoInternalTypesAccumulator(using Context) extends TypeAccumulator[Boolean] { + override def apply(x: Boolean, tp: Type): Boolean = x && containsNoInternalTypes(tp, this) + } - private[core] def getConstraint: Constraint = constraint - private[core] def getMapping: SimpleIdentityMap[Symbol, TypeVar] = mapping - private[core] def getReverseMapping: SimpleIdentityMap[TypeParamRef, Symbol] = reverseMapping - private[core] def getWasConstrained: Boolean = wasConstrained + override def toText(printer: Printer): Texts.Text = printer.toText(this) + + /** Provides more information than toText, by showing the underlying Constraint details. */ + def debugBoundsDescription(using Context): String = i"$this\n$constraint" + + private def copy( + myConstraint: Constraint = myConstraint, + mapping: SimpleIdentityMap[Symbol, TypeVar] = mapping, + reverseMapping: SimpleIdentityMap[TypeParamRef, Symbol] = reverseMapping, + wasConstrained: Boolean = wasConstrained, + ): GadtConstraint = GadtConstraint(myConstraint, mapping, reverseMapping, wasConstrained) +end GadtConstraint + +object GadtConstraintHandling: + def apply(gadt: GadtConstraint): GadtConstraintHandling = new ProperGadtConstraintHandling(gadt) + +sealed trait GadtConstraintHandling(private var myGadt: GadtConstraint) { + this: ConstraintHandling => + + def gadt: GadtConstraint = myGadt + private def gadt_=(g: GadtConstraint) = myGadt = g /** Exposes ConstraintHandling.subsumes */ def subsumes(left: GadtConstraint, right: GadtConstraint, pre: GadtConstraint)(using Context): Boolean = { @@ -57,22 +168,19 @@ sealed trait GadtConstraint ( // and used as orderings. def substDependentSyms(tp: Type, isUpper: Boolean)(using Context): Type = { def loop(tp: Type) = substDependentSyms(tp, isUpper) - tp match { + tp match case tp @ AndType(tp1, tp2) if !isUpper => tp.derivedAndType(loop(tp1), loop(tp2)) case tp @ OrType(tp1, tp2) if isUpper => tp.derivedOrType(loop(tp1), loop(tp2)) case tp: NamedType => - params.indexOf(tp.symbol) match { + params.indexOf(tp.symbol) match case -1 => - mapping(tp.symbol) match { + gadt.internalize(tp) match case tv: TypeVar => tv.origin - case null => tp - } + case _ => tp case i => pt.paramRefs(i) - } case tp => tp - } } val tb = param.info.bounds @@ -86,205 +194,92 @@ sealed trait GadtConstraint ( val tvars = params.lazyZip(poly1.paramRefs).map { (sym, paramRef) => val tv = TypeVar(paramRef, creatorState = null) - mapping = mapping.updated(sym, tv) - reverseMapping = reverseMapping.updated(tv.origin, sym) + gadt = gadt.add(sym, tv) tv } // The replaced symbols are picked up here. addToConstraint(poly1, tvars) - .showing(i"added to constraint: [$poly1] $params%, % gadt = $this", gadts) + .showing(i"added to constraint: [$poly1] $params%, % gadt = $gadt", gadts) } /** Further constrain a symbol already present in the constraint. */ def addBound(sym: Symbol, bound: Type, isUpper: Boolean)(using Context): Boolean = { - @annotation.tailrec def stripInternalTypeVar(tp: Type): Type = tp match { - case tv: TypeVar => - val inst = constraint.instType(tv) - if (inst.exists) stripInternalTypeVar(inst) else tv - case _ => tp - } - - val symTvar: TypeVar = stripInternalTypeVar(tvarOrError(sym)) match { + val symTvar: TypeVar = gadt.stripInternalTypeVar(gadt.tvarOrError(sym)) match case tv: TypeVar => tv case inst => gadts.println(i"instantiated: $sym -> $inst") - return if (isUpper) isSub(inst, bound) else isSub(bound, inst) - } + return if isUpper then isSub(inst, bound) else isSub(bound, inst) - val internalizedBound = bound match { - case nt: NamedType => - val ntTvar = mapping(nt.symbol) - if (ntTvar != null) stripInternalTypeVar(ntTvar) else bound - case _ => bound - } + val internalizedBound = gadt.stripInternalTypeVar(gadt.internalize(bound)) val saved = constraint val result = internalizedBound match case boundTvar: TypeVar => - if (boundTvar eq symTvar) true - else if (isUpper) addLess(symTvar.origin, boundTvar.origin) + if boundTvar eq symTvar then true + else if isUpper + then addLess(symTvar.origin, boundTvar.origin) else addLess(boundTvar.origin, symTvar.origin) case bound => addBoundTransitively(symTvar.origin, bound, isUpper) gadts.println { - val descr = if (isUpper) "upper" else "lower" - val op = if (isUpper) "<:" else ">:" + val descr = if isUpper then "upper" else "lower" + val op = if isUpper then "<:" else ">:" i"adding $descr bound $sym $op $bound = $result" } - if constraint ne saved then wasConstrained = true + if constraint ne saved then gadt = gadt.withWasConstrained result } - /** Is `sym1` ordered to be less than `sym2`? */ - def isLess(sym1: Symbol, sym2: Symbol)(using Context): Boolean = - constraint.isLess(tvarOrError(sym1).origin, tvarOrError(sym2).origin) - - /** Full bounds of `sym`, including TypeRefs to other lower/upper symbols. - * - * @note this performs subtype checks between ordered symbols. - * Using this in isSubType can lead to infinite recursion. Consider `bounds` instead. - */ - def fullBounds(sym: Symbol)(using Context): TypeBounds | Null = - mapping(sym) match { - case null => null - // TODO: Improve flow typing so that ascription becomes redundant - case tv: TypeVar => - fullBounds(tv.origin) - // .ensuring(containsNoInternalTypes(_)) - } - - /** Immediate bounds of `sym`. Does not contain lower/upper symbols (see [[fullBounds]]). */ - def bounds(sym: Symbol)(using Context): TypeBounds | Null = - mapping(sym) match { - case null => null - // TODO: Improve flow typing so that ascription becomes redundant - case tv: TypeVar => - def retrieveBounds: TypeBounds = externalize(bounds(tv.origin)).bounds - retrieveBounds - //.showing(i"gadt bounds $sym: $result", gadts) - //.ensuring(containsNoInternalTypes(_)) - } - - /** Is the symbol registered in the constraint? - * - * @note this is true even if the symbol is constrained to be equal to another type, unlike [[Constraint.contains]]. - */ - def contains(sym: Symbol)(using Context): Boolean = mapping(sym) != null - - /** GADT constraint narrows bounds of at least one variable */ - def isNarrowing: Boolean = wasConstrained + def isLess(sym1: Symbol, sym2: Symbol)(using Context): Boolean = gadt.isLess(sym1, sym2) + def fullBounds(sym: Symbol)(using Context): TypeBounds | Null = gadt.fullBounds(sym) + def bounds(sym: Symbol)(using Context): TypeBounds | Null = gadt.bounds(sym) + def contains(sym: Symbol)(using Context): Boolean = gadt.contains(sym) + def isNarrowing: Boolean = gadt.isNarrowing + def symbols: List[Symbol] = gadt.symbols /** See [[ConstraintHandling.approximation]] */ def approximation(sym: Symbol, fromBelow: Boolean, maxLevel: Int = Int.MaxValue)(using Context): Type = { - val res = - approximation(tvarOrError(sym).origin, fromBelow, maxLevel) match - case tpr: TypeParamRef => - // Here we do externalization when the returned type is a TypeParamRef, - // b/c ConstraintHandling.approximation may return internal types when - // the type variable is instantiated. See #15531. - externalize(tpr) - case tp => tp - - gadts.println(i"approximating $sym ~> $res") - res + approximation(gadt.tvarOrError(sym).origin, fromBelow, maxLevel).match + case tpr: TypeParamRef => + // Here we do externalization when the returned type is a TypeParamRef, + // b/c ConstraintHandling.approximation may return internal types when + // the type variable is instantiated. See #15531. + gadt.externalize(tpr) + case tp => tp + .showing(i"approximating $sym ~> $result", gadts) } - def symbols: List[Symbol] = mapping.keys + def fresh: GadtConstraintHandling = GadtConstraintHandling(gadt) - def fresh: GadtConstraint = new ProperGadtConstraint(myConstraint, mapping, reverseMapping, wasConstrained) - - /** Restore the state from other [[GadtConstraint]], probably copied using [[fresh]] */ - def restore(other: GadtConstraint): Unit = - this.myConstraint = other.myConstraint - this.mapping = other.mapping - this.reverseMapping = other.reverseMapping - this.wasConstrained = other.wasConstrained - - def restore(constr: Constraint, mapping: SimpleIdentityMap[Symbol, TypeVar], revMapping: SimpleIdentityMap[TypeParamRef, Symbol], wasConstrained: Boolean): Unit = - this.myConstraint = constr - this.mapping = mapping - this.reverseMapping = revMapping - this.wasConstrained = wasConstrained + /** Restore the GadtConstraint state. */ + def restore(gadt: GadtConstraint): Unit = this.gadt = gadt inline def rollbackGadtUnless(inline op: Boolean): Boolean = - val savedConstr = myConstraint - val savedMapping = mapping - val savedReverseMapping = reverseMapping - val savedWasConstrained = wasConstrained + val saved = gadt var result = false - try - result = op - finally - if !result then - restore(savedConstr, savedMapping, savedReverseMapping, savedWasConstrained) + try result = op + finally if !result then restore(saved) result // ---- Protected/internal ----------------------------------------------- - override protected def constraint = myConstraint - override protected def constraint_=(c: Constraint) = myConstraint = c + override protected def constraint = gadt.constraint + override protected def constraint_=(c: Constraint) = gadt = gadt.withConstraint(c) override protected def isSub(tp1: Type, tp2: Type)(using Context): Boolean = TypeComparer.isSubType(tp1, tp2) override protected def isSame(tp1: Type, tp2: Type)(using Context): Boolean = TypeComparer.isSameType(tp1, tp2) - override def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds = - externalize(constraint.nonParamBounds(param)).bounds - - override def fullLowerBound(param: TypeParamRef)(using Context): Type = - constraint.minLower(param).foldLeft(nonParamBounds(param).lo) { - (t, u) => t | externalize(u) - } - - override def fullUpperBound(param: TypeParamRef)(using Context): Type = - constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (t, u) => - val eu = externalize(u) - // Any as the upper bound means "no bound", but if F is higher-kinded, - // Any & F = F[_]; this is wrong for us so we need to short-circuit - if t.isAny then eu else t & eu - } - - // ---- Private ---------------------------------------------------------- - - private def externalize(tp: Type, theMap: TypeMap | Null = null)(using Context): Type = tp match - case param: TypeParamRef => reverseMapping(param) match - case sym: Symbol => sym.typeRef - case null => param - case tp: TypeAlias => tp.derivedAlias(externalize(tp.alias, theMap)) - case tp => (if theMap == null then ExternalizeMap() else theMap).mapOver(tp) - - private class ExternalizeMap(using Context) extends TypeMap: - def apply(tp: Type): Type = externalize(tp, this)(using mapCtx) - - private def tvarOrError(sym: Symbol)(using Context): TypeVar = - mapping(sym).ensuring(_ != null, i"not a constrainable symbol: $sym").uncheckedNN - - private def containsNoInternalTypes(tp: Type, theAcc: TypeAccumulator[Boolean] | Null = null)(using Context): Boolean = tp match { - case tpr: TypeParamRef => !reverseMapping.contains(tpr) - case tv: TypeVar => !reverseMapping.contains(tv.origin) - case tp => - (if (theAcc != null) theAcc else new ContainsNoInternalTypesAccumulator()).foldOver(true, tp) - } - - private class ContainsNoInternalTypesAccumulator(using Context) extends TypeAccumulator[Boolean] { - override def apply(x: Boolean, tp: Type): Boolean = x && containsNoInternalTypes(tp, this) - } + override def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds = gadt.nonParamBounds(param) + override def fullLowerBound(param: TypeParamRef)(using Context): Type = gadt.fullLowerBound(param) + override def fullUpperBound(param: TypeParamRef)(using Context): Type = gadt.fullUpperBound(param) // ---- Debug ------------------------------------------------------------ override def constr = gadtsConstr - - override def toText(printer: Printer): Texts.Text = printer.toText(this) - - /** Provides more information than toText, by showing the underlying Constraint details. */ - def debugBoundsDescription(using Context): String = i"$this\n$constraint" } -private class ProperGadtConstraint ( - myConstraint: Constraint, - mapping: SimpleIdentityMap[Symbol, TypeVar], - reverseMapping: SimpleIdentityMap[TypeParamRef, Symbol], - wasConstrained: Boolean, -) extends ConstraintHandling with GadtConstraint(myConstraint, mapping, reverseMapping, wasConstrained) +// Hide ConstraintHandling within GadtConstraintHandling +private class ProperGadtConstraintHandling(gadt: GadtConstraint) extends ConstraintHandling with GadtConstraintHandling(gadt) diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index 212b70336f4b..faea30390d2b 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -224,6 +224,17 @@ class OrderingConstraint(private val boundsMap: ParamBounds, def exclusiveUpper(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] = upper(param).filterNot(isLess(butNot, _)) + def bounds(param: TypeParamRef)(using Context): TypeBounds = { + val e = entry(param) + if (e.exists) e.bounds + else { + // TODO: should we change the type of paramInfos to nullable? + val pinfos: List[param.binder.PInfo] | Null = param.binder.paramInfos + if (pinfos != null) pinfos(param.paramNum) // pinfos == null happens in pos/i536.scala + else TypeBounds.empty + } + } + // ---------- Info related to TypeParamRefs ------------------------------------------- def isLess(param1: TypeParamRef, param2: TypeParamRef): Boolean = diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index e7f54d088c09..9fff257ee963 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -261,7 +261,7 @@ trait PatternTypeConstrainer { self: TypeComparer => val assumeInvariantRefinement = migrateTo3 || forceInvariantRefinement || refinementIsInvariant(patternTp) - trace(i"constraining simple pattern type $tp >:< $pt", gadts, (res: Boolean) => i"$res gadt = ${ctx.gadt}") { + trace(i"constraining simple pattern type $tp >:< $pt", gadts, (res: Boolean) => i"$res gadt = ${ctx.gadt.gadt}") { (tp, pt) match { case (AppliedType(tyconS, argsS), AppliedType(tyconP, argsP)) => val saved = state.nn.constraint diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index cf2507aa1724..32b41ed9c63b 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -1445,14 +1445,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling if tp2 eq NoType then false else if tp1 eq tp2 then true else - val saved = constraint - val savedGadtConstr = ctx.gadt.getConstraint - val savedMapping = ctx.gadt.getMapping - val savedReverseMapping = ctx.gadt.getReverseMapping - val savedWasConstrained = ctx.gadt.getWasConstrained + val savedCstr = constraint + val savedGadt = ctx.gadt.gadt inline def restore() = - state.constraint = saved - ctx.gadt.restore(savedGadtConstr, savedMapping, savedReverseMapping, savedWasConstrained) + state.constraint = savedCstr + ctx.gadt.restore(savedGadt) val savedSuccessCount = successCount try recCount += 1 @@ -1858,22 +1855,23 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling */ private def necessaryEither(op1: => Boolean, op2: => Boolean): Boolean = val preConstraint = constraint - val preGadt = ctx.gadt.fresh + val preGadtHandling = ctx.gadt.fresh + val preGadt = preGadtHandling.gadt def allSubsumes(leftGadt: GadtConstraint, rightGadt: GadtConstraint, left: Constraint, right: Constraint): Boolean = - subsumes(left, right, preConstraint) && preGadt.subsumes(leftGadt, rightGadt, preGadt) + subsumes(left, right, preConstraint) && preGadtHandling.subsumes(leftGadt, rightGadt, preGadt) if op1 then val op1Constraint = constraint - val op1Gadt = ctx.gadt.fresh + val op1Gadt = ctx.gadt.gadt constraint = preConstraint ctx.gadt.restore(preGadt) if op2 then - if allSubsumes(op1Gadt, ctx.gadt, op1Constraint, constraint) then - gadts.println(i"GADT CUT - prefer ${ctx.gadt} over $op1Gadt") + if allSubsumes(op1Gadt, ctx.gadt.gadt, op1Constraint, constraint) then + gadts.println(i"GADT CUT - prefer ${ctx.gadt.gadt} over $op1Gadt") constr.println(i"CUT - prefer $constraint over $op1Constraint") - else if allSubsumes(ctx.gadt, op1Gadt, constraint, op1Constraint) then - gadts.println(i"GADT CUT - prefer $op1Gadt over ${ctx.gadt}") + else if allSubsumes(ctx.gadt.gadt, op1Gadt, constraint, op1Constraint) then + gadts.println(i"GADT CUT - prefer $op1Gadt over ${ctx.gadt.gadt}") constr.println(i"CUT - prefer $op1Constraint over $constraint") constraint = op1Constraint ctx.gadt.restore(op1Gadt) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 5abb32b15d57..19a18305ee65 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -269,7 +269,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case CaseDef(pat, _, _) => val gadtCtx = pat.removeAttachment(typer.Typer.InferredGadtConstraints) match - case Some(gadt) => ctx.fresh.setGadt(gadt) + case Some(gadt) => ctx.fresh.setGadt(GadtConstraintHandling(gadt)) case None => ctx super.transform(tree)(using gadtCtx) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 3e0e7dd5879d..596ad01bb888 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1030,7 +1030,7 @@ trait Implicits: case result: SearchSuccess => if result.tstate ne ctx.typerState then result.tstate.commit() - if result.gstate ne ctx.gadt then + if result.gstate ne ctx.gadt.gadt then ctx.gadt.restore(result.gstate) if hasSkolem(false, result.tree) then report.error(SkolemInInferred(result.tree, pt, argument), ctx.source.atSpan(span)) @@ -1145,7 +1145,7 @@ trait Implicits: SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument))) } else - SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt) + SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt.gadt) } /** An implicit search; parameters as in `inferImplicit` */ @@ -1343,7 +1343,7 @@ trait Implicits: case _: SearchFailure => SearchSuccess(ref(defn.NotGiven_value), defn.NotGiven_value.termRef, 0)( ctx.typerState.fresh().setCommittable(true), - ctx.gadt + ctx.gadt.gadt ) case _: SearchSuccess => NoMatchingImplicitsFailure @@ -1526,7 +1526,7 @@ trait Implicits: // other candidates need to be considered. recursiveRef match case ref: TermRef => - SearchSuccess(tpd.ref(ref).withSpan(span.startPos), ref, 0)(ctx.typerState, ctx.gadt) + SearchSuccess(tpd.ref(ref).withSpan(span.startPos), ref, 0)(ctx.typerState, ctx.gadt.gadt) case _ => searchImplicit(contextual = true) end bestImplicit diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 1a24a94e527e..1cb723b85c27 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1782,7 +1782,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // see tests/pos/i12226 and issue #12226. It might be possible that this // will end up taking too much memory. If it does, we should just limit // how much GADT constraints we infer - it's always sound to infer less. - pat1.putAttachment(InferredGadtConstraints, ctx.gadt) + pat1.putAttachment(InferredGadtConstraints, ctx.gadt.gadt) if (pt1.isValueType) // insert a cast if body does not conform to expected type if we disregard gadt bounds body1 = body1.ensureConforms(pt1)(using originalCtx) assignType(cpy.CaseDef(tree)(pat1, guard1, body1), pat1, body1) @@ -3835,7 +3835,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer adaptToSubType(wtp) case CompareResult.OKwithGADTUsed if pt.isValueType - && !inContext(ctx.fresh.setGadt(GadtConstraint.empty)) { + && !inContext(ctx.fresh.setGadt(GadtConstraintHandling(GadtConstraint.empty))) { val res = (tree.tpe.widenExpr frozen_<:< pt) if res then // we overshot; a cast is not needed, after all. From a12e92434a49fa7fd79894c3e66a77acd5230aa1 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 24 Jan 2023 15:46:55 +0100 Subject: [PATCH 029/657] Check outer class prefixes in type projections when pattern matching A type pattern of the form A#B should be checked if the prefix A is not already the owner of B or a superclass. Fixes #16728 --- .../tools/dotc/transform/PatternMatcher.scala | 73 +++++++++++-------- .../fatal-warnings/i16728.check | 4 + .../fatal-warnings/i16728.scala | 32 ++++++++ tests/run/i16728.check | 3 + tests/run/i16728.scala | 52 +++++++++++++ 5 files changed, 132 insertions(+), 32 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i16728.check create mode 100644 tests/neg-custom-args/fatal-warnings/i16728.scala create mode 100644 tests/run/i16728.check create mode 100644 tests/run/i16728.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 63ffdffbddef..6a38c40f6916 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -11,6 +11,7 @@ import util.Spans._ import typer.Applications.* import SymUtils._ import TypeUtils.* +import Annotations.* import Flags._, Constants._ import Decorators._ import NameKinds.{PatMatStdBinderName, PatMatAltsName, PatMatResultName} @@ -707,9 +708,9 @@ object PatternMatcher { // ----- Generating trees from plans --------------- /** The condition a test plan rewrites to */ - private def emitCondition(plan: TestPlan): Tree = { + private def emitCondition(plan: TestPlan): Tree = val scrutinee = plan.scrutinee - (plan.test: @unchecked) match { + (plan.test: @unchecked) match case NonEmptyTest => constToLiteral( scrutinee @@ -737,41 +738,49 @@ object PatternMatcher { case TypeTest(tpt, trusted) => val expectedTp = tpt.tpe - // An outer test is needed in a situation like `case x: y.Inner => ...` - def outerTestNeeded: Boolean = { - def go(expected: Type): Boolean = expected match { - case tref @ TypeRef(pre: SingletonType, _) => - tref.symbol.isClass && - ExplicitOuter.needsOuterIfReferenced(tref.symbol.asClass) - case AppliedType(tpe, _) => go(tpe) - case _ => - false - } - // See the test for SI-7214 for motivation for dealias. Later `treeCondStrategy#outerTest` - // generates an outer test based on `patType.prefix` with automatically dealises. - go(expectedTp.dealias) - } + def typeTest(scrut: Tree, expected: Type): Tree = + val ttest = scrut.select(defn.Any_typeTest).appliedToType(expected) + if trusted then ttest.pushAttachment(TrustedTypeTestKey, ()) + ttest - def outerTest: Tree = thisPhase.transformFollowingDeep { - val expectedOuter = singleton(expectedTp.normalizedPrefix) - val expectedClass = expectedTp.dealias.classSymbol.asClass - ExplicitOuter.ensureOuterAccessors(expectedClass) - scrutinee.ensureConforms(expectedTp) - .outerSelect(1, expectedClass.owner.typeRef) - .select(defn.Object_eq) - .appliedTo(expectedOuter) - } + /** An outer test is needed in a situation like `case x: y.Inner => ... + * or like case x: O#Inner if the owner of Inner is not a subclass of O. + * Outer tests are added here instead of in TypeTestsCasts since they + * might cause outer accessors to be added to inner classes (via ensureOuterAccessors) + * and therefore have to run before ExplicitOuter. + */ + def addOuterTest(tree: Tree, expected: Type): Tree = expected.dealias match + case tref @ TypeRef(pre, _) => + tref.symbol match + case expectedCls: ClassSymbol if ExplicitOuter.needsOuterIfReferenced(expectedCls) => + def selectOuter = + ExplicitOuter.ensureOuterAccessors(expectedCls) + scrutinee.ensureConforms(expected).outerSelect(1, expectedCls.owner.typeRef) + if pre.isSingleton then + val expectedOuter = singleton(pre) + tree.and(selectOuter.select(defn.Object_eq).appliedTo(expectedOuter)) + else if !expectedCls.isStatic + && expectedCls.owner.isType + && !expectedCls.owner.derivesFrom(pre.classSymbol) + then + val testPre = + if expected.hasAnnotation(defn.UncheckedAnnot) then + AnnotatedType(pre, Annotation(defn.UncheckedAnnot, tree.span)) + else pre + tree.and(typeTest(selectOuter, testPre)) + else tree + case _ => tree + case AppliedType(tycon, _) => + addOuterTest(tree, tycon) + case _ => + tree - expectedTp.dealias match { + expectedTp.dealias match case expectedTp: SingletonType => scrutinee.isInstance(expectedTp) // will be translated to an equality test case _ => - val typeTest = scrutinee.select(defn.Any_typeTest).appliedToType(expectedTp) - if (trusted) typeTest.pushAttachment(TrustedTypeTestKey, ()) - if (outerTestNeeded) typeTest.and(outerTest) else typeTest - } - } - } + addOuterTest(typeTest(scrutinee, expectedTp), expectedTp) + end emitCondition @tailrec private def canFallThrough(plan: Plan): Boolean = plan match { diff --git a/tests/neg-custom-args/fatal-warnings/i16728.check b/tests/neg-custom-args/fatal-warnings/i16728.check new file mode 100644 index 000000000000..a797baf19be0 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16728.check @@ -0,0 +1,4 @@ +-- Error: tests/neg-custom-args/fatal-warnings/i16728.scala:16:11 ------------------------------------------------------ +16 | case tx : C[Int]#X => // error + | ^ + | the type test for C[Int] cannot be checked at runtime because its type arguments can't be determined from A diff --git a/tests/neg-custom-args/fatal-warnings/i16728.scala b/tests/neg-custom-args/fatal-warnings/i16728.scala new file mode 100644 index 000000000000..42c860cc40b2 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16728.scala @@ -0,0 +1,32 @@ +class A[T] { + class X { + def outer : A.this.type = A.this + } +} + +class B extends A[Int] +class C[T] extends A[T] + +object Test { + def main(args: Array[String]) : Unit = { + val b0 = new B + val b0x : A[?]#X = new b0.X + + def test = b0x match { + case tx : C[Int]#X => // error + val c : C[Int] = tx.outer + c + case _ => + "no match" + } + + def test2 = b0x match { + case tx : C[Int]#X @unchecked => // ok + val c : C[Int] = tx.outer + c + case _ => + "no match" + } + + } +} \ No newline at end of file diff --git a/tests/run/i16728.check b/tests/run/i16728.check new file mode 100644 index 000000000000..06995cd05e9a --- /dev/null +++ b/tests/run/i16728.check @@ -0,0 +1,3 @@ +b1.X +B#X +ELSE diff --git a/tests/run/i16728.scala b/tests/run/i16728.scala new file mode 100644 index 000000000000..a1ada09e6d29 --- /dev/null +++ b/tests/run/i16728.scala @@ -0,0 +1,52 @@ +class A { + class X { + def outer : A.this.type = A.this + } +} + +class B extends A +class C extends A + +object Test { + def main(args: Array[String]) : Unit = { + val b0 = new B + val b1 = b0 + val b2 = new B + + val c0 = new C + val c1 = c0 + val c2 = new C + + val b0x : A#X = new b0.X + + val pathTypeMatch = b0x match { + case _ : c2.X => "c2.X" + case _ : c1.X => "c1.x" + case _ : c0.X => "c0.X" + case _ : b2.X => "b2.X" + case _ : b1.X => "b1.X" + case _ : b0.X => "b0.X" + case _ => "ELSE" + } + + println(pathTypeMatch) + + val projectionTypeMatch = b0x match { + case _ : C#X => "C#X" + case _ : B#X => "B#X" + case _ : A#X => "A#X" + case _ => "ELSE" + } + + println(projectionTypeMatch) + + val failingTypeMatch = b0x match { + case cx : C#X => + val c : C = cx.outer + c + case _ => "ELSE" + } + + println(failingTypeMatch) + } +} \ No newline at end of file From b1a035acad2deb74daaa8433e8ded1256bff3a81 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 24 Jan 2023 12:16:05 +0000 Subject: [PATCH 030/657] Rename GadtConstraintHandling to GadtState --- .../src/dotty/tools/dotc/core/Contexts.scala | 21 ++++++------ .../tools/dotc/core/GadtConstraint.scala | 33 +++++++------------ .../src/dotty/tools/dotc/core/NamerOps.scala | 6 ++-- .../dotc/core/PatternTypeConstrainer.scala | 4 +-- .../src/dotty/tools/dotc/core/Symbols.scala | 2 +- .../dotty/tools/dotc/core/TypeComparer.scala | 32 +++++++++--------- .../src/dotty/tools/dotc/core/TypeOps.scala | 6 ++-- .../tools/dotc/inlines/InlineReducer.scala | 4 +-- .../tools/dotc/transform/PostTyper.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 10 +++--- .../dotty/tools/dotc/typer/Inferencing.scala | 4 +-- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 8 ++--- .../quoted/runtime/impl/QuotesImpl.scala | 2 +- 14 files changed, 63 insertions(+), 73 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 398af92e494d..2f28975dd066 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -141,7 +141,8 @@ object Contexts { def tree: Tree[?] def scope: Scope def typerState: TyperState - def gadt: GadtConstraintHandling + def gadt: GadtConstraint = gadtState.gadt + def gadtState: GadtState def searchHistory: SearchHistory def source: SourceFile @@ -410,7 +411,7 @@ object Contexts { val constrCtx = outersIterator.dropWhile(_.outer.owner == owner).next() superOrThisCallContext(owner, constrCtx.scope) .setTyperState(typerState) - .setGadt(gadt) + .setGadtState(gadtState) .fresh .setScope(this.scope) } @@ -541,8 +542,8 @@ object Contexts { private var _typerState: TyperState = uninitialized final def typerState: TyperState = _typerState - private var _gadt: GadtConstraintHandling = uninitialized - final def gadt: GadtConstraintHandling = _gadt + private var _gadtState: GadtState = uninitialized + final def gadtState: GadtState = _gadtState private var _searchHistory: SearchHistory = uninitialized final def searchHistory: SearchHistory = _searchHistory @@ -567,7 +568,7 @@ object Contexts { _owner = origin.owner _tree = origin.tree _scope = origin.scope - _gadt = origin.gadt + _gadtState = origin.gadtState _searchHistory = origin.searchHistory _source = origin.source _moreProperties = origin.moreProperties @@ -624,12 +625,12 @@ object Contexts { this._scope = typer.scope setTypeAssigner(typer) - def setGadt(gadt: GadtConstraintHandling): this.type = - util.Stats.record("Context.setGadt") - this._gadt = gadt + def setGadtState(gadtState: GadtState): this.type = + util.Stats.record("Context.setGadtState") + this._gadtState = gadtState this def setFreshGADTBounds: this.type = - setGadt(gadt.fresh) + setGadtState(gadtState.fresh) def setSearchHistory(searchHistory: SearchHistory): this.type = util.Stats.record("Context.setSearchHistory") @@ -721,7 +722,7 @@ object Contexts { .updated(notNullInfosLoc, Nil) .updated(compilationUnitLoc, NoCompilationUnit) c._searchHistory = new SearchRoot - c._gadt = GadtConstraintHandling(GadtConstraint.empty) + c._gadtState = GadtState(GadtConstraint.empty) c end FreshContext diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index d683c15a9241..a863a982a44d 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -133,20 +133,14 @@ class GadtConstraint private ( ): GadtConstraint = GadtConstraint(myConstraint, mapping, reverseMapping, wasConstrained) end GadtConstraint -object GadtConstraintHandling: - def apply(gadt: GadtConstraint): GadtConstraintHandling = new ProperGadtConstraintHandling(gadt) +object GadtState: + def apply(gadt: GadtConstraint): GadtState = ProperGadtState(gadt) -sealed trait GadtConstraintHandling(private var myGadt: GadtConstraint) { - this: ConstraintHandling => +sealed trait GadtState { + this: ConstraintHandling => // Hide ConstraintHandling within GadtConstraintHandling - def gadt: GadtConstraint = myGadt - private def gadt_=(g: GadtConstraint) = myGadt = g - - /** Exposes ConstraintHandling.subsumes */ - def subsumes(left: GadtConstraint, right: GadtConstraint, pre: GadtConstraint)(using Context): Boolean = { - def extractConstraint(g: GadtConstraint) = g.constraint - subsumes(extractConstraint(left), extractConstraint(right), extractConstraint(pre)) - } + def gadt: GadtConstraint + def gadt_=(g: GadtConstraint): Unit override protected def legalBound(param: TypeParamRef, rawBound: Type, isUpper: Boolean)(using Context): Type = // GADT constraints never involve wildcards and are not propagated outside @@ -233,13 +227,6 @@ sealed trait GadtConstraintHandling(private var myGadt: GadtConstraint) { result } - def isLess(sym1: Symbol, sym2: Symbol)(using Context): Boolean = gadt.isLess(sym1, sym2) - def fullBounds(sym: Symbol)(using Context): TypeBounds | Null = gadt.fullBounds(sym) - def bounds(sym: Symbol)(using Context): TypeBounds | Null = gadt.bounds(sym) - def contains(sym: Symbol)(using Context): Boolean = gadt.contains(sym) - def isNarrowing: Boolean = gadt.isNarrowing - def symbols: List[Symbol] = gadt.symbols - /** See [[ConstraintHandling.approximation]] */ def approximation(sym: Symbol, fromBelow: Boolean, maxLevel: Int = Int.MaxValue)(using Context): Type = { approximation(gadt.tvarOrError(sym).origin, fromBelow, maxLevel).match @@ -252,7 +239,7 @@ sealed trait GadtConstraintHandling(private var myGadt: GadtConstraint) { .showing(i"approximating $sym ~> $result", gadts) } - def fresh: GadtConstraintHandling = GadtConstraintHandling(gadt) + def fresh: GadtState = GadtState(gadt) /** Restore the GadtConstraint state. */ def restore(gadt: GadtConstraint): Unit = this.gadt = gadt @@ -281,5 +268,7 @@ sealed trait GadtConstraintHandling(private var myGadt: GadtConstraint) { override def constr = gadtsConstr } -// Hide ConstraintHandling within GadtConstraintHandling -private class ProperGadtConstraintHandling(gadt: GadtConstraint) extends ConstraintHandling with GadtConstraintHandling(gadt) +// Hide ConstraintHandling within GadtState +private class ProperGadtState(private var myGadt: GadtConstraint) extends ConstraintHandling with GadtState: + def gadt: GadtConstraint = myGadt + def gadt_=(gadt: GadtConstraint): Unit = myGadt = gadt diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index 66912537dbce..db6f72590818 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -212,11 +212,11 @@ object NamerOps: * by (ab?)-using GADT constraints. See pos/i941.scala. */ def linkConstructorParams(sym: Symbol, tparams: List[Symbol], rhsCtx: Context)(using Context): Unit = - rhsCtx.gadt.addToConstraint(tparams) + rhsCtx.gadtState.addToConstraint(tparams) tparams.lazyZip(sym.owner.typeParams).foreach { (psym, tparam) => val tr = tparam.typeRef - rhsCtx.gadt.addBound(psym, tr, isUpper = false) - rhsCtx.gadt.addBound(psym, tr, isUpper = true) + rhsCtx.gadtState.addBound(psym, tr, isUpper = false) + rhsCtx.gadtState.addBound(psym, tr, isUpper = true) } end NamerOps diff --git a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala index 9fff257ee963..5e8a960608e6 100644 --- a/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala +++ b/compiler/src/dotty/tools/dotc/core/PatternTypeConstrainer.scala @@ -261,12 +261,12 @@ trait PatternTypeConstrainer { self: TypeComparer => val assumeInvariantRefinement = migrateTo3 || forceInvariantRefinement || refinementIsInvariant(patternTp) - trace(i"constraining simple pattern type $tp >:< $pt", gadts, (res: Boolean) => i"$res gadt = ${ctx.gadt.gadt}") { + trace(i"constraining simple pattern type $tp >:< $pt", gadts, (res: Boolean) => i"$res gadt = ${ctx.gadt}") { (tp, pt) match { case (AppliedType(tyconS, argsS), AppliedType(tyconP, argsP)) => val saved = state.nn.constraint val result = - ctx.gadt.rollbackGadtUnless { + ctx.gadtState.rollbackGadtUnless { tyconS.typeParams.lazyZip(argsS).lazyZip(argsP).forall { (param, argS, argP) => val variance = param.paramVarianceSign if variance == 0 || assumeInvariantRefinement || diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index d14be2b0dfb9..aa3ae0c3c513 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -686,7 +686,7 @@ object Symbols { addToGadt: Boolean = true, flags: FlagSet = EmptyFlags)(using Context): Symbol = { val sym = newSymbol(ctx.owner, name, Case | flags, info, coord = span) - if (addToGadt && name.isTypeName) ctx.gadt.addToConstraint(sym) + if (addToGadt && name.isTypeName) ctx.gadtState.addToConstraint(sym) sym } diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 32b41ed9c63b..2a0072590550 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -116,7 +116,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling private def isBottom(tp: Type) = tp.widen.isRef(NothingClass) protected def gadtBounds(sym: Symbol)(using Context) = ctx.gadt.bounds(sym) - protected def gadtAddBound(sym: Symbol, b: Type, isUpper: Boolean): Boolean = ctx.gadt.addBound(sym, b, isUpper) + protected def gadtAddBound(sym: Symbol, b: Type, isUpper: Boolean): Boolean = ctx.gadtState.addBound(sym, b, isUpper) protected def typeVarInstance(tvar: TypeVar)(using Context): Type = tvar.underlying @@ -1446,10 +1446,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling else if tp1 eq tp2 then true else val savedCstr = constraint - val savedGadt = ctx.gadt.gadt + val savedGadt = ctx.gadt inline def restore() = state.constraint = savedCstr - ctx.gadt.restore(savedGadt) + ctx.gadtState.restore(savedGadt) val savedSuccessCount = successCount try recCount += 1 @@ -1855,34 +1855,34 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling */ private def necessaryEither(op1: => Boolean, op2: => Boolean): Boolean = val preConstraint = constraint - val preGadtHandling = ctx.gadt.fresh - val preGadt = preGadtHandling.gadt + val preGadt = ctx.gadt def allSubsumes(leftGadt: GadtConstraint, rightGadt: GadtConstraint, left: Constraint, right: Constraint): Boolean = - subsumes(left, right, preConstraint) && preGadtHandling.subsumes(leftGadt, rightGadt, preGadt) + subsumes(left, right, preConstraint) + && subsumes(leftGadt.constraint, rightGadt.constraint, preGadt.constraint) if op1 then val op1Constraint = constraint - val op1Gadt = ctx.gadt.gadt + val op1Gadt = ctx.gadt constraint = preConstraint - ctx.gadt.restore(preGadt) + ctx.gadtState.restore(preGadt) if op2 then - if allSubsumes(op1Gadt, ctx.gadt.gadt, op1Constraint, constraint) then - gadts.println(i"GADT CUT - prefer ${ctx.gadt.gadt} over $op1Gadt") + if allSubsumes(op1Gadt, ctx.gadt, op1Constraint, constraint) then + gadts.println(i"GADT CUT - prefer ${ctx.gadt} over $op1Gadt") constr.println(i"CUT - prefer $constraint over $op1Constraint") - else if allSubsumes(ctx.gadt.gadt, op1Gadt, constraint, op1Constraint) then - gadts.println(i"GADT CUT - prefer $op1Gadt over ${ctx.gadt.gadt}") + else if allSubsumes(ctx.gadt, op1Gadt, constraint, op1Constraint) then + gadts.println(i"GADT CUT - prefer $op1Gadt over ${ctx.gadt}") constr.println(i"CUT - prefer $op1Constraint over $constraint") constraint = op1Constraint - ctx.gadt.restore(op1Gadt) + ctx.gadtState.restore(op1Gadt) else gadts.println(i"GADT CUT - no constraint is preferable, reverting to $preGadt") constr.println(i"CUT - no constraint is preferable, reverting to $preConstraint") constraint = preConstraint - ctx.gadt.restore(preGadt) + ctx.gadtState.restore(preGadt) else constraint = op1Constraint - ctx.gadt.restore(op1Gadt) + ctx.gadtState.restore(op1Gadt) true else op2 end necessaryEither @@ -2054,7 +2054,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling gadts.println(i"narrow gadt bound of $tparam: ${tparam.info} from ${if (isUpper) "above" else "below"} to $bound ${bound.toString} ${bound.isRef(tparam)}") if (bound.isRef(tparam)) false else - ctx.gadt.rollbackGadtUnless(gadtAddBound(tparam, bound, isUpper)) + ctx.gadtState.rollbackGadtUnless(gadtAddBound(tparam, bound, isUpper)) } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index ea8dcee5fca5..d9da11c561e8 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -687,8 +687,8 @@ object TypeOps: val bound1 = massage(bound) if (bound1 ne bound) { if (checkCtx eq ctx) checkCtx = ctx.fresh.setFreshGADTBounds - if (!checkCtx.gadt.contains(sym)) checkCtx.gadt.addToConstraint(sym) - checkCtx.gadt.addBound(sym, bound1, fromBelow) + if (!checkCtx.gadt.contains(sym)) checkCtx.gadtState.addToConstraint(sym) + checkCtx.gadtState.addBound(sym, bound1, fromBelow) typr.println("install GADT bound $bound1 for when checking F-bounded $sym") } } @@ -872,7 +872,7 @@ object TypeOps: case tp: TypeRef if tp.symbol.exists && !tp.symbol.isClass => foldOver(tp.symbol :: xs, tp) case tp => foldOver(xs, tp) val syms2 = getAbstractSymbols(Nil, tp2).reverse - if syms2.nonEmpty then ctx.gadt.addToConstraint(syms2) + if syms2.nonEmpty then ctx.gadtState.addToConstraint(syms2) // If parent contains a reference to an abstract type, then we should // refine subtype checking to eliminate abstract types according to diff --git a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala index 42e86b71eff8..e1b2aaa02866 100644 --- a/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala +++ b/compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala @@ -311,11 +311,11 @@ class InlineReducer(inliner: Inliner)(using Context): def addTypeBindings(typeBinds: TypeBindsMap)(using Context): Unit = typeBinds.foreachBinding { case (sym, shouldBeMinimized) => newTypeBinding(sym, - ctx.gadt.approximation(sym, fromBelow = shouldBeMinimized, maxLevel = Int.MaxValue)) + ctx.gadtState.approximation(sym, fromBelow = shouldBeMinimized, maxLevel = Int.MaxValue)) } def registerAsGadtSyms(typeBinds: TypeBindsMap)(using Context): Unit = - if (typeBinds.size > 0) ctx.gadt.addToConstraint(typeBinds.keys) + if (typeBinds.size > 0) ctx.gadtState.addToConstraint(typeBinds.keys) pat match { case Typed(pat1, tpt) => diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 19a18305ee65..2039a8f19558 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -269,7 +269,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case CaseDef(pat, _, _) => val gadtCtx = pat.removeAttachment(typer.Typer.InferredGadtConstraints) match - case Some(gadt) => ctx.fresh.setGadt(GadtConstraintHandling(gadt)) + case Some(gadt) => ctx.fresh.setGadtState(GadtState(gadt)) case None => ctx super.transform(tree)(using gadtCtx) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 596ad01bb888..03d3011b4bcd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1030,8 +1030,8 @@ trait Implicits: case result: SearchSuccess => if result.tstate ne ctx.typerState then result.tstate.commit() - if result.gstate ne ctx.gadt.gadt then - ctx.gadt.restore(result.gstate) + if result.gstate ne ctx.gadt then + ctx.gadtState.restore(result.gstate) if hasSkolem(false, result.tree) then report.error(SkolemInInferred(result.tree, pt, argument), ctx.source.atSpan(span)) implicits.println(i"success: $result") @@ -1145,7 +1145,7 @@ trait Implicits: SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument))) } else - SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt.gadt) + SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt) } /** An implicit search; parameters as in `inferImplicit` */ @@ -1343,7 +1343,7 @@ trait Implicits: case _: SearchFailure => SearchSuccess(ref(defn.NotGiven_value), defn.NotGiven_value.termRef, 0)( ctx.typerState.fresh().setCommittable(true), - ctx.gadt.gadt + ctx.gadt ) case _: SearchSuccess => NoMatchingImplicitsFailure @@ -1526,7 +1526,7 @@ trait Implicits: // other candidates need to be considered. recursiveRef match case ref: TermRef => - SearchSuccess(tpd.ref(ref).withSpan(span.startPos), ref, 0)(ctx.typerState, ctx.gadt.gadt) + SearchSuccess(tpd.ref(ref).withSpan(span.startPos), ref, 0)(ctx.typerState, ctx.gadt) case _ => searchImplicit(contextual = true) end bestImplicit diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 2aef3433228b..3442207653d4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -262,7 +262,7 @@ object Inferencing { && ctx.gadt.contains(tp.symbol) => val sym = tp.symbol - val res = ctx.gadt.approximation(sym, fromBelow = variance < 0) + val res = ctx.gadtState.approximation(sym, fromBelow = variance < 0) gadts.println(i"approximated $tp ~~ $res") res @@ -432,7 +432,7 @@ object Inferencing { } // We add the created symbols to GADT constraint here. - if (res.nonEmpty) ctx.gadt.addToConstraint(res) + if (res.nonEmpty) ctx.gadtState.addToConstraint(res) res } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6cdd0150518b..6f85efb0fc8a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1864,7 +1864,7 @@ class Namer { typer: Typer => // so we must allow constraining its type parameters // compare with typedDefDef, see tests/pos/gadt-inference.scala rhsCtx.setFreshGADTBounds - rhsCtx.gadt.addToConstraint(typeParams) + rhsCtx.gadtState.addToConstraint(typeParams) } def typedAheadRhs(pt: Type) = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 1cb723b85c27..eb09d30e60f3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1782,7 +1782,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // see tests/pos/i12226 and issue #12226. It might be possible that this // will end up taking too much memory. If it does, we should just limit // how much GADT constraints we infer - it's always sound to infer less. - pat1.putAttachment(InferredGadtConstraints, ctx.gadt.gadt) + pat1.putAttachment(InferredGadtConstraints, ctx.gadt) if (pt1.isValueType) // insert a cast if body does not conform to expected type if we disregard gadt bounds body1 = body1.ensureConforms(pt1)(using originalCtx) assignType(cpy.CaseDef(tree)(pat1, guard1, body1), pat1, body1) @@ -2362,7 +2362,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer ctx.outer.outersIterator.takeWhile(!_.owner.is(Method)) .filter(ctx => ctx.owner.isClass && ctx.owner.typeParams.nonEmpty) .toList.reverse - .foreach(ctx => rhsCtx.gadt.addToConstraint(ctx.owner.typeParams)) + .foreach(ctx => rhsCtx.gadtState.addToConstraint(ctx.owner.typeParams)) if tparamss.nonEmpty then rhsCtx.setFreshGADTBounds @@ -2371,7 +2371,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // we're typing a polymorphic definition's body, // so we allow constraining all of its type parameters // constructors are an exception as we don't allow constraining type params of classes - rhsCtx.gadt.addToConstraint(tparamSyms) + rhsCtx.gadtState.addToConstraint(tparamSyms) else if !sym.isPrimaryConstructor then linkConstructorParams(sym, tparamSyms, rhsCtx) @@ -3835,7 +3835,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer adaptToSubType(wtp) case CompareResult.OKwithGADTUsed if pt.isValueType - && !inContext(ctx.fresh.setGadt(GadtConstraintHandling(GadtConstraint.empty))) { + && !inContext(ctx.fresh.setGadtState(GadtState(GadtConstraint.empty))) { val res = (tree.tpe.widenExpr frozen_<:< pt) if res then // we overshot; a cast is not needed, after all. diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index dd6471a882bd..4d08e0582d1d 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -3130,7 +3130,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler if typeHoles.isEmpty then ctx else val ctx1 = ctx.fresh.setFreshGADTBounds.addMode(dotc.core.Mode.GadtConstraintInference) - ctx1.gadt.addToConstraint(typeHoles) + ctx1.gadtState.addToConstraint(typeHoles) ctx1 val matchings = QuoteMatcher.treeMatch(scrutinee, pat1)(using ctx1) From 1978bcb55859fbd4373767282e323abaf8526640 Mon Sep 17 00:00:00 2001 From: Mohammad Yousuf Minhaj Zia Date: Wed, 25 Jan 2023 00:48:25 +0800 Subject: [PATCH 031/657] Added jpath check to `ClassLikeSupport` getParentsAsTreeSymbolTuples Fixes #15927 Check for whether the non-scala3 parent exists before checking the start and end of the span to confirm whether the span exists in getParentsAsTreeSymbolTuples. --- scaladoc-testcases/src/tests/nonScala3Parent.scala | 13 +++++++++++++ .../tools/scaladoc/tasty/ClassLikeSupport.scala | 3 ++- .../TranslatableSignaturesTestCases.scala | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 scaladoc-testcases/src/tests/nonScala3Parent.scala diff --git a/scaladoc-testcases/src/tests/nonScala3Parent.scala b/scaladoc-testcases/src/tests/nonScala3Parent.scala new file mode 100644 index 000000000000..91183d25b583 --- /dev/null +++ b/scaladoc-testcases/src/tests/nonScala3Parent.scala @@ -0,0 +1,13 @@ +package tests +package nonScala3Parent + +import javax.swing.JPanel +import javax.swing.JFrame + +// https://github.com/lampepfl/dotty/issues/15927 + +trait Foo1 extends Numeric[Any] +trait Foo2 extends JPanel +trait Foo3 extends JFrame +trait Foo4 extends Ordering[Any] +trait Foo5 extends Enumeration diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 920621b8577c..38cc90330265 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -266,7 +266,8 @@ trait ClassLikeSupport: def getParentsAsTreeSymbolTuples: List[(Tree, Symbol)] = if noPosClassDefs.contains(c.symbol) then Nil else for - parentTree <- c.parents if parentTree.pos.start != parentTree.pos.end // We assume here that order is correct + // TODO: add exists function to position methods in Quotes and replace the condition here for checking the JPath + parentTree <- c.parents if parentTree.pos.sourceFile.getJPath.isDefined && parentTree.pos.start != parentTree.pos.end // We assume here that order is correct parentSymbol = parentTree match case t: TypeTree => t.tpe.typeSymbol case tree if tree.symbol.isClassConstructor => tree.symbol.owner diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala index ab7c2189e5d5..49316b08dbc0 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala @@ -106,3 +106,5 @@ class ImplicitMembers extends SignatureTest( Seq("def"), filterFunc = _.toString.endsWith("OuterClass$ImplicitMemberTarget.html") ) + +class NonScala3Parent extends SignatureTest("nonScala3Parent", SignatureTest.all) From 208071a5b333541d2e2db6cefc91c41ef1c55138 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 24 Jan 2023 22:12:23 -0500 Subject: [PATCH 032/657] Test implicitly() too; change err msg --- .../dotty/tools/dotc/reporting/messages.scala | 6 ++--- tests/neg/i16453.check | 24 +++++++++++++------ tests/neg/i16453.scala | 13 ++++++---- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index be5b4c16d170..743a50a33700 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2746,9 +2746,9 @@ class MissingImplicitArgument( i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." def noChainConversionsNote(s: Iterable[TermRef]): Option[String] = Option.when(s.nonEmpty)( - i"\n\nNote: Chaining implicit conversions is no longer allowed in Scala. " + - i"The following conversions were ignored:${s.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + - i"\nIf there is an implicit `Conversion[A, ${pt.show}]` and an implicit `A` in scope, you need to explicitly pass the argument, e.g. `(using summon[A])`" + i"\n\nNote: Chaining implicit conversions are not allowed in Scala. " + + i"The following conversions are in scope:${s.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + + i"\nIf there is an implicit `Conversion[A, ${pt.show}]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])`" ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index 871df3b6d7d2..d112266a44c6 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -1,8 +1,18 @@ --- [E172] Type Error: tests/neg/i16453.scala:10:43 --------------------------------------------------------------------- -10 | val fails = summon[String => Option[Int]] // error - | ^ - |No given instance of type String => Option[Int] was found for parameter x of method summon in object Predef +-- [E172] Type Error: tests/neg/i16453.scala:14:21 --------------------------------------------------------------------- +14 | summon[Option[Int]] // error + | ^ + |No given instance of type Option[Int] was found for parameter x of method summon in object Predef | - |Note: Chaining implicit conversions is no longer allowed in Scala. The following conversions were ignored: - | - final given def given_Conversion_Function_Function[T]: Conversion[String => T, String => Option[T]] - |If there is an implicit `Conversion[A, String => Option[Int]]` and an implicit `A` in scope, you need to explicitly pass the argument, e.g. `(using summon[A])` + |Note: Chaining implicit conversions are not allowed in Scala. The following conversions are in scope: + | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] + | - implicit def toOption[T](t: T): Option[T] + |If there is an implicit `Conversion[A, Option[Int]]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])` +-- [E172] Type Error: tests/neg/i16453.scala:15:25 --------------------------------------------------------------------- +15 | implicitly[Option[Int]] // error + | ^ + |No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef + | + |Note: Chaining implicit conversions are not allowed in Scala. The following conversions are in scope: + | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] + | - implicit def toOption[T](t: T): Option[T] + |If there is an implicit `Conversion[A, Option[Int]]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])` diff --git a/tests/neg/i16453.scala b/tests/neg/i16453.scala index b60c174e4077..a143237711d9 100644 --- a/tests/neg/i16453.scala +++ b/tests/neg/i16453.scala @@ -1,11 +1,16 @@ import scala.language.implicitConversions -given [T]: Conversion[String => T, String => Option[T]] = ??? +// Scala 3 style conversion +given [T]: Conversion[T, Option[T]] = ??? +// Scala 2 style conversion +implicit def toOption[T](t: T): Option[T] = Option(t) + // This one is irrelevant, shouldn't be included in error message -given irrelevant[T]: Conversion[String => T, String => Byte] = ??? +given irrelevant: Conversion[Int, Option[Long]] = ??? def test() = { - given foo: (String => Int) = _ => 42 + given foo: Int = 0 - val fails = summon[String => Option[Int]] // error + summon[Option[Int]] // error + implicitly[Option[Int]] // error } From a527b10706fbced4158caeb58cca7bdf9e11c8ee Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 23 Jan 2023 09:26:12 +0000 Subject: [PATCH 033/657] Fix isFullyDefined trace First, switch to the "i" interpolator and replace "IFT" with "isFullyDefined". Then apply it to the whole apply, and make the dealias appear in the trace. Requires fixing an early return usage. Also expose the ForceDegree specified. Found this in the course of enabling tracing and the typr printer and seeing lots of verbose type case classes printed. --- .../tools/dotc/printing/Formatting.scala | 1 + .../dotty/tools/dotc/typer/Inferencing.scala | 32 +++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index f4bbd74842c8..0acfd06de433 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -90,6 +90,7 @@ object Formatting { given Show[util.SourceFile] = ShowAny given Show[util.Spans.Span] = ShowAny given Show[tasty.TreeUnpickler#OwnerTree] = ShowAny + given Show[typer.ForceDegree.Value] = ShowAny private def show1[A: Show](x: A)(using Context) = show2(Show[A].show(x).ctxShow) private def show2(x: Shown)(using Context): String = x match diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 656a1266d5ab..dc83b49a8aa3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -170,14 +170,18 @@ object Inferencing { private var toMaximize: List[TypeVar] = Nil - def apply(x: Boolean, tp: Type): Boolean = - try tp.dealias match + def apply(x: Boolean, tp: Type): Boolean = trace(i"isFullyDefined($tp, $force)", typr) { + try { + val tpd = tp.dealias + if tpd ne tp then apply(x, tpd) + else tp match case _: WildcardType | _: ProtoType => false case tvar: TypeVar if !tvar.isInstantiated => force.appliesTo(tvar) && ctx.typerState.constraint.contains(tvar) && { + var fail = false val direction = instDirection(tvar.origin) if minimizeSelected then if direction <= 0 && tvar.hasLowerBound then @@ -190,17 +194,16 @@ object Inferencing { else if variance >= 0 && (force.ifBottom == IfBottom.ok || tvar.hasLowerBound) then instantiate(tvar, fromBelow = true) else if variance >= 0 && force.ifBottom == IfBottom.fail then - return false + fail = true else toMaximize = tvar :: toMaximize - foldOver(x, tvar) - } - case tp => - reporting.trace(s"IFT $tp") { - foldOver(x, tp) + !fail && foldOver(x, tvar) } + case tp => foldOver(x, tp) + } catch case ex: Throwable => handleRecursive("check fully defined", tp.show, ex) + } def process(tp: Type): Boolean = // Maximize type vars in the order they were visited before */ @@ -767,13 +770,14 @@ trait Inferencing { this: Typer => end constrainIfDependentParamRef } -/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */ +/** An enumeration controlling the degree of forcing in "is-fully-defined" checks. */ @sharable object ForceDegree { - class Value(val appliesTo: TypeVar => Boolean, val ifBottom: IfBottom) - val none: Value = new Value(_ => false, IfBottom.ok) - val all: Value = new Value(_ => true, IfBottom.ok) - val failBottom: Value = new Value(_ => true, IfBottom.fail) - val flipBottom: Value = new Value(_ => true, IfBottom.flip) + class Value(val appliesTo: TypeVar => Boolean, val ifBottom: IfBottom): + override def toString = s"ForceDegree.Value(.., $ifBottom)" + val none: Value = new Value(_ => false, IfBottom.ok) { override def toString = "ForceDegree.none" } + val all: Value = new Value(_ => true, IfBottom.ok) { override def toString = "ForceDegree.all" } + val failBottom: Value = new Value(_ => true, IfBottom.fail) { override def toString = "ForceDegree.failBottom" } + val flipBottom: Value = new Value(_ => true, IfBottom.flip) { override def toString = "ForceDegree.flipBottom" } } enum IfBottom: From 1978b8b0ef35eb04debe9623904369d40cc49180 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 25 Jan 2023 16:00:13 +0100 Subject: [PATCH 034/657] Simplifications (2) --- .../src/dotty/tools/dotc/typer/Typer.scala | 71 +++++++++---------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 44ae2ac7a46c..b03a4962cf61 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1197,9 +1197,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer ) end typedIf - /** Decompose function prototype into a list of parameter prototypes, an optional list - * describing whether the parameter prototypes come from WildcardTypes, and a result prototype - * tree, using WildcardTypes where a type is not known. + /** Decompose function prototype into a list of parameter prototypes and a result + * prototype tree, using WildcardTypes where a type is not known. + * Note: parameter prototypes may be TypeBounds. * For the result type we do this even if the expected type is not fully * defined, which is a bit of a hack. But it's needed to make the following work * (see typers.scala and printers/PlainPrinter.scala for examples). @@ -1207,7 +1207,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * def double(x: Char): String = s"$x$x" * "abc" flatMap double */ - private def decomposeProtoFunction(pt: Type, defaultArity: Int, pos: SrcPos)(using Context): (List[Type], Option[List[Boolean]], untpd.Tree) = { + private def decomposeProtoFunction(pt: Type, defaultArity: Int, pos: SrcPos)(using Context): (List[Type], untpd.Tree) = { def typeTree(tp: Type) = tp match { case _: WildcardType => new untpd.InferredTypeTree() case _ => untpd.InferredTypeTree(tp) @@ -1235,26 +1235,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // if expected parameter type(s) are wildcards, approximate from below. // if expected result type is a wildcard, approximate from above. // this can type the greatest set of admissible closures. - // However, we still keep the information on whether expected parameter types were - // wildcards, in case of types inferred from target being more specific - val fromWildcards = pt1.argInfos.init.map{ - case bounds @ TypeBounds(nt, at) if nt == defn.NothingType && at == defn.AnyType => true - case bounds => false - } - - (pt1.argTypesLo.init, Some(fromWildcards), typeTree(interpolateWildcards(pt1.argTypesHi.last))) + (pt1.argInfos.init, typeTree(interpolateWildcards(pt1.argInfos.last.hiBound))) case RefinedType(parent, nme.apply, mt @ MethodTpe(_, formals, restpe)) if defn.isNonRefinedFunction(parent) && formals.length == defaultArity => - (formals, None, untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef)))) + (formals, untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef)))) case SAMType(mt @ MethodTpe(_, formals, restpe)) => - (formals, None, + (formals, if (mt.isResultDependent) untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef))) else typeTree(restpe)) case _ => - (List.tabulate(defaultArity)(alwaysWildcardType), None, untpd.TypeTree()) + (List.tabulate(defaultArity)(alwaysWildcardType), untpd.TypeTree()) } } } @@ -1276,7 +1269,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * If both attempts fail, return `NoType`. */ def inferredFromTarget( - param: untpd.ValDef, formal: Type, calleeType: Type, paramIndex: Name => Int, isWildcardParam: Boolean)(using Context): Type = + param: untpd.ValDef, formal: Type, calleeType: Type, paramIndex: Name => Int)(using Context): Type = val target = calleeType.widen match case mtpe: MethodType => val pos = paramIndex(param.name) @@ -1289,7 +1282,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else NoType case _ => NoType if target.exists then formal <:< target - if !isWildcardParam && isFullyDefined(formal, ForceDegree.flipBottom) then formal + if !formal.isExactlyNothing && isFullyDefined(formal, ForceDegree.flipBottom) then formal else if target.exists && isFullyDefined(target, ForceDegree.flipBottom) then target else NoType @@ -1466,7 +1459,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case _ => } - val (protoFormals, areWildcardParams, resultTpt) = decomposeProtoFunction(pt, params.length, tree.srcPos) + val (protoFormals, resultTpt) = decomposeProtoFunction(pt, params.length, tree.srcPos) def protoFormal(i: Int): Type = if (protoFormals.length == params.length) protoFormals(i) @@ -1482,11 +1475,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } var desugared: untpd.Tree = EmptyTree - if protoFormals.length == 1 && params.length != 1 && ptIsCorrectProduct(protoFormals.head) then - val isGenericTuple = - protoFormals.head.derivesFrom(defn.TupleClass) - && !defn.isTupleClass(protoFormals.head.typeSymbol) - desugared = desugar.makeTupledFunction(params, fnBody, isGenericTuple) + if protoFormals.length == 1 && params.length != 1 then + val firstFormal = protoFormals.head.loBound + if ptIsCorrectProduct(firstFormal) then + val isGenericTuple = + firstFormal.derivesFrom(defn.TupleClass) + && !defn.isTupleClass(firstFormal.typeSymbol) + desugared = desugar.makeTupledFunction(params, fnBody, isGenericTuple) else if protoFormals.length > 1 && params.length == 1 then def isParamRef(scrut: untpd.Tree): Boolean = scrut match case untpd.Annotated(scrut1, _) => isParamRef(scrut1) @@ -1508,22 +1503,22 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer for ((param, i) <- params.zipWithIndex) yield if (!param.tpt.isEmpty) param else - val formal = protoFormal(i) - val isWildcardParam = areWildcardParams.map(list => if i < list.length then list(i) else false).getOrElse(false) + val formalBounds = protoFormal(i) + val formal = formalBounds.loBound + val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing val knownFormal = isFullyDefined(formal, ForceDegree.failBottom) - // Since decomposeProtoFunction eagerly approximates function arguments - // from below, then in the case that the argument was also identified as - // a wildcard type we try to prioritize inferring from target, if possible. - // See issue 16405 (tests/run/16405.scala) - val (usingFormal, paramType) = - if !isWildcardParam && knownFormal then (true, formal) - else - val fromTarget = inferredFromTarget(param, formal, calleeType, paramIndex, isWildcardParam) - if fromTarget.exists then (false, fromTarget) - else if knownFormal then (true, formal) - else (false, errorType(AnonymousFunctionMissingParamType(param, tree, formal), param.srcPos)) + // If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound, + // try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala) + val paramType = + if knownFormal && !isBottomFromWildcard then + formal + else + inferredFromTarget(param, formal, calleeType, paramIndex).orElse( + if knownFormal then formal + else errorType(AnonymousFunctionMissingParamType(param, tree, formal), param.srcPos) + ) val paramTpt = untpd.TypedSplice( - (if usingFormal then InferredTypeTree() else untpd.TypeTree()) + (if knownFormal then InferredTypeTree() else untpd.TypeTree()) .withType(paramType.translateFromRepeated(toArray = false)) .withSpan(param.span.endPos) ) @@ -1594,7 +1589,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer typedMatchFinish(tree, tpd.EmptyTree, defn.ImplicitScrutineeTypeRef, cases1, pt) } else { - val (protoFormals, _, _) = decomposeProtoFunction(pt, 1, tree.srcPos) + val (protoFormals, _) = decomposeProtoFunction(pt, 1, tree.srcPos) val checkMode = if (pt.isRef(defn.PartialFunctionClass)) desugar.MatchCheck.None else desugar.MatchCheck.Exhaustive From 6bded27149b83c508d795bd5772bad43a8b334e6 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:01:38 -0500 Subject: [PATCH 035/657] Use clearer name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michał Pałka --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 743a50a33700..8b52544922ec 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2744,7 +2744,7 @@ class MissingImplicitArgument( // show all available additional info def hiddenImplicitNote(s: SearchSuccess) = i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." - def noChainConversionsNote(s: Iterable[TermRef]): Option[String] = + def noChainConversionsNote(ignoredConversions: Iterable[TermRef]): Option[String] = Option.when(s.nonEmpty)( i"\n\nNote: Chaining implicit conversions are not allowed in Scala. " + i"The following conversions are in scope:${s.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + From 76c826d2ec81f1475521bf09569e0dffbbfdd85e Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 25 Jan 2023 12:04:38 -0500 Subject: [PATCH 036/657] Update message to be more generic --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 8 ++++---- tests/neg/i16453.check | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 8b52544922ec..9816ac25023b 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2745,10 +2745,10 @@ class MissingImplicitArgument( def hiddenImplicitNote(s: SearchSuccess) = i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." def noChainConversionsNote(ignoredConversions: Iterable[TermRef]): Option[String] = - Option.when(s.nonEmpty)( - i"\n\nNote: Chaining implicit conversions are not allowed in Scala. " + - i"The following conversions are in scope:${s.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + - i"\nIf there is an implicit `Conversion[A, ${pt.show}]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])`" + Option.when(ignoredConversions.nonEmpty)( + i"\n\nNote: implicit conversions are not automatically applied to implicit arguments. " + + i"You will have to pass the argument explicitly.\n" + + i"The following conversions in scope result in ${pt.show}: ${ignoredConversions.map(g => s"\n - ${g.symbol.showDcl}").mkString}" ) super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index d112266a44c6..e9fb0443311e 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -3,16 +3,16 @@ | ^ |No given instance of type Option[Int] was found for parameter x of method summon in object Predef | - |Note: Chaining implicit conversions are not allowed in Scala. The following conversions are in scope: + |Note: implicit conversions are not automatically applied to implicit arguments. You will have to pass the argument explicitly. + |The following conversions in scope result in Option[Int]: | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] | - implicit def toOption[T](t: T): Option[T] - |If there is an implicit `Conversion[A, Option[Int]]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])` -- [E172] Type Error: tests/neg/i16453.scala:15:25 --------------------------------------------------------------------- 15 | implicitly[Option[Int]] // error | ^ |No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef | - |Note: Chaining implicit conversions are not allowed in Scala. The following conversions are in scope: + |Note: implicit conversions are not automatically applied to implicit arguments. You will have to pass the argument explicitly. + |The following conversions in scope result in Option[Int]: | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] | - implicit def toOption[T](t: T): Option[T] - |If there is an implicit `Conversion[A, Option[Int]]` and an implicit `A` in scope, you can explicitly pass the argument, e.g. `(using summon[A])` From adc1995e1313a35eabf4b9fbf56210ee13982316 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 25 Jan 2023 12:07:19 -0500 Subject: [PATCH 037/657] Edit message according to suggestion --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 9816ac25023b..def04818255a 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2746,7 +2746,7 @@ class MissingImplicitArgument( i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." def noChainConversionsNote(ignoredConversions: Iterable[TermRef]): Option[String] = Option.when(ignoredConversions.nonEmpty)( - i"\n\nNote: implicit conversions are not automatically applied to implicit arguments. " + + i"\n\nNote: implicit conversions are not automatically applied to arguments of using clauses. " + i"You will have to pass the argument explicitly.\n" + i"The following conversions in scope result in ${pt.show}: ${ignoredConversions.map(g => s"\n - ${g.symbol.showDcl}").mkString}" ) From 90abf995c4c02a5de48fafba054e82dd91dcb222 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 25 Jan 2023 17:18:13 +0000 Subject: [PATCH 038/657] Fix tuple member selection so it works with GADT healing --- .../src/dotty/tools/dotc/core/Definitions.scala | 9 +++++++++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 17 ++++++++--------- tests/pos/i16590.scala | 13 +++++++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 tests/pos/i16590.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index ed86050436e8..854608143df9 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1675,6 +1675,15 @@ class Definitions { rec(tp.stripTypeVar, Nil, bound) } + def isSmallGenericTuple(tp: Type)(using Context): Boolean = + if tp.derivesFrom(defn.PairClass) && !defn.isTupleNType(tp.widenDealias) then + // If this is a generic tuple we need to cast it to make the TupleN/ members accessible. + // This works only for generic tuples of known size up to 22. + defn.tupleTypes(tp.widenTermRefExpr) match + case Some(elems) if elems.length <= Definitions.MaxTupleArity => true + case _ => false + else false + def isProductSubType(tp: Type)(using Context): Boolean = tp.derivesFrom(ProductClass) /** Is `tp` (an alias) of either a scala.FunctionN or a scala.ContextFunctionN diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 3c4146f7ca2d..67b5736b2c22 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -635,6 +635,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // There's a second trial where we try to instantiate all type variables in `qual.tpe.widen`, // but that is done only after we search for extension methods or conversions. typedSelect(tree, pt, qual) + else if defn.isSmallGenericTuple(qual.tpe) then + val elems = defn.tupleTypes(qual.tpe.widenTermRefExpr).getOrElse(Nil) + typedSelect(tree, pt, qual.cast(defn.tupleType(elems))) else val tree1 = tryExtensionOrConversion( tree, pt, IgnoredProto(pt), qual, ctx.typerState.ownedVars, this, inSelect = true) @@ -654,6 +657,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if checkedType1.exists then gadts.println(i"Member selection healed by GADT approximation") finish(tree1, qual1, checkedType1) + else if defn.isSmallGenericTuple(qual1.tpe) then + gadts.println(i"Tuple member selection healed by GADT approximation") + typedSelect(tree, pt, qual1) else tryExtensionOrConversion(tree1, pt, IgnoredProto(pt), qual1, ctx.typerState.ownedVars, this, inSelect = true) else EmptyTree @@ -3998,15 +4004,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else err.typeMismatch(tree, pt, failure) pt match - case pt: SelectionProto => - if tree.tpe.derivesFrom(defn.PairClass) && !defn.isTupleNType(tree.tpe.widenDealias) then - // If this is a generic tuple we need to cast it to make the TupleN/ members accessible. - // This works only for generic tuples of known size up to 22. - defn.tupleTypes(tree.tpe.widenTermRefExpr) match - case Some(elems) if elems.length <= Definitions.MaxTupleArity => - tree.cast(defn.tupleType(elems)) - case _ => tree - else tree // other adaptations for selections are handled in typedSelect + case _: SelectionProto => + tree // adaptations for selections are handled in typedSelect case _ if ctx.mode.is(Mode.ImplicitsEnabled) && tree.tpe.isValueType => if pt.isRef(defn.AnyValClass, skipRefined = false) || pt.isRef(defn.ObjectClass, skipRefined = false) diff --git a/tests/pos/i16590.scala b/tests/pos/i16590.scala new file mode 100644 index 000000000000..d70054fd52b4 --- /dev/null +++ b/tests/pos/i16590.scala @@ -0,0 +1,13 @@ +enum Tag[A]: + case MyTuple extends Tag[(String, String)] + +def printIt[A](t: Tag[A], a: A): Unit = + t match + case Tag.MyTuple => println(a._1) + +enum Tag2[A]: + case MyTuple extends Tag2[String *: String *: EmptyTuple] + +def printIt2[A](t: Tag2[A], a: A): Unit = + t match + case Tag2.MyTuple => println(a._1) From d13062aa301a29caa57c45e4892978154d7238f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 25 Jan 2023 19:37:26 +0100 Subject: [PATCH 039/657] Update base version to 3.3.1-RC1 Also fix incorrect tasty version --- project/Build.scala | 4 +-- project/MiMaFilters.scala | 25 ++++++++++++++++--- tasty/src/dotty/tools/tasty/TastyFormat.scala | 4 +-- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 9babd3c9c679..4b347db088a9 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -80,9 +80,9 @@ object DottyJSPlugin extends AutoPlugin { object Build { import ScaladocConfigs._ - val referenceVersion = "3.2.2" + val referenceVersion = "3.3.0-RC1" - val baseVersion = "3.3.0-RC1" + val baseVersion = "3.3.1-RC1" // Versions used by the vscode extension to create a new project // This should be the latest published releases. diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 689a4b8f1614..63ce926355bb 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -5,10 +5,29 @@ object MiMaFilters { val Library: Seq[ProblemFilter] = Seq( ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.unsafeBox"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.unsafeUnbox"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.CanEqual.canEqualMap"), + ProblemFilters.exclude[MissingClassProblem]("scala.caps$Pure"), + ProblemFilters.exclude[MissingClassProblem]("scala.caps$unsafe$"), + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.3.3-migration"), + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.3.3"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$3$u002E3$"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$3$u002E3$minusmigration$"), + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.into"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$into$"), + ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary"), + ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$"), + ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Break"), + ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Label") + ) + val TastyCore: Seq[ProblemFilter] = Seq( + ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.TastyBuffer.reset"), + ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.TastyFormat.APPLYsigpoly"), + ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.TastyHash.pjwHash64"), + ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.util.Util.dble") ) - val TastyCore: Seq[ProblemFilter] = Seq() val Interfaces: Seq[ProblemFilter] = Seq( - ProblemFilters.exclude[MissingClassProblem]("dotty.tools.dotc.interfaces.DiagnosticRelatedInformation"), - ProblemFilters.exclude[ReversedMissingMethodProblem]("dotty.tools.dotc.interfaces.Diagnostic.diagnosticRelatedInformation") + ProblemFilters.exclude[ReversedMissingMethodProblem]("dotty.tools.dotc.interfaces.Diagnostic.diagnosticRelatedInformation"), + ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.dotc.interfaces.Diagnostic.diagnosticRelatedInformation"), + ProblemFilters.exclude[MissingClassProblem]("dotty.tools.dotc.interfaces.DiagnosticRelatedInformation") ) } diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index ded313fb171c..23b74ed1d7c7 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -289,7 +289,7 @@ object TastyFormat { * compatibility, but remains backwards compatible, with all * preceeding `MinorVersion`. */ - final val MinorVersion: Int = 2 + final val MinorVersion: Int = 4 /** Natural Number. The `ExperimentalVersion` allows for * experimentation with changes to TASTy without committing @@ -305,7 +305,7 @@ object TastyFormat { * is able to read final TASTy documents if the file's * `MinorVersion` is strictly less than the current value. */ - final val ExperimentalVersion: Int = 0 + final val ExperimentalVersion: Int = 1 /**This method implements a binary relation (`<:<`) between two TASTy versions. * From 82d69a761d9912705f45613c0aaeff9ca90c54d2 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Wed, 25 Jan 2023 15:10:21 -0500 Subject: [PATCH 040/657] Update check --- tests/neg/i16453.check | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index e9fb0443311e..46fea0f7c8c0 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -3,7 +3,7 @@ | ^ |No given instance of type Option[Int] was found for parameter x of method summon in object Predef | - |Note: implicit conversions are not automatically applied to implicit arguments. You will have to pass the argument explicitly. + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. |The following conversions in scope result in Option[Int]: | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] | - implicit def toOption[T](t: T): Option[T] @@ -12,7 +12,7 @@ | ^ |No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef | - |Note: implicit conversions are not automatically applied to implicit arguments. You will have to pass the argument explicitly. + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. |The following conversions in scope result in Option[Int]: | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] | - implicit def toOption[T](t: T): Option[T] From 381242e2e73735bd3ac5e7e7c89303c819b2c527 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Mon, 16 Jan 2023 06:56:59 +0100 Subject: [PATCH 041/657] Extract reusable code --- .../tools/dotc/transform/init/Cache.scala | 132 +++++++ .../tools/dotc/transform/init/Errors.scala | 71 +--- .../tools/dotc/transform/init/Semantic.scala | 336 ++++-------------- .../tools/dotc/transform/init/Trace.scala | 76 ++++ .../tools/dotc/transform/init/Util.scala | 102 ++++++ 5 files changed, 394 insertions(+), 323 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/init/Cache.scala create mode 100644 compiler/src/dotty/tools/dotc/transform/init/Trace.scala create mode 100644 compiler/src/dotty/tools/dotc/transform/init/Util.scala diff --git a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala new file mode 100644 index 000000000000..5ee651c2ed02 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala @@ -0,0 +1,132 @@ +package dotty.tools.dotc +package transform +package init + +import core.* +import Contexts.* + +import ast.tpd +import tpd.Tree + +class Cache[Config, Res]: + import Cache.* + + /** The cache for expression values from last iteration */ + protected var last: ExprValueCache[Config, Res] = Map.empty + + /** The output cache for expression values + * + * The output cache is computed based on the cache values `last` from the + * last iteration. + * + * Both `last` and `current` are required to make sure an encountered + * expression is evaluated once in each iteration. + */ + protected var current: ExprValueCache[Config, Res] = Map.empty + + /** Whether the current heap is different from the last heap? + * + * `changed == false` implies that the fixed point has been reached. + */ + protected var changed: Boolean = false + + /** Used to avoid allocation, its state does not matter */ + protected given MutableTreeWrapper = new MutableTreeWrapper + + def get(config: Config, expr: Tree): Option[Res] = + current.get(config, expr) + + /** Copy the value (config, expr)` from the last cache to the current cache + * + * It assumes `default` if it doesn't exist in the last cache. + * + * It updates the current caches if the values change. + * + * The two caches are required because we want to make sure in a new iteration, an expression is evaluated once. + */ + def cachedEval(config: Config, expr: Tree, cacheResult: Boolean, default: Res)(eval: => Res): Res = + this.get(config, expr) match + case Some(value) => value + case None => + val assumeValue: Res = + last.get(config, expr) match + case Some(value) => value + case None => + this.last = last.updatedNested(config, expr, default) + default + + this.current = current.updatedNested(config, expr, assumeValue) + + val actual = eval + if actual != assumeValue then + // println("Changed! from = " + assumeValue + ", to = " + actual) + this.changed = true + // TODO: respect cacheResult to reduce cache size + this.current = this.current.updatedNested(config, expr, actual) + // this.current = this.current.removed(config, expr) + end if + + actual + end cachedEval + + def hasChanged = changed + + /** Prepare cache for the next iteration + * + * 1. Reset changed flag. + * + * 2. Use current cache as last cache and set current cache to be empty. + */ + def prepareForNextIteration()(using Context) = + this.changed = false + this.last = this.current + this.current = Map.empty +end Cache + +object Cache: + type ExprValueCache[Config, Res] = Map[Config, Map[TreeWrapper, Res]] + + /** A wrapper for trees for storage in maps based on referential equality of trees. */ + abstract class TreeWrapper: + def tree: Tree + + override final def equals(other: Any): Boolean = + other match + case that: TreeWrapper => this.tree eq that.tree + case _ => false + + override final def hashCode = tree.hashCode + + /** The immutable wrapper is intended to be stored as key in the heap. */ + class ImmutableTreeWrapper(val tree: Tree) extends TreeWrapper + + /** For queries on the heap, reuse the same wrapper to avoid unnecessary allocation. + * + * A `MutableTreeWrapper` is only ever used temporarily for querying a map, + * and is never inserted to the map. + */ + class MutableTreeWrapper extends TreeWrapper: + var queryTree: Tree | Null = null + def tree: Tree = queryTree match + case tree: Tree => tree + case null => ??? + + extension [Config, Res](cache: ExprValueCache[Config, Res]) + def get(config: Config, expr: Tree)(using queryWrapper: MutableTreeWrapper): Option[Res] = + queryWrapper.queryTree = expr + cache.get(config).flatMap(_.get(queryWrapper)) + + def removed(config: Config, expr: Tree)(using queryWrapper: MutableTreeWrapper) = + queryWrapper.queryTree = expr + val innerMap2 = cache(config).removed(queryWrapper) + cache.updated(config, innerMap2) + + def updatedNested(config: Config, expr: Tree, result: Res): ExprValueCache[Config, Res] = + val wrapper = new ImmutableTreeWrapper(expr) + updatedNestedWrapper(config, wrapper, result) + + def updatedNestedWrapper(config: Config, wrapper: ImmutableTreeWrapper, result: Res): ExprValueCache[Config, Res] = + val innerMap = cache.getOrElse(config, Map.empty[TreeWrapper, Res]) + val innerMap2 = innerMap.updated(wrapper, result) + cache.updated(config, innerMap2) + end extension diff --git a/compiler/src/dotty/tools/dotc/transform/init/Errors.scala b/compiler/src/dotty/tools/dotc/transform/init/Errors.scala index 7d92d2b2a921..762e029ba36f 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Errors.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Errors.scala @@ -5,106 +5,61 @@ package init import ast.tpd._ import core._ -import util.SourcePosition import util.Property -import Decorators._, printing.SyntaxHighlighting +import util.SourcePosition import Types._, Symbols._, Contexts._ -import scala.collection.mutable +import Trace.Trace object Errors: private val IsFromPromotion = new Property.Key[Boolean] sealed trait Error: - def trace: Seq[Tree] + def trace: Trace def show(using Context): String - def pos(using Context): SourcePosition = trace.last.sourcePos + def pos(using Context): SourcePosition = Trace.position(using trace).sourcePos def stacktrace(using Context): String = val preamble: String = if ctx.property(IsFromPromotion).nonEmpty then " Promotion trace:\n" else " Calling trace:\n" - buildStacktrace(trace, preamble) + Trace.buildStacktrace(trace, preamble) def issue(using Context): Unit = report.warning(show, this.pos) end Error - def buildStacktrace(trace: Seq[Tree], preamble: String)(using Context): String = if trace.isEmpty then "" else preamble + { - var lastLineNum = -1 - var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer - trace.foreach { tree => - val pos = tree.sourcePos - val prefix = "-> " - val line = - if pos.source.exists then - val loc = "[ " + pos.source.file.name + ":" + (pos.line + 1) + " ]" - val code = SyntaxHighlighting.highlight(pos.lineContent.trim.nn) - i"$code\t$loc" - else - tree.show - val positionMarkerLine = - if pos.exists && pos.source.exists then - positionMarker(pos) - else "" - - // always use the more precise trace location - if lastLineNum == pos.line then - lines.dropRightInPlace(1) - - lines += (prefix + line + "\n" + positionMarkerLine) - - lastLineNum = pos.line - } - val sb = new StringBuilder - for line <- lines do sb.append(line) - sb.toString - } - - /** Used to underline source positions in the stack trace - * pos.source must exist - */ - private def positionMarker(pos: SourcePosition): String = - val trimmed = pos.lineContent.takeWhile(c => c.isWhitespace).length - val padding = pos.startColumnPadding.substring(trimmed).nn + " " - val carets = - if (pos.startLine == pos.endLine) - "^" * math.max(1, pos.endColumn - pos.startColumn) - else "^" - - s"$padding$carets\n" - override def toString() = this.getClass.getName.nn /** Access non-initialized field */ - case class AccessNonInit(field: Symbol)(val trace: Seq[Tree]) extends Error: - def source: Tree = trace.last + case class AccessNonInit(field: Symbol)(val trace: Trace) extends Error: + def source: Tree = Trace.position(using trace) def show(using Context): String = "Access non-initialized " + field.show + "." + stacktrace override def pos(using Context): SourcePosition = field.sourcePos /** Promote a value under initialization to fully-initialized */ - case class PromoteError(msg: String)(val trace: Seq[Tree]) extends Error: + case class PromoteError(msg: String)(val trace: Trace) extends Error: def show(using Context): String = msg + stacktrace - case class AccessCold(field: Symbol)(val trace: Seq[Tree]) extends Error: + case class AccessCold(field: Symbol)(val trace: Trace) extends Error: def show(using Context): String = "Access field " + field.show + " on a cold object." + stacktrace - case class CallCold(meth: Symbol)(val trace: Seq[Tree]) extends Error: + case class CallCold(meth: Symbol)(val trace: Trace) extends Error: def show(using Context): String = "Call method " + meth.show + " on a cold object." + stacktrace - case class CallUnknown(meth: Symbol)(val trace: Seq[Tree]) extends Error: + case class CallUnknown(meth: Symbol)(val trace: Trace) extends Error: def show(using Context): String = val prefix = if meth.is(Flags.Method) then "Calling the external method " else "Accessing the external field" prefix + meth.show + " may cause initialization errors." + stacktrace /** Promote a value under initialization to fully-initialized */ - case class UnsafePromotion(msg: String, error: Error)(val trace: Seq[Tree]) extends Error: + case class UnsafePromotion(msg: String, error: Error)(val trace: Trace) extends Error: def show(using Context): String = msg + stacktrace + "\n" + "Promoting the value to hot (transitively initialized) failed due to the following problem:\n" + { @@ -116,7 +71,7 @@ object Errors: * * Invariant: argsIndices.nonEmpty */ - case class UnsafeLeaking(error: Error, nonHotOuterClass: Symbol, argsIndices: List[Int])(val trace: Seq[Tree]) extends Error: + case class UnsafeLeaking(error: Error, nonHotOuterClass: Symbol, argsIndices: List[Int])(val trace: Trace) extends Error: def show(using Context): String = "Problematic object instantiation: " + argumentInfo() + stacktrace + "\n" + "It leads to the following error during object initialization:\n" + diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index eb1692e00a12..4aaf0fa9ba72 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -15,10 +15,19 @@ import config.Printers.init as printer import reporting.trace as log import Errors.* +import Trace.* +import Util.* +import Cache.* import scala.collection.mutable import scala.annotation.tailrec +/** + * Checks safe initialization of objects + * + * This algorithm cannot handle safe access of global object names. That part + * is handled by the check in `Objects` (@see Objects). + */ object Semantic: // ----- Domain definitions -------------------------------- @@ -117,7 +126,7 @@ object Semantic: assert(!populatingParams, "the object is already populating parameters") populatingParams = true val tpl = klass.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template] - extendTrace(klass.defTree) { this.callConstructor(ctor, args.map(arg => ArgInfo(arg, trace))) } + extendTrace(klass.defTree) { this.callConstructor(ctor, args.map(arg => new ArgInfo(arg, trace))) } populatingParams = false this } @@ -207,7 +216,7 @@ object Semantic: object Cache: /** Cache for expressions * - * Ref -> Tree -> Value + * Value -> Tree -> Value * * The first key is the value of `this` for the expression. * @@ -233,66 +242,29 @@ object Semantic: * that could be reused to check other classes. We employ this trick to * improve performance of the analysis. */ - private type ExprValueCache = Map[Value, Map[TreeWrapper, Value]] + private type Config = Value + private type Res = Value /** The heap for abstract objects * - * The heap objects are immutable. - */ - private type Heap = Map[Ref, Objekt] - - /** A wrapper for trees for storage in maps based on referential equality of trees. */ - private abstract class TreeWrapper: - def tree: Tree - - override final def equals(other: Any): Boolean = - other match - case that: TreeWrapper => this.tree eq that.tree - case _ => false - - override final def hashCode = tree.hashCode - - /** The immutable wrapper is intended to be stored as key in the heap. */ - private class ImmutableTreeWrapper(val tree: Tree) extends TreeWrapper - - /** For queries on the heap, reuse the same wrapper to avoid unnecessary allocation. + * The heap objects are immutable and its values are essentially derived + * from the cache, thus they are not part of the configuration. * - * A `MutableTreeWrapper` is only ever used temporarily for querying a map, - * and is never inserted to the map. + * The only exception is the object correspond to `ThisRef`, where the + * object remembers the set of initialized fields. That information is reset + * in each iteration thus is harmless. */ - private class MutableTreeWrapper extends TreeWrapper: - var queryTree: Tree | Null = null - def tree: Tree = queryTree match - case tree: Tree => tree - case null => ??? - - class Cache: - /** The cache for expression values from last iteration */ - private var last: ExprValueCache = Map.empty + private type Heap = Map[Ref, Objekt] - /** The output cache for expression values - * - * The output cache is computed based on the cache values `last` from the - * last iteration. - * - * Both `last` and `current` are required to make sure an encountered - * expression is evaluated once in each iteration. - */ - private var current: ExprValueCache = Map.empty + class Data extends Cache[Config, Res]: /** Global cached values for expressions * * The values are only added when a fixed point is reached. * * It is intended to improve performance for computation related to warm values. */ - private var stable: ExprValueCache = Map.empty - - /** Whether the current heap is different from the last heap? - * - * `changed == false` implies that the fixed point has been reached. - */ - private var changed: Boolean = false + private var stable: ExprValueCache[Config, Res] = Map.empty /** Abstract heap stores abstract objects * @@ -320,77 +292,38 @@ object Semantic: /** Used to revert heap to last stable heap. */ private var heapStable: Heap = Map.empty - /** Used to avoid allocation, its state does not matter */ - private given MutableTreeWrapper = new MutableTreeWrapper - - def hasChanged = changed - - def get(value: Value, expr: Tree): Option[Value] = - current.get(value, expr) match - case None => stable.get(value, expr) + override def get(value: Value, expr: Tree): Option[Value] = + stable.get(value, expr) match + case None => super.get(value, expr) case res => res /** Backup the state of the cache * * All the shared data structures must be immutable. */ - def backup(): Cache = - val cache = new Cache - cache.last = this.last - cache.current = this.current + def backup(): Data = + val cache = new Data cache.stable = this.stable cache.heap = this.heap cache.heapStable = this.heapStable cache.changed = this.changed + cache.last = this.last + cache.current = this.current cache /** Restore state from a backup */ - def restore(cache: Cache) = + def restore(cache: Data) = + this.changed = cache.changed this.last = cache.last this.current = cache.current this.stable = cache.stable this.heap = cache.heap this.heapStable = cache.heapStable - this.changed = cache.changed - - /** Copy the value of `(value, expr)` from the last cache to the current cache - * - * It assumes the value is `Hot` if it doesn't exist in the last cache. - * - * It updates the current caches if the values change. - * - * The two caches are required because we want to make sure in a new iteration, an expression is evaluated once. - */ - def assume(value: Value, expr: Tree, cacheResult: Boolean)(fun: => Value): Contextual[Value] = - val assumeValue: Value = - last.get(value, expr) match - case Some(value) => value - case None => - this.last = last.updatedNested(value, expr, Hot) - Hot - - this.current = current.updatedNested(value, expr, assumeValue) - - val actual = fun - if actual != assumeValue then - this.changed = true - this.current = this.current.updatedNested(value, expr, actual) - else - // It's tempting to cache the value in stable, but it's unsound. - // The reason is that the current value may depend on other values - // which might change. - // - // stable.put(value, expr, actual) - () - end if - - actual - end assume /** Commit current cache to stable cache. */ private def commitToStableCache() = for - (v, m) <- current + (v, m) <- this.current if v.isWarm // It's useless to cache value for ThisRef. (wrapper, res) <- m do @@ -404,10 +337,8 @@ object Semantic: * * 3. Revert heap to stable. */ - def prepareForNextIteration()(using Context) = - this.changed = false - this.last = this.current - this.current = Map.empty + override def prepareForNextIteration()(using Context) = + super.prepareForNextIteration() this.heap = this.heapStable /** Prepare for checking next class @@ -421,15 +352,15 @@ object Semantic: * 4. Reset last cache. */ def prepareForNextClass()(using Context) = - if this.changed then - this.changed = false + if this.hasChanged then this.heap = this.heapStable else this.commitToStableCache() this.heapStable = this.heap - this.last = Map.empty - this.current = Map.empty + // reset changed and cache + super.prepareForNextIteration() + def updateObject(ref: Ref, obj: Objekt) = assert(!this.heapStable.contains(ref)) @@ -438,59 +369,19 @@ object Semantic: def containsObject(ref: Ref) = heap.contains(ref) def getObject(ref: Ref) = heap(ref) - end Cache - - extension (cache: ExprValueCache) - private def get(value: Value, expr: Tree)(using queryWrapper: MutableTreeWrapper): Option[Value] = - queryWrapper.queryTree = expr - cache.get(value).flatMap(_.get(queryWrapper)) - - private def removed(value: Value, expr: Tree)(using queryWrapper: MutableTreeWrapper) = - queryWrapper.queryTree = expr - val innerMap2 = cache(value).removed(queryWrapper) - cache.updated(value, innerMap2) - - private def updatedNested(value: Value, expr: Tree, result: Value): ExprValueCache = - val wrapper = new ImmutableTreeWrapper(expr) - updatedNestedWrapper(value, wrapper, result) - - private def updatedNestedWrapper(value: Value, wrapper: ImmutableTreeWrapper, result: Value): ExprValueCache = - val innerMap = cache.getOrElse(value, Map.empty[TreeWrapper, Value]) - val innerMap2 = innerMap.updated(wrapper, result) - cache.updated(value, innerMap2) - end extension - end Cache + end Data - import Cache.* + end Cache - inline def cache(using c: Cache): Cache = c + inline def cache(using c: Cache.Data): Cache.Data = c // ----- Checker State ----------------------------------- /** The state that threads through the interpreter */ - type Contextual[T] = (Context, Trace, Promoted, Cache, Reporter) ?=> T + type Contextual[T] = (Context, Trace, Promoted, Cache.Data, Reporter) ?=> T // ----- Error Handling ----------------------------------- - object Trace: - opaque type Trace = Vector[Tree] - - val empty: Trace = Vector.empty - - extension (trace: Trace) - def add(node: Tree): Trace = trace :+ node - def toVector: Vector[Tree] = trace - - def show(using trace: Trace, ctx: Context): String = buildStacktrace(trace, "\n") - - def position(using trace: Trace): Tree = trace.last - type Trace = Trace.Trace - - import Trace.* - def trace(using t: Trace): Trace = t - inline def withTrace[T](t: Trace)(op: Trace ?=> T): T = op(using t) - inline def extendTrace[T](node: Tree)(using t: Trace)(op: Trace ?=> T): T = op(using t.add(node)) - /** Error reporting */ trait Reporter: def report(err: Error): Unit @@ -508,7 +399,7 @@ object Semantic: /** * Revert the cache to previous state. */ - def abort()(using Cache): Unit + def abort()(using Cache.Data): Unit def errors: List[Error] object Reporter: @@ -517,8 +408,8 @@ object Semantic: def errors = buf.toList def report(err: Error) = buf += err - class TryBufferedReporter(backup: Cache) extends BufferedReporter with TryReporter: - def abort()(using Cache): Unit = cache.restore(backup) + class TryBufferedReporter(backup: Cache.Data) extends BufferedReporter with TryReporter: + def abort()(using Cache.Data): Unit = cache.restore(backup) class ErrorFound(val error: Error) extends Exception class StopEarlyReporter extends Reporter: @@ -529,7 +420,7 @@ object Semantic: * The TryReporter cannot be thrown away: either `abort` must be called or * the errors must be reported. */ - def errorsIn(fn: Reporter ?=> Unit)(using Cache): TryReporter = + def errorsIn(fn: Reporter ?=> Unit)(using Cache.Data): TryReporter = val reporter = new TryBufferedReporter(cache.backup()) fn(using reporter) reporter @@ -544,7 +435,7 @@ object Semantic: catch case ex: ErrorFound => ex.error :: Nil - def hasErrors(fn: Reporter ?=> Unit)(using Cache): Boolean = + def hasErrors(fn: Reporter ?=> Unit)(using Cache.Data): Boolean = val backup = cache.backup() val errors = stopEarly(fn) cache.restore(backup) @@ -606,14 +497,14 @@ object Semantic: case _ => cache.getObject(ref) - def ensureObjectExists()(using Cache): ref.type = + def ensureObjectExists()(using Cache.Data): ref.type = if cache.containsObject(ref) then printer.println("object " + ref + " already exists") ref else ensureFresh() - def ensureFresh()(using Cache): ref.type = + def ensureFresh()(using Cache.Data): ref.type = val obj = Objekt(ref.klass, fields = Map.empty, outers = Map(ref.klass -> ref.outer)) printer.println("reset object " + ref) cache.updateObject(ref, obj) @@ -664,7 +555,7 @@ object Semantic: Hot case Cold => - val error = AccessCold(field)(trace.toVector) + val error = AccessCold(field)(trace) reporter.report(error) Hot @@ -689,11 +580,11 @@ object Semantic: val rhs = target.defTree.asInstanceOf[ValOrDefDef].rhs eval(rhs, ref, target.owner.asClass, cacheResult = true) else - val error = CallUnknown(field)(trace.toVector) + val error = CallUnknown(field)(trace) reporter.report(error) Hot else - val error = AccessNonInit(target)(trace.toVector) + val error = AccessNonInit(target)(trace) reporter.report(error) Hot else @@ -779,7 +670,7 @@ object Semantic: case Cold => promoteArgs() - val error = CallCold(meth)(trace.toVector) + val error = CallCold(meth)(trace) reporter.report(error) Hot @@ -820,7 +711,7 @@ object Semantic: // try promoting the receiver as last resort val hasErrors = Reporter.hasErrors { ref.promote("try promote value to hot") } if hasErrors then - val error = CallUnknown(target)(trace.toVector) + val error = CallUnknown(target)(trace) reporter.report(error) Hot else if target.exists then @@ -899,7 +790,7 @@ object Semantic: Hot else // no source code available - val error = CallUnknown(ctor)(trace.toVector) + val error = CallUnknown(ctor)(trace) reporter.report(error) Hot } @@ -922,7 +813,7 @@ object Semantic: yield i + 1 - val error = UnsafeLeaking(errors.head, nonHotOuterClass, indices)(trace.toVector) + val error = UnsafeLeaking(errors.head, nonHotOuterClass, indices)(trace) reporter.report(error) Hot else @@ -947,7 +838,7 @@ object Semantic: tryLeak(warm, NoSymbol, args2) case Cold => - val error = CallCold(ctor)(trace.toVector) + val error = CallCold(ctor)(trace) reporter.report(error) Hot @@ -1078,7 +969,7 @@ object Semantic: case Hot => case Cold => - reporter.report(PromoteError(msg)(trace.toVector)) + reporter.report(PromoteError(msg)(trace)) case thisRef: ThisRef => val emptyFields = thisRef.nonInitFields() @@ -1086,7 +977,7 @@ object Semantic: promoted.promoteCurrent(thisRef) else val fields = "Non initialized field(s): " + emptyFields.map(_.show).mkString(", ") + "." - reporter.report(PromoteError(msg + "\n" + fields)(trace.toVector)) + reporter.report(PromoteError(msg + "\n" + fields)(trace)) case warm: Warm => if !promoted.contains(warm) then @@ -1106,7 +997,7 @@ object Semantic: res.promote("The function return value is not hot. Found = " + res.show + ".") } if errors.nonEmpty then - reporter.report(UnsafePromotion(msg, errors.head)(trace.toVector)) + reporter.report(UnsafePromotion(msg, errors.head)(trace)) else promoted.add(fun) @@ -1156,12 +1047,12 @@ object Semantic: if !isHotSegment then for member <- klass.info.decls do if member.isClass then - val error = PromoteError("Promotion cancelled as the value contains inner " + member.show + ".")(Vector.empty) + val error = PromoteError("Promotion cancelled as the value contains inner " + member.show + ".")(Trace.empty) reporter.report(error) else if !member.isType && !member.isConstructor && !member.is(Flags.Deferred) then given Trace = Trace.empty if member.is(Flags.Method, butNot = Flags.Accessor) then - val args = member.info.paramInfoss.flatten.map(_ => ArgInfo(Hot, Trace.empty)) + val args = member.info.paramInfoss.flatten.map(_ => new ArgInfo(Hot: Value, Trace.empty)) val res = warm.call(member, args, receiver = warm.klass.typeRef, superType = NoType) withTrace(trace.add(member.defTree)) { res.promote("Cannot prove that the return value of " + member.show + " is hot. Found = " + res.show + ".") @@ -1189,7 +1080,7 @@ object Semantic: } if errors.isEmpty then Nil - else UnsafePromotion(msg, errors.head)(trace.toVector) :: Nil + else UnsafePromotion(msg, errors.head)(trace) :: Nil } end extension @@ -1212,7 +1103,7 @@ object Semantic: * * The class to be checked must be an instantiable concrete class. */ - private def checkClass(classSym: ClassSymbol)(using Cache, Context): Unit = + private def checkClass(classSym: ClassSymbol)(using Cache.Data, Context): Unit = val thisRef = ThisRef(classSym) val tpl = classSym.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template] @@ -1246,16 +1137,16 @@ object Semantic: * Check the specified concrete classes */ def checkClasses(classes: List[ClassSymbol])(using Context): Unit = - given Cache() + given Cache.Data() for classSym <- classes if isConcreteClass(classSym) do checkClass(classSym) // ----- Semantic definition -------------------------------- + type ArgInfo = TraceValue[Value] - /** Utility definition used for better error-reporting of argument errors */ - case class ArgInfo(value: Value, trace: Trace): - def promote: Contextual[Unit] = withTrace(trace) { - value.promote("Cannot prove the method argument is hot. Only hot values are safe to leak.\nFound = " + value.show + ".") + extension (arg: ArgInfo) + def promote: Contextual[Unit] = withTrace(arg.trace) { + arg.value.promote("Cannot prove the method argument is hot. Only hot values are safe to leak.\nFound = " + arg.value.show + ".") } /** Evaluate an expression with the given value for `this` in a given class `klass` @@ -1279,10 +1170,7 @@ object Semantic: * @param cacheResult It is used to reduce the size of the cache. */ def eval(expr: Tree, thisV: Ref, klass: ClassSymbol, cacheResult: Boolean = false): Contextual[Value] = log("evaluating " + expr.show + ", this = " + thisV.show + " in " + klass.show, printer, (_: Value).show) { - cache.get(thisV, expr) match - case Some(value) => value - case None => - cache.assume(thisV, expr, cacheResult) { cases(expr, thisV, klass) } + cache.cachedEval(thisV, expr, cacheResult, default = Hot) { cases(expr, thisV, klass) } } /** Evaluate a list of expressions */ @@ -1299,7 +1187,7 @@ object Semantic: else eval(arg.tree, thisV, klass) - argInfos += ArgInfo(res, trace.add(arg.tree)) + argInfos += new ArgInfo(res, trace.add(arg.tree)) } argInfos.toList @@ -1667,7 +1555,7 @@ object Semantic: // The parameter check of traits comes late in the mixin phase. // To avoid crash we supply hot values for erroneous parent calls. // See tests/neg/i16438.scala. - val args: List[ArgInfo] = ctor.info.paramInfoss.flatten.map(_ => ArgInfo(Hot, Trace.empty)) + val args: List[ArgInfo] = ctor.info.paramInfoss.flatten.map(_ => new ArgInfo(Hot, Trace.empty)) extendTrace(superParent) { superCall(tref, ctor, args, tasks) } @@ -1726,85 +1614,3 @@ object Semantic: traverseChildren(tp) traverser.traverse(tpt.tpe) - -// ----- Utility methods and extractors -------------------------------- - - def typeRefOf(tp: Type)(using Context): TypeRef = tp.dealias.typeConstructor match - case tref: TypeRef => tref - case hklambda: HKTypeLambda => typeRefOf(hklambda.resType) - - - opaque type Arg = Tree | ByNameArg - case class ByNameArg(tree: Tree) - - extension (arg: Arg) - def isByName = arg.isInstanceOf[ByNameArg] - def tree: Tree = arg match - case t: Tree => t - case ByNameArg(t) => t - - object Call: - - def unapply(tree: Tree)(using Context): Option[(Tree, List[List[Arg]])] = - tree match - case Apply(fn, args) => - val argTps = fn.tpe.widen match - case mt: MethodType => mt.paramInfos - val normArgs: List[Arg] = args.zip(argTps).map { - case (arg, _: ExprType) => ByNameArg(arg) - case (arg, _) => arg - } - unapply(fn) match - case Some((ref, args0)) => Some((ref, args0 :+ normArgs)) - case None => None - - case TypeApply(fn, targs) => - unapply(fn) - - case ref: RefTree if ref.tpe.widenSingleton.isInstanceOf[MethodicType] => - Some((ref, Nil)) - - case _ => None - - object NewExpr: - def unapply(tree: Tree)(using Context): Option[(TypeRef, New, Symbol, List[List[Arg]])] = - tree match - case Call(fn @ Select(newTree: New, init), argss) if init == nme.CONSTRUCTOR => - val tref = typeRefOf(newTree.tpe) - Some((tref, newTree, fn.symbol, argss)) - case _ => None - - object PolyFun: - def unapply(tree: Tree)(using Context): Option[Tree] = - tree match - case Block((cdef: TypeDef) :: Nil, Typed(NewExpr(tref, _, _, _), _)) - if tref.symbol.isAnonymousClass && tref <:< defn.PolyFunctionType - => - val body = cdef.rhs.asInstanceOf[Template].body - val apply = body.head.asInstanceOf[DefDef] - Some(apply.rhs) - case _ => - None - - extension (symbol: Symbol) def hasSource(using Context): Boolean = - !symbol.defTree.isEmpty - - def resolve(cls: ClassSymbol, sym: Symbol)(using Context): Symbol = log("resove " + cls + ", " + sym, printer, (_: Symbol).show) { - if (sym.isEffectivelyFinal || sym.isConstructor) sym - else sym.matchingMember(cls.appliedRef) - } - - private def isConcreteClass(cls: ClassSymbol)(using Context) = { - val instantiable: Boolean = - cls.is(Flags.Module) || - !cls.isOneOf(Flags.AbstractOrTrait) && { - // see `Checking.checkInstantiable` in typer - val tp = cls.appliedRef - val stp = SkolemType(tp) - val selfType = cls.givenSelfType.asSeenFrom(stp, cls) - !selfType.exists || stp <:< selfType - } - - // A concrete class may not be instantiated if the self type is not satisfied - instantiable && cls.enclosingPackageClass != defn.StdLibPatchesPackage.moduleClass - } diff --git a/compiler/src/dotty/tools/dotc/transform/init/Trace.scala b/compiler/src/dotty/tools/dotc/transform/init/Trace.scala new file mode 100644 index 000000000000..048a3d318646 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/init/Trace.scala @@ -0,0 +1,76 @@ +package dotty.tools.dotc +package transform +package init + +import core.* +import Contexts.* +import ast.tpd.* +import util.SourcePosition + +import Decorators._, printing.SyntaxHighlighting + +import scala.collection.mutable + +object Trace: + opaque type Trace = Vector[Tree] + + val empty: Trace = Vector.empty + + extension (trace: Trace) + def add(node: Tree): Trace = trace :+ node + def toVector: Vector[Tree] = trace + def ++(trace2: Trace): Trace = trace ++ trace2 + + def show(using trace: Trace, ctx: Context): String = buildStacktrace(trace, "\n") + + def position(using trace: Trace): Tree = trace.last + + def trace(using t: Trace): Trace = t + + inline def withTrace[T](t: Trace)(op: Trace ?=> T): T = op(using t) + + inline def extendTrace[T](node: Tree)(using t: Trace)(op: Trace ?=> T): T = op(using t.add(node)) + + def buildStacktrace(trace: Trace, preamble: String)(using Context): String = if trace.isEmpty then "" else preamble + { + var lastLineNum = -1 + var lines: mutable.ArrayBuffer[String] = new mutable.ArrayBuffer + trace.foreach { tree => + val pos = tree.sourcePos + val prefix = "-> " + val line = + if pos.source.exists then + val loc = "[ " + pos.source.file.name + ":" + (pos.line + 1) + " ]" + val code = SyntaxHighlighting.highlight(pos.lineContent.trim.nn) + i"$code\t$loc" + else + tree.show + val positionMarkerLine = + if pos.exists && pos.source.exists then + positionMarker(pos) + else "" + + // always use the more precise trace location + if lastLineNum == pos.line then + lines.dropRightInPlace(1) + + lines += (prefix + line + "\n" + positionMarkerLine) + + lastLineNum = pos.line + } + val sb = new StringBuilder + for line <- lines do sb.append(line) + sb.toString + } + + /** Used to underline source positions in the stack trace + * pos.source must exist + */ + private def positionMarker(pos: SourcePosition): String = + val trimmed = pos.lineContent.takeWhile(c => c.isWhitespace).length + val padding = pos.startColumnPadding.substring(trimmed).nn + " " + val carets = + if (pos.startLine == pos.endLine) + "^" * math.max(1, pos.endColumn - pos.startColumn) + else "^" + + s"$padding$carets\n" diff --git a/compiler/src/dotty/tools/dotc/transform/init/Util.scala b/compiler/src/dotty/tools/dotc/transform/init/Util.scala new file mode 100644 index 000000000000..4e60c1325b09 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/init/Util.scala @@ -0,0 +1,102 @@ +package dotty.tools.dotc +package transform +package init + +import core.* +import Contexts.* +import Types.* +import Symbols.* +import StdNames.* +import ast.tpd.* + +import reporting.trace as log +import config.Printers.init as printer + +import Trace.* + +object Util: + /** Utility definition used for better error-reporting of argument errors */ + case class TraceValue[T](value: T, trace: Trace) + + def typeRefOf(tp: Type)(using Context): TypeRef = tp.dealias.typeConstructor match + case tref: TypeRef => tref + case hklambda: HKTypeLambda => typeRefOf(hklambda.resType) + + + opaque type Arg = Tree | ByNameArg + case class ByNameArg(tree: Tree) + + extension (arg: Arg) + def isByName = arg.isInstanceOf[ByNameArg] + def tree: Tree = arg match + case t: Tree => t + case ByNameArg(t) => t + + object Call: + + def unapply(tree: Tree)(using Context): Option[(Tree, List[List[Arg]])] = + tree match + case Apply(fn, args) => + val argTps = fn.tpe.widen match + case mt: MethodType => mt.paramInfos + val normArgs: List[Arg] = args.zip(argTps).map { + case (arg, _: ExprType) => ByNameArg(arg) + case (arg, _) => arg + } + unapply(fn) match + case Some((ref, args0)) => Some((ref, args0 :+ normArgs)) + case None => None + + case TypeApply(fn, targs) => + unapply(fn) + + case ref: RefTree if ref.tpe.widenSingleton.isInstanceOf[MethodicType] => + Some((ref, Nil)) + + case _ => None + + object NewExpr: + def unapply(tree: Tree)(using Context): Option[(TypeRef, New, Symbol, List[List[Arg]])] = + tree match + case Call(fn @ Select(newTree: New, init), argss) if init == nme.CONSTRUCTOR => + val tref = typeRefOf(newTree.tpe) + Some((tref, newTree, fn.symbol, argss)) + case _ => None + + object PolyFun: + def unapply(tree: Tree)(using Context): Option[Tree] = + tree match + case Block((cdef: TypeDef) :: Nil, Typed(NewExpr(tref, _, _, _), _)) + if tref.symbol.isAnonymousClass && tref <:< defn.PolyFunctionType + => + val body = cdef.rhs.asInstanceOf[Template].body + val apply = body.head.asInstanceOf[DefDef] + Some(apply.rhs) + case _ => + None + + def resolve(cls: ClassSymbol, sym: Symbol)(using Context): Symbol = log("resove " + cls + ", " + sym, printer, (_: Symbol).show) { + if (sym.isEffectivelyFinal || sym.isConstructor) sym + else sym.matchingMember(cls.appliedRef) + } + + + extension (sym: Symbol) + def hasSource(using Context): Boolean = !sym.defTree.isEmpty + + def isStaticObject(using Context) = + sym.is(Flags.Module, butNot = Flags.Package) && sym.isStatic + + def isConcreteClass(cls: ClassSymbol)(using Context) = + val instantiable: Boolean = + cls.is(Flags.Module) || + !cls.isOneOf(Flags.AbstractOrTrait) && { + // see `Checking.checkInstantiable` in typer + val tp = cls.appliedRef + val stp = SkolemType(tp) + val selfType = cls.givenSelfType.asSeenFrom(stp, cls) + !selfType.exists || stp <:< selfType + } + + // A concrete class may not be instantiated if the self type is not satisfied + instantiable && cls.enclosingPackageClass != defn.StdLibPatchesPackage.moduleClass From 1864f4f8b1cd1f5cc39138781a66ad2f50b33ddd Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Mon, 16 Jan 2023 23:58:42 +0100 Subject: [PATCH 042/657] Fix pickling test --- compiler/src/dotty/tools/dotc/transform/init/Semantic.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 4aaf0fa9ba72..4b7de601c053 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -242,8 +242,6 @@ object Semantic: * that could be reused to check other classes. We employ this trick to * improve performance of the analysis. */ - private type Config = Value - private type Res = Value /** The heap for abstract objects * @@ -257,14 +255,14 @@ object Semantic: private type Heap = Map[Ref, Objekt] - class Data extends Cache[Config, Res]: + class Data extends Cache[Value, Value]: /** Global cached values for expressions * * The values are only added when a fixed point is reached. * * It is intended to improve performance for computation related to warm values. */ - private var stable: ExprValueCache[Config, Res] = Map.empty + private var stable: ExprValueCache[Value, Value] = Map.empty /** Abstract heap stores abstract objects * From fcc87911c1dc85b01547b832c5f2b451c9348eb0 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Wed, 25 Jan 2023 08:52:14 +0100 Subject: [PATCH 043/657] Address review --- .../tools/dotc/transform/init/Cache.scala | 71 ++++++++++++++++--- .../tools/dotc/transform/init/Semantic.scala | 2 +- .../tools/dotc/transform/init/Trace.scala | 6 ++ 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala index 5ee651c2ed02..b4d378aed4ec 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala @@ -8,6 +8,51 @@ import Contexts.* import ast.tpd import tpd.Tree +/** The co-inductive cache used for analysis + * + * The cache contains two maps from `(Config, Tree)` to `Res`: + * + * - input cache (`this.last`) + * - output cache (`this.current`) + * + * The two caches are required because we want to make sure in a new iteration, + * an expression is evaluated exactly once. The monotonicity of the analysis + * ensures that the cache state goes up the lattice of the abstract domain, + * consequently the algorithm terminates. + * + * The general skeleton for usage of the cache is as follows + * + * def analysis() = { + * def iterate(entryExp: Expr)(using Cache) = + * eval(entryExp, initConfig) + * if cache.hasChanged && noErrors then + * cache.last = cache.current + * cache.current = Empty + * cache.changed = false + * iterate(outputCache, emptyCache) + * else + * reportErrors + * + * + * def eval(exp: Exp, config: Config)(using Cache) = + * cache.cachedEval(config, expr) { + * // Actual recursive evaluation of expression. + * // + * // Only executed if the entry `(exp, config)` is not in the output cache. + * } + * + * iterate(entryExp)(using new Cache) + * } + * + * See the documentation for the method `Cache.cachedEval` for more information. + * + * What goes to the configuration (`Config`) and what goes to the result (`Res`) + * need to be decided by the specific analysis and justified by reasoning about + * soundness. + * + * @param Config The analysis state that matters for evaluating an expression. + * @param Res The result from the evaluation the given expression. + */ class Cache[Config, Res]: import Cache.* @@ -36,28 +81,36 @@ class Cache[Config, Res]: def get(config: Config, expr: Tree): Option[Res] = current.get(config, expr) - /** Copy the value (config, expr)` from the last cache to the current cache + /** Evaluate an expression with cache * - * It assumes `default` if it doesn't exist in the last cache. + * The algorithmic skeleton is as follows: * - * It updates the current caches if the values change. + * if this.current.contains(config, expr) then + * return cached value + * else + * val assumed = this.last(config, expr) or bottom value if absent + * this.current(config, expr) = assumed + * val actual = eval(exp) + * + * if assumed != actual then + * this.changed = true + * this.current(config, expr) = actual * - * The two caches are required because we want to make sure in a new iteration, an expression is evaluated once. */ - def cachedEval(config: Config, expr: Tree, cacheResult: Boolean, default: Res)(eval: => Res): Res = + def cachedEval(config: Config, expr: Tree, cacheResult: Boolean, default: Res)(eval: Tree => Res): Res = this.get(config, expr) match case Some(value) => value case None => val assumeValue: Res = - last.get(config, expr) match + this.last.get(config, expr) match case Some(value) => value case None => - this.last = last.updatedNested(config, expr, default) + this.last = this.last.updatedNested(config, expr, default) default - this.current = current.updatedNested(config, expr, assumeValue) + this.current = this.current.updatedNested(config, expr, assumeValue) - val actual = eval + val actual = eval(expr) if actual != assumeValue then // println("Changed! from = " + assumeValue + ", to = " + actual) this.changed = true diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 4b7de601c053..286e3a124d12 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -1168,7 +1168,7 @@ object Semantic: * @param cacheResult It is used to reduce the size of the cache. */ def eval(expr: Tree, thisV: Ref, klass: ClassSymbol, cacheResult: Boolean = false): Contextual[Value] = log("evaluating " + expr.show + ", this = " + thisV.show + " in " + klass.show, printer, (_: Value).show) { - cache.cachedEval(thisV, expr, cacheResult, default = Hot) { cases(expr, thisV, klass) } + cache.cachedEval(thisV, expr, cacheResult, default = Hot) { expr => cases(expr, thisV, klass) } } /** Evaluate a list of expressions */ diff --git a/compiler/src/dotty/tools/dotc/transform/init/Trace.scala b/compiler/src/dotty/tools/dotc/transform/init/Trace.scala index 048a3d318646..7dfbc0b6cfa5 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Trace.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Trace.scala @@ -11,6 +11,12 @@ import Decorators._, printing.SyntaxHighlighting import scala.collection.mutable +/** Logic related to evaluation trace for showing friendly error messages + * + * A trace is a sequence of program positions which tells the evaluation order + * that leads to an error. It is usually more informative than the stack trace + * by tracking the exact sub-expression in the trace instead of only methods. + */ object Trace: opaque type Trace = Vector[Tree] From 464fa5d56933d6875302653e738435a125b6fcc4 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Wed, 25 Jan 2023 09:23:41 +0100 Subject: [PATCH 044/657] Fix typo in comment --- compiler/src/dotty/tools/dotc/transform/init/Cache.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala index b4d378aed4ec..14a52d995131 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala @@ -22,19 +22,19 @@ import tpd.Tree * * The general skeleton for usage of the cache is as follows * - * def analysis() = { + * def analysis(entryExp: Expr) = { * def iterate(entryExp: Expr)(using Cache) = * eval(entryExp, initConfig) * if cache.hasChanged && noErrors then * cache.last = cache.current * cache.current = Empty * cache.changed = false - * iterate(outputCache, emptyCache) + * iterate(entryExp) * else * reportErrors * * - * def eval(exp: Exp, config: Config)(using Cache) = + * def eval(expr: Expr, config: Config)(using Cache) = * cache.cachedEval(config, expr) { * // Actual recursive evaluation of expression. * // From e3ed95e90bd9a3144ad0ed54a8e306a25c0fb506 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Tue, 24 Jan 2023 23:22:54 +0100 Subject: [PATCH 045/657] Add default scaladoc settings to scaladoc artifact publishing --- project/Build.scala | 9 +++++++-- project/ScaladocGeneration.scala | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 4b347db088a9..473624df0fa2 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -843,6 +843,7 @@ object Build { "-sourcepath", (Compile / sourceDirectories).value.map(_.getAbsolutePath).distinct.mkString(File.pathSeparator), "-Yexplicit-nulls", ), + (Compile / doc / scalacOptions) ++= ScaladocConfigs.DefaultGenerationSettings.value.settings ) lazy val `scala3-library` = project.in(file("library")).asDottyLibrary(NonBootstrapped) @@ -1892,8 +1893,7 @@ object ScaladocConfigs { ) } - lazy val DefaultGenerationConfig = Def.task { - def distLocation = (dist / pack).value + lazy val DefaultGenerationSettings = Def.task { def projectVersion = version.value def socialLinks = SocialLinks(List( "github::https://github.com/lampepfl/dotty", @@ -1934,6 +1934,11 @@ object ScaladocConfigs { ) } + lazy val DefaultGenerationConfig = Def.task { + def distLocation = (dist / pack).value + DefaultGenerationSettings.value + } + lazy val Scaladoc = Def.task { DefaultGenerationConfig.value .add(UseJavacp(true)) diff --git a/project/ScaladocGeneration.scala b/project/ScaladocGeneration.scala index c6c4393c071f..fd972311da1d 100644 --- a/project/ScaladocGeneration.scala +++ b/project/ScaladocGeneration.scala @@ -141,6 +141,7 @@ object ScaladocGeneration { def remove[T <: Arg[_]: ClassTag]: GenerationConfig def withTargets(targets: Seq[String]): GenerationConfig def serialize: String + def settings: Seq[String] } object GenerationConfig { @@ -173,6 +174,9 @@ object ScaladocGeneration { ++ targets ).mkString(" ") + override def settings: Seq[String] = + args.map(_.serialize) ++ targets + private def argsWithout[T <: Arg[_]]( implicit tag: ClassTag[T] ): (Option[T], Seq[Arg[_]]) = args.foldLeft[(Option[T], Seq[Arg[_]])]((None, Seq.empty)) { From 031bae6b9918f90f33ca0ec12827fc819015302d Mon Sep 17 00:00:00 2001 From: Yadu Krishnan Date: Thu, 26 Jan 2023 20:39:40 +0100 Subject: [PATCH 046/657] Small correction in documentation I believe that the current doc is confusing a bit. Readers might get confused with the usage `single quote` in the doc with the `'` symbol. I also added an additional message to avoid confusion between that and the triple quote interpolation. --- docs/_docs/reference/changed-features/interpolation-escapes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/reference/changed-features/interpolation-escapes.md b/docs/_docs/reference/changed-features/interpolation-escapes.md index 594e7671c5ab..ed1cb89cce7a 100644 --- a/docs/_docs/reference/changed-features/interpolation-escapes.md +++ b/docs/_docs/reference/changed-features/interpolation-escapes.md @@ -4,7 +4,7 @@ title: "Escapes in interpolations" nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/interpolation-escapes.html --- -In Scala 2 there is no straightforward way to represent a single quote character `"` in a single quoted interpolation. A `\` character can't be used for that because interpolators themselves decide how to handle escaping, so the parser doesn't know whether the `"` character should be escaped or used as a terminator. +In Scala 2 there is no straightforward way to represent a double-quote character `"` in a quoted interpolation(except in triple-quote interpolation). A `\` character can't be used for that because interpolators themselves decide how to handle escaping, so the parser doesn't know whether the `"` character should be escaped or used as a terminator. In Scala 3, we can use the `$` meta character of interpolations to escape a `"` character. Example: From 113c5735925a1cc243cdd163654faff96ce0a367 Mon Sep 17 00:00:00 2001 From: Yadu Krishnan Date: Thu, 26 Jan 2023 20:47:19 +0100 Subject: [PATCH 047/657] update documentation message --- docs/_docs/reference/changed-features/interpolation-escapes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_docs/reference/changed-features/interpolation-escapes.md b/docs/_docs/reference/changed-features/interpolation-escapes.md index ed1cb89cce7a..4abeabdce3ac 100644 --- a/docs/_docs/reference/changed-features/interpolation-escapes.md +++ b/docs/_docs/reference/changed-features/interpolation-escapes.md @@ -4,7 +4,7 @@ title: "Escapes in interpolations" nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/interpolation-escapes.html --- -In Scala 2 there is no straightforward way to represent a double-quote character `"` in a quoted interpolation(except in triple-quote interpolation). A `\` character can't be used for that because interpolators themselves decide how to handle escaping, so the parser doesn't know whether the `"` character should be escaped or used as a terminator. +In Scala 2 there is no straightforward way to represent a double-quote character `"` in a quoted interpolation (except in triple-quote interpolation). A `\` character can't be used for that because interpolators themselves decide how to handle escaping, so the parser doesn't know whether the `"` character should be escaped or used as a terminator. In Scala 3, we can use the `$` meta character of interpolations to escape a `"` character. Example: From 859d30b0cd8a2de2510cab0d315ca731174b3fe6 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Thu, 26 Jan 2023 23:41:46 -0500 Subject: [PATCH 048/657] Find all implicits first --- .../src/dotty/tools/dotc/reporting/messages.scala | 12 +++++++++--- .../src/dotty/tools/dotc/typer/Implicits.scala | 14 ++++++++++++-- tests/neg/i16453.scala | 9 +++++++-- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index def04818255a..83209fb63c13 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2555,7 +2555,7 @@ class MissingImplicitArgument( where: String, paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, ignoredInstanceNormalImport: => Option[SearchSuccess], - ignoredConversions: => Iterable[TermRef] + ignoredConversions: => Iterable[(TermRef, Iterable[TermRef])] )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): arg.tpe match @@ -2744,12 +2744,18 @@ class MissingImplicitArgument( // show all available additional info def hiddenImplicitNote(s: SearchSuccess) = i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." - def noChainConversionsNote(ignoredConversions: Iterable[TermRef]): Option[String] = + def showImplicitAndConversions(imp: TermRef, convs: Iterable[TermRef]) = + i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" + def noChainConversionsNote(ignoredConversions: Iterable[(TermRef, Iterable[TermRef])]): Option[String] = { + val convsFormatted = ignoredConversions.map{ (imp, convs) => + i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" + }.mkString Option.when(ignoredConversions.nonEmpty)( i"\n\nNote: implicit conversions are not automatically applied to arguments of using clauses. " + i"You will have to pass the argument explicitly.\n" + - i"The following conversions in scope result in ${pt.show}: ${ignoredConversions.map(g => s"\n - ${g.symbol.showDcl}").mkString}" + i"The following conversions in scope result in ${pt.show}: $convsFormatted" ) + } super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) .orElse(noChainConversionsNote(ignoredConversions)) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f723a18eb241..928ac2ec999b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -923,11 +923,21 @@ trait Implicits: // example where searching for a nested type causes an infinite loop. None + def allImplicits(currImplicits: ContextualImplicits): List[ImplicitRef] = + if currImplicits.outerImplicits == null then currImplicits.refs + else currImplicits.refs ::: allImplicits(currImplicits.outerImplicits) + def ignoredConversions = arg.tpe match case fail: SearchFailureType => + // Get every implicit in scope and find Conversions for each if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then - ctx.implicits.eligible(ViewProto(WildcardType, wildApprox(fail.expectedType))) - .collect { case c if c.isConversion => c.ref } + // todo filter out implicit conversions + allImplicits(ctx.implicits).map { imp => + // todo imp.underlyingRef.underlying does not work for implicit functions or givens + // with type or implicit parameters + val convs = ctx.implicits.eligible(ViewProto(imp.underlyingRef.underlying, wildApprox(fail.expectedType))) + (imp.underlyingRef, convs.map(_.ref)) + }.filter(_._2.nonEmpty) else Nil diff --git a/tests/neg/i16453.scala b/tests/neg/i16453.scala index a143237711d9..0288c05938b7 100644 --- a/tests/neg/i16453.scala +++ b/tests/neg/i16453.scala @@ -1,7 +1,10 @@ import scala.language.implicitConversions +trait Foo { type T } + // Scala 3 style conversion -given [T]: Conversion[T, Option[T]] = ??? +// given [T]: Conversion[T, Option[T]] = ??? +given [F <: Foo](using f: F): Conversion[f.T, Option[f.T]] = ??? // Scala 2 style conversion implicit def toOption[T](t: T): Option[T] = Option(t) @@ -9,7 +12,9 @@ implicit def toOption[T](t: T): Option[T] = Option(t) given irrelevant: Conversion[Int, Option[Long]] = ??? def test() = { - given foo: Int = 0 + given foo: Foo with + type T = Int + given bar: Int = 0 summon[Option[Int]] // error implicitly[Option[Int]] // error From b5755deba79f04db5d846fcc24004e15973f04b0 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 27 Jan 2023 00:28:41 -0500 Subject: [PATCH 049/657] Use finalResultType --- .../src/dotty/tools/dotc/reporting/messages.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 83209fb63c13..69965ab4a10a 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2748,7 +2748,7 @@ class MissingImplicitArgument( i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" def noChainConversionsNote(ignoredConversions: Iterable[(TermRef, Iterable[TermRef])]): Option[String] = { val convsFormatted = ignoredConversions.map{ (imp, convs) => - i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" + s"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" }.mkString Option.when(ignoredConversions.nonEmpty)( i"\n\nNote: implicit conversions are not automatically applied to arguments of using clauses. " + diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 928ac2ec999b..c7d9a7c9ef89 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -935,8 +935,17 @@ trait Implicits: allImplicits(ctx.implicits).map { imp => // todo imp.underlyingRef.underlying does not work for implicit functions or givens // with type or implicit parameters - val convs = ctx.implicits.eligible(ViewProto(imp.underlyingRef.underlying, wildApprox(fail.expectedType))) - (imp.underlyingRef, convs.map(_.ref)) + val impRef = imp.underlyingRef + val impResultType = wildApprox(impRef.underlying.finalResultType) + val convs = ctx.implicits.eligible(ViewProto(impResultType, fail.expectedType)) + .filter { conv => + if !conv.isConversion then false + else + // Actually feed the summoned implicit into the Conversion to + // check if it works + true + } + (impRef, convs.map(_.ref)) }.filter(_._2.nonEmpty) else Nil From a3e87e7cbf433ee106aaa5adf40e453e10956a8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Fri, 27 Jan 2023 11:38:55 +0100 Subject: [PATCH 050/657] Set reference version to 3.3.0-RC2 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 4b347db088a9..3c37f83347af 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -80,7 +80,7 @@ object DottyJSPlugin extends AutoPlugin { object Build { import ScaladocConfigs._ - val referenceVersion = "3.3.0-RC1" + val referenceVersion = "3.3.0-RC2" val baseVersion = "3.3.1-RC1" From fd64c6a99087e9bba5ac9732f5780459c9650ca4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 27 Jan 2023 13:11:25 +0100 Subject: [PATCH 051/657] Run macro tests only on bootstrapped test --- .../dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala | 2 ++ compiler/test/dotty/tools/dotc/CompilationTests.scala | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala index e9d0e26f33b0..8c8f0079e868 100644 --- a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala @@ -130,6 +130,8 @@ class BootstrappedOnlyCompilationTests { compileFilesInDir("tests/run-custom-args/Yretain-trees", defaultOptions and "-Yretain-trees"), compileFilesInDir("tests/run-custom-args/Yread-comments", defaultOptions and "-Yread-docs"), compileFilesInDir("tests/run-custom-args/run-macros-erased", defaultOptions.and("-language:experimental.erasedDefinitions").and("-Xcheck-macros")), + compileDir("tests/run-custom-args/Xmacro-settings/simple", defaultOptions.and("-Xmacro-settings:one,two,three")), + compileDir("tests/run-custom-args/Xmacro-settings/compileTimeEnv", defaultOptions.and("-Xmacro-settings:a,b=1,c.b.a=x.y.z=1,myLogger.level=INFO")), ) }.checkRuns() diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index b8b38cce92e4..2fdebef3f5db 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -208,8 +208,6 @@ class CompilationTests { compileFile("tests/run-custom-args/defaults-serizaliable-no-forwarders.scala", defaultOptions and "-Xmixin-force-forwarders:false"), compileFilesInDir("tests/run-custom-args/erased", defaultOptions.and("-language:experimental.erasedDefinitions")), compileFilesInDir("tests/run-custom-args/fatal-warnings", defaultOptions.and("-Xfatal-warnings")), - compileDir("tests/run-custom-args/Xmacro-settings/simple", defaultOptions.and("-Xmacro-settings:one,two,three")), - compileDir("tests/run-custom-args/Xmacro-settings/compileTimeEnv", defaultOptions.and("-Xmacro-settings:a,b=1,c.b.a=x.y.z=1,myLogger.level=INFO")), compileFilesInDir("tests/run-custom-args/captures", allowDeepSubtypes.and("-language:experimental.captureChecking")), compileFilesInDir("tests/run-deep-subtype", allowDeepSubtypes), compileFilesInDir("tests/run", defaultOptions.and("-Ysafe-init")), From 9579aa56bd04704d63ee727be79634ff71313c4f Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Fri, 27 Jan 2023 23:38:55 -0500 Subject: [PATCH 052/657] Filter out Scala 2-style implicit conversions --- .../dotty/tools/dotc/typer/Implicits.scala | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c7d9a7c9ef89..32ab6518bce9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -927,26 +927,33 @@ trait Implicits: if currImplicits.outerImplicits == null then currImplicits.refs else currImplicits.refs ::: allImplicits(currImplicits.outerImplicits) + /** Ensure an implicit is not a Scala 2-style implicit conversion, based on its type */ + def notImplicitConv(typ: Type): Boolean = typ match { + case PolyType(_, resType) => notImplicitConv(resType) + case mt: MethodType => mt.isImplicitMethod || mt.isContextualMethod + case _ => true + } + def ignoredConversions = arg.tpe match case fail: SearchFailureType => // Get every implicit in scope and find Conversions for each if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then // todo filter out implicit conversions - allImplicits(ctx.implicits).map { imp => - // todo imp.underlyingRef.underlying does not work for implicit functions or givens - // with type or implicit parameters - val impRef = imp.underlyingRef - val impResultType = wildApprox(impRef.underlying.finalResultType) - val convs = ctx.implicits.eligible(ViewProto(impResultType, fail.expectedType)) - .filter { conv => - if !conv.isConversion then false - else - // Actually feed the summoned implicit into the Conversion to - // check if it works - true - } - (impRef, convs.map(_.ref)) - }.filter(_._2.nonEmpty) + allImplicits(ctx.implicits) + .filter(imp => notImplicitConv(imp.underlyingRef.underlying)) + .map { imp => + val impRef = imp.underlyingRef + val impResultType = wildApprox(impRef.underlying.finalResultType) + val convs = ctx.implicits.eligible(ViewProto(impResultType, fail.expectedType)) + .filter { conv => + if !conv.isConversion then false + else + // TODO Actually feed the summoned implicit into the Conversion to + // check if it works + true + } + (impRef, convs.map(_.ref)) + }.filter(_._2.nonEmpty) else Nil From ebb616c0e15b75c752badb855e9bb65176fd3125 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sat, 28 Jan 2023 21:17:55 -0500 Subject: [PATCH 053/657] Use typed directly instead of finding conversions --- .../dotty/tools/dotc/reporting/messages.scala | 19 ++++------ .../dotty/tools/dotc/typer/Implicits.scala | 38 +++++++++++-------- tests/neg/i16453.check | 20 ++++------ tests/neg/i16453.scala | 2 +- 4 files changed, 39 insertions(+), 40 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 69965ab4a10a..785f502a0f9c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2555,7 +2555,7 @@ class MissingImplicitArgument( where: String, paramSymWithMethodCallTree: Option[(Symbol, tpd.Tree)] = None, ignoredInstanceNormalImport: => Option[SearchSuccess], - ignoredConversions: => Iterable[(TermRef, Iterable[TermRef])] + ignoredConvertibleImplicits: => Iterable[TermRef] )(using Context) extends TypeMsg(MissingImplicitArgumentID), ShowMatchTrace(pt): arg.tpe match @@ -2746,19 +2746,16 @@ class MissingImplicitArgument( i"\n\nNote: ${s.ref.symbol.showLocated} was not considered because it was not imported with `import given`." def showImplicitAndConversions(imp: TermRef, convs: Iterable[TermRef]) = i"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" - def noChainConversionsNote(ignoredConversions: Iterable[(TermRef, Iterable[TermRef])]): Option[String] = { - val convsFormatted = ignoredConversions.map{ (imp, convs) => - s"\n- ${imp.symbol.showDcl}${convs.map(c => "\n - " + c.symbol.showDcl).mkString}" - }.mkString - Option.when(ignoredConversions.nonEmpty)( - i"\n\nNote: implicit conversions are not automatically applied to arguments of using clauses. " + - i"You will have to pass the argument explicitly.\n" + - i"The following conversions in scope result in ${pt.show}: $convsFormatted" + def noChainConversionsNote(ignoredConvertibleImplicits: Iterable[TermRef]): Option[String] = + Option.when(ignoredConvertibleImplicits.nonEmpty)( + i"\n\nNote: implicit conversions are not automatically applied to arguments of using clauses. " + + i"You will have to pass the argument explicitly.\n" + + i"The following implicits in scope can be converted to ${pt.show}:" + + ignoredConvertibleImplicits.map { imp => s"\n- ${imp.symbol.showDcl}"}.mkString ) - } super.msgPostscript ++ ignoredInstanceNormalImport.map(hiddenImplicitNote) - .orElse(noChainConversionsNote(ignoredConversions)) + .orElse(noChainConversionsNote(ignoredConvertibleImplicits)) .getOrElse(ctx.typer.importSuggestionAddendum(pt)) def explain(using Context) = "" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 32ab6518bce9..c016d469f5ac 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -934,26 +934,34 @@ trait Implicits: case _ => true } + def isScala2Conv(typ: Type): Boolean = typ match { + case PolyType(_, resType) => isScala2Conv(resType) + case mt: MethodType => + (!mt.resultType.isImplicitMethod && !mt.resultType.isContextualMethod) + || isScala2Conv(mt.resultType) + case _ => false + } + def ignoredConversions = arg.tpe match case fail: SearchFailureType => // Get every implicit in scope and find Conversions for each if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then - // todo filter out implicit conversions allImplicits(ctx.implicits) - .filter(imp => notImplicitConv(imp.underlyingRef.underlying)) - .map { imp => - val impRef = imp.underlyingRef - val impResultType = wildApprox(impRef.underlying.finalResultType) - val convs = ctx.implicits.eligible(ViewProto(impResultType, fail.expectedType)) - .filter { conv => - if !conv.isConversion then false - else - // TODO Actually feed the summoned implicit into the Conversion to - // check if it works - true - } - (impRef, convs.map(_.ref)) - }.filter(_._2.nonEmpty) + .map(_.underlyingRef) + .filter { imp => + if notImplicitConv(imp.underlying) then false + else + val locked = ctx.typerState.ownedVars + val tryCtx = ctx.fresh + val tried = Contexts.withMode(Mode.Printing) { + typed( + tpd.ref(imp).withSpan(arg.span), + fail.expectedType, + locked + ) + }(using tryCtx) + !tryCtx.reporter.hasErrors && fail.expectedType =:= tried.tpe + } else Nil diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index 46fea0f7c8c0..5ccd9b491a1e 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -1,18 +1,12 @@ --- [E172] Type Error: tests/neg/i16453.scala:14:21 --------------------------------------------------------------------- -14 | summon[Option[Int]] // error +-- [E172] Type Error: tests/neg/i16453.scala:19:21 --------------------------------------------------------------------- +19 | summon[Option[Int]] // error | ^ |No given instance of type Option[Int] was found for parameter x of method summon in object Predef | |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. - |The following conversions in scope result in Option[Int]: - | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] - | - implicit def toOption[T](t: T): Option[T] --- [E172] Type Error: tests/neg/i16453.scala:15:25 --------------------------------------------------------------------- -15 | implicitly[Option[Int]] // error + |The following conversions in scope can be converted to Option[Int]: + |- given bar: Int +-- [E172] Type Error: tests/neg/i16453.scala:20:25 --------------------------------------------------------------------- +20 | implicitly[Option[Int]] // error | ^ - |No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef - | - |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. - |The following conversions in scope result in Option[Int]: - | - final given def given_Conversion_T_Option[T]: Conversion[T, Option[T]] - | - implicit def toOption[T](t: T): Option[T] + | No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef diff --git a/tests/neg/i16453.scala b/tests/neg/i16453.scala index 0288c05938b7..517a22cc1270 100644 --- a/tests/neg/i16453.scala +++ b/tests/neg/i16453.scala @@ -3,7 +3,7 @@ import scala.language.implicitConversions trait Foo { type T } // Scala 3 style conversion -// given [T]: Conversion[T, Option[T]] = ??? +given [T]: Conversion[T, Option[T]] = ??? given [F <: Foo](using f: F): Conversion[f.T, Option[f.T]] = ??? // Scala 2 style conversion implicit def toOption[T](t: T): Option[T] = Option(t) From 98bf4b2b5f7dd634e3fa54b676b1171dd1969a1a Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Sun, 29 Jan 2023 06:39:45 +0000 Subject: [PATCH 054/657] Fix inversion of if, check for errors properly --- .../dotty/tools/dotc/reporting/messages.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 38 ++++++++------- tests/neg/i16453.check | 47 ++++++++++++++++--- tests/neg/i16453.scala | 34 ++++++++++---- 4 files changed, 86 insertions(+), 35 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 785f502a0f9c..f41d34b8c17c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2750,7 +2750,7 @@ class MissingImplicitArgument( Option.when(ignoredConvertibleImplicits.nonEmpty)( i"\n\nNote: implicit conversions are not automatically applied to arguments of using clauses. " + i"You will have to pass the argument explicitly.\n" + - i"The following implicits in scope can be converted to ${pt.show}:" + + i"The following implicits in scope can be implicitly converted to ${pt.show}:" + ignoredConvertibleImplicits.map { imp => s"\n- ${imp.symbol.showDcl}"}.mkString ) super.msgPostscript diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c016d469f5ac..1553053e9d2e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -928,44 +928,46 @@ trait Implicits: else currImplicits.refs ::: allImplicits(currImplicits.outerImplicits) /** Ensure an implicit is not a Scala 2-style implicit conversion, based on its type */ - def notImplicitConv(typ: Type): Boolean = typ match { - case PolyType(_, resType) => notImplicitConv(resType) - case mt: MethodType => mt.isImplicitMethod || mt.isContextualMethod - case _ => true - } - def isScala2Conv(typ: Type): Boolean = typ match { case PolyType(_, resType) => isScala2Conv(resType) - case mt: MethodType => - (!mt.resultType.isImplicitMethod && !mt.resultType.isContextualMethod) - || isScala2Conv(mt.resultType) + case mt: MethodType => !mt.isImplicitMethod && !mt.isContextualMethod case _ => false } - def ignoredConversions = arg.tpe match + def hasErrors(tree: Tree): Boolean = + if tree.tpe.isInstanceOf[ErrorType] then true + else + tree match { + case Apply(_, List(arg)) => arg.tpe.isInstanceOf[ErrorType] + case _ => false + } + + def ignoredConvertibleImplicits = arg.tpe match case fail: SearchFailureType => - // Get every implicit in scope and find Conversions for each if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then + // Get every implicit in scope and try to convert each allImplicits(ctx.implicits) + .distinctBy(_.underlyingRef.denot) + .view .map(_.underlyingRef) .filter { imp => - if notImplicitConv(imp.underlying) then false + if isScala2Conv(imp.underlying) || imp.symbol == defn.Predef_conforms then + false else - val locked = ctx.typerState.ownedVars - val tryCtx = ctx.fresh + // Using Mode.Printing will stop it from printing errors val tried = Contexts.withMode(Mode.Printing) { typed( tpd.ref(imp).withSpan(arg.span), fail.expectedType, - locked + ctx.typerState.ownedVars ) - }(using tryCtx) - !tryCtx.reporter.hasErrors && fail.expectedType =:= tried.tpe + } + !hasErrors(tried) && fail.expectedType =:= tried.tpe } else Nil - MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, ignoredConversions) + MissingImplicitArgument(arg, pt, where, paramSymWithMethodCallTree, ignoredInstanceNormalImport, ignoredConvertibleImplicits) } /** A string indicating the formal parameter corresponding to a missing argument */ diff --git a/tests/neg/i16453.check b/tests/neg/i16453.check index 5ccd9b491a1e..e01ddf5cab7a 100644 --- a/tests/neg/i16453.check +++ b/tests/neg/i16453.check @@ -1,12 +1,45 @@ --- [E172] Type Error: tests/neg/i16453.scala:19:21 --------------------------------------------------------------------- -19 | summon[Option[Int]] // error +-- [E172] Type Error: tests/neg/i16453.scala:21:19 --------------------------------------------------------------------- +21 | summon[List[Int]] // error + | ^ + | No given instance of type List[Int] was found for parameter x of method summon in object Predef +-- [E172] Type Error: tests/neg/i16453.scala:23:21 --------------------------------------------------------------------- +23 | summon[Option[Int]] // error | ^ |No given instance of type Option[Int] was found for parameter x of method summon in object Predef | |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. - |The following conversions in scope can be converted to Option[Int]: - |- given bar: Int --- [E172] Type Error: tests/neg/i16453.scala:20:25 --------------------------------------------------------------------- -20 | implicitly[Option[Int]] // error + |The following implicits in scope can be implicitly converted to Option[Int]: + |- final lazy given val baz3: Char + |- final lazy given val bar3: Int +-- [E172] Type Error: tests/neg/i16453.scala:24:26 --------------------------------------------------------------------- +24 | implicitly[Option[Char]] // error + | ^ + |No given instance of type Option[Char] was found for parameter e of method implicitly in object Predef + | + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. + |The following implicits in scope can be implicitly converted to Option[Char]: + |- final lazy given val baz3: Char +-- [E172] Type Error: tests/neg/i16453.scala:25:20 --------------------------------------------------------------------- +25 | implicitly[String] // error + | ^ + |No given instance of type String was found for parameter e of method implicitly in object Predef + | + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. + |The following implicits in scope can be implicitly converted to String: + |- final lazy given val baz3: Char +-- [E172] Type Error: tests/neg/i16453.scala:35:16 --------------------------------------------------------------------- +35 | summon[String] // error + | ^ + |No given instance of type String was found for parameter x of method summon in object Predef + | + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. + |The following implicits in scope can be implicitly converted to String: + |- implicit val baz2: Char +-- [E172] Type Error: tests/neg/i16453.scala:36:25 --------------------------------------------------------------------- +36 | implicitly[Option[Int]] // error | ^ - | No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef + |No given instance of type Option[Int] was found for parameter e of method implicitly in object Predef + | + |Note: implicit conversions are not automatically applied to arguments of using clauses. You will have to pass the argument explicitly. + |The following implicits in scope can be implicitly converted to Option[Int]: + |- implicit val bar2: Int diff --git a/tests/neg/i16453.scala b/tests/neg/i16453.scala index 517a22cc1270..00495c39e21a 100644 --- a/tests/neg/i16453.scala +++ b/tests/neg/i16453.scala @@ -2,20 +2,36 @@ import scala.language.implicitConversions trait Foo { type T } -// Scala 3 style conversion -given [T]: Conversion[T, Option[T]] = ??? -given [F <: Foo](using f: F): Conversion[f.T, Option[f.T]] = ??? -// Scala 2 style conversion -implicit def toOption[T](t: T): Option[T] = Option(t) - // This one is irrelevant, shouldn't be included in error message -given irrelevant: Conversion[Int, Option[Long]] = ??? +given irrelevant: Long = ??? + +/** Use Scala 3 givens/conversions */ +def testScala3() = { + given c1[T]: Conversion[T, Option[T]] = ??? + given c2[F <: Foo](using f: F): Conversion[f.T, Option[f.T]] = ??? + given Conversion[Char, String] = ??? + given Conversion[Char, Option[Int]] = ??? -def test() = { given foo: Foo with type T = Int - given bar: Int = 0 + given bar3: Int = 0 + given baz3: Char = 'a' + + // This should get the usual error + summon[List[Int]] // error summon[Option[Int]] // error + implicitly[Option[Char]] // error + implicitly[String] // error +} + +/** Use Scala 2 implicits */ +def testScala2() = { + implicit def toOpt[T](t: T): Option[T] = ??? + implicit def char2Str(c: Char): String = ??? + implicit val bar2: Int = 1 + implicit val baz2: Char = 'b' + + summon[String] // error implicitly[Option[Int]] // error } From 12a8051b7712327fe81c9bc062cb78c60e4e7ce2 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 29 Jan 2023 19:24:18 +0000 Subject: [PATCH 055/657] Infer: Don't minimise to Nothing if there's an upper bound --- .../src/dotty/tools/dotc/core/Types.scala | 2 +- .../dotty/tools/dotc/typer/Inferencing.scala | 2 +- .../tools/dotc/typer/InstantiateModel.scala | 57 +++++++++++++++++++ tests/neg/i15525.scala | 2 +- tests/pos/i14218.http4s.scala | 22 +++++++ tests/pos/i14218.scala | 15 +++++ 6 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 compiler/test/dotty/tools/dotc/typer/InstantiateModel.scala create mode 100644 tests/pos/i14218.http4s.scala create mode 100644 tests/pos/i14218.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 15b0b00ed0f3..d2fc225ff19d 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4781,7 +4781,7 @@ object Types { def hasLowerBound(using Context): Boolean = !currentEntry.loBound.isExactlyNothing /** For uninstantiated type variables: Is the upper bound different from Any? */ - def hasUpperBound(using Context): Boolean = !currentEntry.hiBound.isRef(defn.AnyClass) + def hasUpperBound(using Context): Boolean = !currentEntry.hiBound.finalResultType.isExactlyAny /** Unwrap to instance (if instantiated) or origin (if not), until result * is no longer a TypeVar diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index be74cb0fa2ec..af4174423cbc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -183,7 +183,7 @@ object Inferencing { // else hold off instantiating unbounded unconstrained variable else if direction != 0 then instantiate(tvar, fromBelow = direction < 0) - else if variance >= 0 && (force.ifBottom == IfBottom.ok || tvar.hasLowerBound) then + else if variance >= 0 && (force.ifBottom == IfBottom.ok && !tvar.hasUpperBound || tvar.hasLowerBound) then instantiate(tvar, fromBelow = true) else if variance >= 0 && force.ifBottom == IfBottom.fail then return false diff --git a/compiler/test/dotty/tools/dotc/typer/InstantiateModel.scala b/compiler/test/dotty/tools/dotc/typer/InstantiateModel.scala new file mode 100644 index 000000000000..b08062913dac --- /dev/null +++ b/compiler/test/dotty/tools/dotc/typer/InstantiateModel.scala @@ -0,0 +1,57 @@ +package dotty.tools +package dotc +package typer + +// Modelling the decision in IsFullyDefined +object InstantiateModel: + enum LB { case NN; case LL; case L1 }; import LB.* + enum UB { case AA; case UU; case U1 }; import UB.* + enum Var { case V; case NotV }; import Var.* + enum MSe { case M; case NotM }; import MSe.* + enum Bot { case Fail; case Ok; case Flip }; import Bot.* + enum Act { case Min; case Max; case ToMax; case Skip; case False }; import Act.* + + // NN/AA = Nothing/Any + // LL/UU = the original bounds, on the type parameter + // L1/U1 = the constrained bounds, on the type variable + // V = variance >= 0 ("non-contravariant") + // MSe = minimisedSelected + // Bot = IfBottom + // ToMax = delayed maximisation, via addition to toMaximize + // Skip = minimisedSelected "hold off instantiating" + // False = return false + + // there are 9 combinations: + // # | LB | UB | d | // d = direction + // --+----+----+---+ + // 1 | L1 | AA | - | L1 <: T + // 2 | L1 | UU | - | L1 <: T <: UU + // 3 | LL | U1 | + | LL <: T <: U1 + // 4 | NN | U1 | + | T <: U1 + // 5 | L1 | U1 | 0 | L1 <: T <: U1 + // 6 | LL | UU | 0 | LL <: T <: UU + // 7 | LL | AA | 0 | LL <: T + // 8 | NN | UU | 0 | T <: UU + // 9 | NN | AA | 0 | T + + def decide(lb: LB, ub: UB, v: Var, bot: Bot, m: MSe): Act = (lb, ub) match + case (L1, AA) => Min + case (L1, UU) => Min + case (LL, U1) => Max + case (NN, U1) => Max + + case (L1, U1) => if m==M || v==V then Min else ToMax + case (LL, UU) => if m==M || v==V then Min else ToMax + case (LL, AA) => if m==M || v==V then Min else ToMax + + case (NN, UU) => bot match + case _ if m==M => Max + //case Ok if v==V => Min // removed, i14218 fix + case Fail if v==V => False + case _ => ToMax + + case (NN, AA) => bot match + case _ if m==M => Skip + case Ok if v==V => Min + case Fail if v==V => False + case _ => ToMax diff --git a/tests/neg/i15525.scala b/tests/neg/i15525.scala index 0813d7c82435..b14c244b43c8 100644 --- a/tests/neg/i15525.scala +++ b/tests/neg/i15525.scala @@ -37,7 +37,7 @@ def element22( transmittable20.Type / transmittable21.Type } = ??? -def test22 = +def test22 = // error Resolution( element22( Resolution(element0), Resolution(element0), // error // error diff --git a/tests/pos/i14218.http4s.scala b/tests/pos/i14218.http4s.scala new file mode 100644 index 000000000000..774a5432177e --- /dev/null +++ b/tests/pos/i14218.http4s.scala @@ -0,0 +1,22 @@ +// A minimisation from http4s, +// which broke while implementing the fix for i14218. + +final class Bar[+F[_]] +object Bar: + def empty[F[_]]: Bar[F] = new Bar[Nothing] + +final class Foo[+F[_]] + +object Foo: + def apply[F[_]](bar: Bar[F] = Bar.empty): Foo[F] = new Foo + +class Test: + def test[F[_]]: Foo[F] = Foo[F]() + +//-- [E007] Type Mismatch Error +//12 | def test[F[_]]: Foo[F] = Foo[F]() +// | ^^^^^^ +// | Found: Bar[[_] =>> Any] +// | Required: Bar[F] +// | +// | where: F is a type in method t1 with bounds <: [_] =>> Any diff --git a/tests/pos/i14218.scala b/tests/pos/i14218.scala new file mode 100644 index 000000000000..e35aec94b3bd --- /dev/null +++ b/tests/pos/i14218.scala @@ -0,0 +1,15 @@ +class Pet +class Cat extends Pet + +class Z1[ S1 <: Pet](val fn: S1 => Unit) +class Z2[ S2 ](val fn: S2 => Unit) +class Z3[-S3 <: Pet](val fn: S3 => Unit) + +abstract class Test: + def test = + val r1 = new Z1((_: Pet) => ()); eat[Z1[Pet]](r1) // the case: using the parameter bound in situ infers Z[Nothing] + val r2 = new Z2((_: Pet) => ()); eat[Z2[Pet]](r2) // counter-example: infers as desired without an upper bound + val r3 = new Z3((_: Pet) => ()); eat[Z3[Pet]](r3) // workaround: declare it contravariant + val r4 = new Z1((_: Cat) => ()); eat[Z1[Cat]](r4) // counter-example: infers as desired with a subtype + + def eat[T](x: T): Unit From 942e184e72066093e768185bc9b5f3aa9c5f3805 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Mon, 30 Jan 2023 17:54:44 +0100 Subject: [PATCH 056/657] Don't allow SemanticDB write to output directory JAR. Writing to outputDirectory when it is a JAR can lead to producing malformed file, it is using FileOutputStream which works somehow correct, but if we would open the same JAR again in later phase (eg. genBCode) header of the file would be malformed: the result file grows in size respectively to provided input, but we can read only files written by SemanticDB phase. --- .../dotc/semanticdb/ExtractSemanticDB.scala | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index 071efb1fb91c..84bc1e77a1ef 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -24,6 +24,7 @@ import scala.annotation.{ threadUnsafe => tu, tailrec } import scala.PartialFunction.condOpt import dotty.tools.dotc.{semanticdb => s} +import dotty.tools.io.{AbstractFile, JarArchive} /** Extract symbol references and uses to semanticdb files. * See https://scalameta.org/docs/semanticdb/specification.html#symbol-1 @@ -38,7 +39,9 @@ class ExtractSemanticDB extends Phase: override val description: String = ExtractSemanticDB.description override def isRunnable(using Context) = - super.isRunnable && ctx.settings.Xsemanticdb.value + import ExtractSemanticDB.{semanticdbTarget, outputDirectory} + def writesToOutputJar = semanticdbTarget.isEmpty && outputDirectory.isInstanceOf[JarArchive] + super.isRunnable && ctx.settings.Xsemanticdb.value && !writesToOutputJar // Check not needed since it does not transform trees override def isCheckable: Boolean = false @@ -475,6 +478,13 @@ object ExtractSemanticDB: val name: String = "extractSemanticDB" val description: String = "extract info into .semanticdb files" + private def semanticdbTarget(using Context): Option[Path] = + Option(ctx.settings.semanticdbTarget.value) + .filterNot(_.isEmpty) + .map(Paths.get(_)) + + private def outputDirectory(using Context): AbstractFile = ctx.settings.outputDir.value + def write( source: SourceFile, occurrences: List[SymbolOccurrence], @@ -482,14 +492,8 @@ object ExtractSemanticDB: synthetics: List[Synthetic], )(using Context): Unit = def absolutePath(path: Path): Path = path.toAbsolutePath.normalize - val semanticdbTarget = - val semanticdbTargetSetting = ctx.settings.semanticdbTarget.value - absolutePath( - if semanticdbTargetSetting.isEmpty then ctx.settings.outputDir.value.jpath - else Paths.get(semanticdbTargetSetting) - ) val relPath = SourceFile.relativePath(source, ctx.settings.sourceroot.value) - val outpath = semanticdbTarget + val outpath = absolutePath(semanticdbTarget.getOrElse(outputDirectory.jpath)) .resolve("META-INF") .resolve("semanticdb") .resolve(relPath) From 2fafeaa7a5dbeb5f65bbbe9efb02d6b85f1e798b Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Mon, 30 Jan 2023 18:43:41 +0100 Subject: [PATCH 057/657] Remove not used integration with Open Community Build (#16751) This change removes integration with Open Community Build, triggering builds after publishing a new nightly version. This feature was not really used previously - nightly builds we're testing only 100 - 200 projects, and we relied on weekly builds (with over 1k projects) anyway. This change makes project secret `BUILD_TOKEN` obsolete. --- .github/workflows/ci.yaml | 39 ------------------- .../scripts/triggerUnmanagedCommunityBuild.sh | 37 ------------------ 2 files changed, 76 deletions(-) delete mode 100755 .github/workflows/scripts/triggerUnmanagedCommunityBuild.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 72e7149fa762..46a4e6064d07 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -585,45 +585,6 @@ jobs: external_repository: lampepfl/dotty-website publish_branch: gh-pages - nightly_unmanaged_community_build: - # Self-hosted runner is used only for getting current build version - runs-on: [self-hosted, Linux] - container: - image: lampepfl/dotty:2021-03-22 - options: --cpu-shares 4096 - volumes: - - ${{ github.workspace }}/../../cache/sbt:/root/.sbt - - ${{ github.workspace }}/../../cache/ivy:/root/.ivy2/cache - - ${{ github.workspace }}/../../cache/general:/root/.cache - needs: [publish_nightly] - if: "(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && github.repository == 'lampepfl/dotty'" - env: - NIGHTLYBUILD: yes - steps: - - name: Reset existing repo - run: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/lampepfl/dotty" && git reset --hard FETCH_HEAD || true - - - name: Checkout cleanup script - uses: actions/checkout@v3 - - - name: Cleanup - run: .github/workflows/cleanup.sh - - - name: Git Checkout - uses: actions/checkout@v3 - - - name: Add SBT proxy repositories - run: cp -vf .github/workflows/repositories /root/.sbt/ ; true - - - name: Get version string for this build - run: | - ver=$(./project/scripts/sbt "print scala3-compiler-bootstrapped/version" | tail -n1) - echo "This build version: $ver" - echo "THISBUILD_VERSION=$ver" >> $GITHUB_ENV - # Steps above are copy-pasted from publish_nightly, needed only to resolve THISBUILD_VERSION - - name: Trigger unmanaged community build - run: .github/workflows/scripts/triggerUnmanagedCommunityBuild.sh "${{ secrets.BUILD_TOKEN }}" "$THISBUILD_VERSION" - publish_release: permissions: contents: write # for actions/create-release to create a release diff --git a/.github/workflows/scripts/triggerUnmanagedCommunityBuild.sh b/.github/workflows/scripts/triggerUnmanagedCommunityBuild.sh deleted file mode 100755 index 694428e29bb5..000000000000 --- a/.github/workflows/scripts/triggerUnmanagedCommunityBuild.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -# This is script for triggering unamanged community build upon releasing nightly version. -# Script sends request to CB Jenkins instance to start the build for given released Scala version -# Prints url of created job to stdout -# -# Requirement: -# - the latest (nightly) version of scala should be published - -set -u - -if [ $# -ne 2 ]; then - echo "Wrong number of script arguments, expected , got $#: $@" - exit 1 -fi - -CB_ENDPOINT=https://scala3.westeurope.cloudapp.azure.com -CB_BUILD_TOKEN="$1" -SCALA_VERSION="$2" - -startRunResponse=$(curl "${CB_ENDPOINT}/job/runBuild/buildWithParameters?token=${CB_BUILD_TOKEN}&publishedScalaVersion=${SCALA_VERSION}" -v 2>&1) -echo "${startRunResponse}" -queueItem=$(echo "${startRunResponse}" | grep -oP "< Location: \K[\w\d:/.//]+") -# Wait until Jenkins does acknowledge the build (max 1 min ) -for i in {1..12}; do - buildUrl=$(curl -s "${queueItem}/api/json?tree=executable[url]" | jq .executable.url) - if [[ "null" == "${buildUrl}" ]]; then - echo "Waiting for build start..." - sleep 5 - else - echo "Created build url: ${buildUrl}" - exit 0 - fi -done - -# Set error if failed to resolve build url -exit 1 From 18a8a096e4f123e9375e779e487f4ac672dfbf87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wro=C5=84ski?= Date: Sat, 28 Jan 2023 12:47:07 +0100 Subject: [PATCH 058/657] Attach explanation message to diagnostic message --- sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java b/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java index 3e1e291ab7d1..25b934000144 100644 --- a/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java +++ b/sbt-bridge/src/dotty/tools/xsbt/DelegatingReporter.java @@ -39,13 +39,16 @@ public void doReport(Diagnostic dia, Context ctx) { StringBuilder rendered = new StringBuilder(); rendered.append(messageAndPos(dia, ctx)); Message message = dia.msg(); + StringBuilder messageBuilder = new StringBuilder(); + messageBuilder.append(message.message()); String diagnosticCode = String.valueOf(message.errorId().errorNumber()); boolean shouldExplain = Diagnostic.shouldExplain(dia, ctx); if (shouldExplain && !message.explanation().isEmpty()) { rendered.append(explanation(message, ctx)); + messageBuilder.append(System.lineSeparator()).append(explanation(message, ctx)); } - delegate.log(new Problem(position, message.message(), severity, rendered.toString(), diagnosticCode)); + delegate.log(new Problem(position, messageBuilder.toString(), severity, rendered.toString(), diagnosticCode)); } private static Severity severityOf(int level) { From dda91bba3387155bd40677c238722fe72ec597eb Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:11:46 -0500 Subject: [PATCH 059/657] Make code more readable --- .../dotty/tools/dotc/typer/Implicits.scala | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 1553053e9d2e..3ebe60e327ea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -927,42 +927,44 @@ trait Implicits: if currImplicits.outerImplicits == null then currImplicits.refs else currImplicits.refs ::: allImplicits(currImplicits.outerImplicits) - /** Ensure an implicit is not a Scala 2-style implicit conversion, based on its type */ - def isScala2Conv(typ: Type): Boolean = typ match { - case PolyType(_, resType) => isScala2Conv(resType) + /** Whether the given type is for an implicit def that's a Scala 2 implicit conversion */ + def isImplicitDefConversion(typ: Type): Boolean = typ match { + case PolyType(_, resType) => isImplicitDefConversion(resType) case mt: MethodType => !mt.isImplicitMethod && !mt.isContextualMethod case _ => false } - def hasErrors(tree: Tree): Boolean = - if tree.tpe.isInstanceOf[ErrorType] then true - else - tree match { + /** Whether a found implicit be converted to the desired type */ + def canBeConverted(ref: TermRef, expected: Type): Boolean = { + // Using Mode.Printing will stop it from printing errors + val tried = Contexts.withMode(Mode.Printing) { + typed( + tpd.ref(ref).withSpan(arg.span), + expected, + ctx.typerState.ownedVars + ) + } + val hasErrors = + if tried.tpe.isInstanceOf[ErrorType] then true + else tried match { case Apply(_, List(arg)) => arg.tpe.isInstanceOf[ErrorType] case _ => false } + !hasErrors && expected =:= tried.tpe + } def ignoredConvertibleImplicits = arg.tpe match case fail: SearchFailureType => if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then // Get every implicit in scope and try to convert each allImplicits(ctx.implicits) - .distinctBy(_.underlyingRef.denot) .view .map(_.underlyingRef) + .distinctBy(_.denot) .filter { imp => - if isScala2Conv(imp.underlying) || imp.symbol == defn.Predef_conforms then - false - else - // Using Mode.Printing will stop it from printing errors - val tried = Contexts.withMode(Mode.Printing) { - typed( - tpd.ref(imp).withSpan(arg.span), - fail.expectedType, - ctx.typerState.ownedVars - ) - } - !hasErrors(tried) && fail.expectedType =:= tried.tpe + !isImplicitDefConversion(imp.underlying) + && imp.symbol != defn.Predef_conforms + && canBeConverted(imp, fail.expectedType) } else Nil From 2584cf6aee10970a51c00f371980ff87e3aa204b Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Mon, 30 Jan 2023 21:12:53 -0500 Subject: [PATCH 060/657] Remove unnecessary check for Predef.conforms --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 3ebe60e327ea..5d97adea514e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -962,9 +962,7 @@ trait Implicits: .map(_.underlyingRef) .distinctBy(_.denot) .filter { imp => - !isImplicitDefConversion(imp.underlying) - && imp.symbol != defn.Predef_conforms - && canBeConverted(imp, fail.expectedType) + !isImplicitDefConversion(imp.underlying) && canBeConverted(imp, fail.expectedType) } else Nil From 95c9dfba8f8efbf578a1baad1740179cdfb14354 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Mon, 30 Jan 2023 22:48:48 -0500 Subject: [PATCH 061/657] Put check for Predef.conforms back --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 5d97adea514e..3ebe60e327ea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -962,7 +962,9 @@ trait Implicits: .map(_.underlyingRef) .distinctBy(_.denot) .filter { imp => - !isImplicitDefConversion(imp.underlying) && canBeConverted(imp, fail.expectedType) + !isImplicitDefConversion(imp.underlying) + && imp.symbol != defn.Predef_conforms + && canBeConverted(imp, fail.expectedType) } else Nil From 1a84513dae20ffb04b5426c79acfe1c2d7486bed Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 24 Jan 2023 16:13:22 +0100 Subject: [PATCH 062/657] Prepare bodies of inline forwarders eagerly Fixes #14131 Fixes #16469 --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 16 ++++++++++++---- tests/pos-macros/i14131.scala | 11 +++++++++++ tests/pos/i16469.scala | 15 +++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 tests/pos-macros/i14131.scala create mode 100644 tests/pos/i16469.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6cdd0150518b..d9c857279266 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1227,13 +1227,21 @@ class Namer { typer: Typer => case pt: MethodOrPoly => 1 + extensionParamsCount(pt.resType) case _ => 0 val ddef = tpd.DefDef(forwarder.asTerm, prefss => { + val forwarderCtx = ctx.withOwner(forwarder) val (pathRefss, methRefss) = prefss.splitAt(extensionParamsCount(path.tpe.widen)) val ref = path.appliedToArgss(pathRefss).select(sym.asTerm) - ref.appliedToArgss(adaptForwarderParams(Nil, sym.info, methRefss)) - .etaExpandCFT(using ctx.withOwner(forwarder)) + val rhs = ref.appliedToArgss(adaptForwarderParams(Nil, sym.info, methRefss)) + .etaExpandCFT(using forwarderCtx) + if forwarder.isInlineMethod then + // Eagerly make the body inlineable. `registerInlineInfo` does this lazily + // but it does not get evaluated during typer as the forwarder we are creating + // is already typed. + val inlinableRhs = PrepareInlineable.makeInlineable(rhs)(using forwarderCtx) + PrepareInlineable.registerInlineInfo(forwarder, inlinableRhs)(using forwarderCtx) + inlinableRhs + else + rhs }) - if forwarder.isInlineMethod then - PrepareInlineable.registerInlineInfo(forwarder, ddef.rhs) buf += ddef.withSpan(span) if hasDefaults then foreachDefaultGetterOf(sym.asTerm, diff --git a/tests/pos-macros/i14131.scala b/tests/pos-macros/i14131.scala new file mode 100644 index 000000000000..76c01839a17f --- /dev/null +++ b/tests/pos-macros/i14131.scala @@ -0,0 +1,11 @@ +class Dog: + inline given bark(using msg: String = "Woof!"): String = s"bark: $msg" + +class Wolf: + private val dog: Dog = Dog() + export dog.given + +def test = + val w = Wolf() + import w.given + summon[String] diff --git a/tests/pos/i16469.scala b/tests/pos/i16469.scala new file mode 100644 index 000000000000..1aaa381bb7e2 --- /dev/null +++ b/tests/pos/i16469.scala @@ -0,0 +1,15 @@ +class Context { + def normalMethod(): String = "normal" + inline def inlineMethod(): String = "inline" +} + +class Script(ctx: Context) { + export ctx.* + normalMethod() + inlineMethod() +} + +class MyScript(context: Context) extends Script(context) { + normalMethod() + inlineMethod() +} From 809f9fc91aade75b91c759aebe7d49335888f994 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Tue, 31 Jan 2023 12:25:48 -0500 Subject: [PATCH 063/657] Use viewExists --- .../dotty/tools/dotc/typer/Implicits.scala | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 3ebe60e327ea..08ec108a6d9a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -934,25 +934,6 @@ trait Implicits: case _ => false } - /** Whether a found implicit be converted to the desired type */ - def canBeConverted(ref: TermRef, expected: Type): Boolean = { - // Using Mode.Printing will stop it from printing errors - val tried = Contexts.withMode(Mode.Printing) { - typed( - tpd.ref(ref).withSpan(arg.span), - expected, - ctx.typerState.ownedVars - ) - } - val hasErrors = - if tried.tpe.isInstanceOf[ErrorType] then true - else tried match { - case Apply(_, List(arg)) => arg.tpe.isInstanceOf[ErrorType] - case _ => false - } - !hasErrors && expected =:= tried.tpe - } - def ignoredConvertibleImplicits = arg.tpe match case fail: SearchFailureType => if (fail.expectedType eq pt) || isFullyDefined(fail.expectedType, ForceDegree.none) then @@ -964,7 +945,7 @@ trait Implicits: .filter { imp => !isImplicitDefConversion(imp.underlying) && imp.symbol != defn.Predef_conforms - && canBeConverted(imp, fail.expectedType) + && viewExists(imp, fail.expectedType) } else Nil From 2dd2595b90f36433aec5c4a59a5f9db33efef736 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 1 Feb 2023 09:28:00 +0100 Subject: [PATCH 064/657] Add regression test Closes #16792 --- tests/pos/i16792.scala | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/pos/i16792.scala diff --git a/tests/pos/i16792.scala b/tests/pos/i16792.scala new file mode 100644 index 000000000000..5a44d8c09458 --- /dev/null +++ b/tests/pos/i16792.scala @@ -0,0 +1,2 @@ +val x = (1, 1) match + case _: (_ *: _) => () From 67a0e2c56b82e2b78d2478d870be60c29772f68d Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 1 Feb 2023 17:39:54 +0100 Subject: [PATCH 065/657] Fix static lazy field holder for GraalVM --- .../lazyvals/InitializedAccessInt.scala | 30 +++++++++++++++++++ .../lazyvals/InitializedObject.scala | 22 ++++++++++++++ .../tools/benchmarks/lazyvals/LazyVals.scala | 18 +++++++++++ .../tools/backend/jvm/BCodeSkelBuilder.scala | 2 +- .../backend/jvm/DottyBackendInterface.scala | 7 +++-- .../dotty/tools/dotc/transform/LazyVals.scala | 11 ++----- 6 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala create mode 100644 bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedObject.scala diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala new file mode 100644 index 000000000000..2a115ad63496 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessInt.scala @@ -0,0 +1,30 @@ +package dotty.tools.benchmarks.lazyvals + +import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.infra.Blackhole +import LazyVals.LazyIntHolder +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class InitializedAccessInt { + + var holder: LazyIntHolder = _ + + @Setup + def prepare: Unit = { + holder = new LazyIntHolder + holder.value + } + + @Benchmark + def measureInitialized(bh: Blackhole) = { + bh.consume(holder) + bh.consume(holder.value) + } +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedObject.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedObject.scala new file mode 100644 index 000000000000..672cc4bf6544 --- /dev/null +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedObject.scala @@ -0,0 +1,22 @@ +package dotty.tools.benchmarks.lazyvals + +import org.openjdk.jmh.annotations.* +import org.openjdk.jmh.infra.Blackhole +import LazyVals.ObjectHolder +import java.util.concurrent.TimeUnit + +@BenchmarkMode(Array(Mode.AverageTime)) +@Fork(2) +@Threads(1) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Benchmark) +class InitializedObject { + + @Benchmark + def measureInitialized(bh: Blackhole) = { + bh.consume(ObjectHolder) + bh.consume(ObjectHolder.value) + } +} diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala index 0afd93d086be..68379f9e142c 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala @@ -50,4 +50,22 @@ object LazyVals { } } } + + class LazyIntHolder { + lazy val value: Int = { + (System.nanoTime() % 1000).toInt + } + } + + object ObjectHolder { + lazy val value: String = { + System.nanoTime() % 5 match { + case 0 => "abc" + case 1 => "def" + case 2 => "ghi" + case 3 => "jkl" + case 4 => "mno" + } + } + } } diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index 1885210a6687..9c1ff1f26763 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -151,7 +151,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { // !!! Part of this logic is duplicated in JSCodeGen.genCompilationUnit claszSymbol.info.decls.foreach { f => - if f.isField && !f.name.is(LazyBitMapName) then + if f.isField && !f.name.is(LazyBitMapName) && !f.name.is(LazyLocalName) then f.setFlag(JavaStatic) } diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index ecdd0ae98803..f8f683a429f6 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -22,7 +22,7 @@ import dotty.tools.dotc.report import tpd._ import StdNames.nme -import NameKinds.LazyBitMapName +import NameKinds.{LazyBitMapName, LazyLocalName} import Names.Name class DottyBackendInterface(val outputDirectory: AbstractFile, val superCallsMap: ReadOnlyMap[Symbol, Set[ClassSymbol]])(using val ctx: Context) { @@ -129,10 +129,11 @@ object DottyBackendInterface { * the new lazy val encoding: https://github.com/lampepfl/dotty/issues/7140 */ def isStaticModuleField(using Context): Boolean = - sym.owner.isStaticModuleClass && sym.isField && !sym.name.is(LazyBitMapName) + sym.owner.isStaticModuleClass && sym.isField && !sym.name.is(LazyBitMapName) && !sym.name.is(LazyLocalName) def isStaticMember(using Context): Boolean = (sym ne NoSymbol) && - (sym.is(JavaStatic) || sym.isScalaStatic || sym.isStaticModuleField) + (sym.is(JavaStatic) || sym.isScalaStatic || sym.isStaticModuleField) + // guard against no sumbol cause this code is executed to select which call type(static\dynamic) to use to call array.clone /** diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index 0a9a2b1214b2..0861350c30a9 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -466,13 +466,9 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { val containerSymbol = newSymbol(claz, containerName, x.symbol.flags &~ containerFlagsMask | containerFlags | Private, defn.ObjectType, coord = x.symbol.coord).enteredAfter(this) containerSymbol.addAnnotation(Annotation(defn.VolatileAnnot, containerSymbol.span)) // private @volatile var _x: AnyRef containerSymbol.addAnnotations(x.symbol.annotations) // pass annotations from original definition - val stat = x.symbol.isStatic - if stat then - containerSymbol.setFlag(JavaStatic) + containerSymbol.removeAnnotation(defn.ScalaStaticAnnot) + containerSymbol.resetFlag(JavaStatic) val getOffset = - if stat then - Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getStaticFieldOffset) - else Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getOffsetStatic) val containerTree = ValDef(containerSymbol, nullLiteral) @@ -490,9 +486,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { val offset = ref(offsetSymbol.nn) val swapOver = - if stat then - tpd.clsOf(x.symbol.owner.typeRef) - else This(claz) val (accessorDef, initMethodDef) = mkThreadSafeDef(x, claz, containerSymbol, offset, swapOver) From 7bdfa4fa4242761c2ea5de888b0f49255d56c569 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 1 Feb 2023 18:19:19 +0100 Subject: [PATCH 066/657] No need to reset JavaStatic as its removed with the amsk --- compiler/src/dotty/tools/dotc/transform/LazyVals.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index 0861350c30a9..8d3702190763 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -467,7 +467,6 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { containerSymbol.addAnnotation(Annotation(defn.VolatileAnnot, containerSymbol.span)) // private @volatile var _x: AnyRef containerSymbol.addAnnotations(x.symbol.annotations) // pass annotations from original definition containerSymbol.removeAnnotation(defn.ScalaStaticAnnot) - containerSymbol.resetFlag(JavaStatic) val getOffset = Select(ref(defn.LazyValsModule), lazyNme.RLazyVals.getOffsetStatic) val containerTree = ValDef(containerSymbol, nullLiteral) From bd17ec592991eb8d2c7b5935b7deba21d3d0ee3a Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 1 Feb 2023 18:25:07 +0100 Subject: [PATCH 067/657] Removing getStaticFieldOffset as it's not used anymore --- compiler/src/dotty/tools/dotc/transform/LazyVals.scala | 1 - library/src/scala/runtime/LazyVals.scala | 8 -------- 2 files changed, 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index 8d3702190763..e4cb21a279d6 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -674,7 +674,6 @@ object LazyVals { val cas: TermName = N.cas.toTermName val getOffset: TermName = N.getOffset.toTermName val getOffsetStatic: TermName = "getOffsetStatic".toTermName - val getStaticFieldOffset: TermName = "getStaticFieldOffset".toTermName val getDeclaredField: TermName = "getDeclaredField".toTermName } val flag: TermName = "flag".toTermName diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index 5d1e8e74b89d..a75042671efa 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -142,14 +142,6 @@ object LazyVals { r } - def getStaticFieldOffset(field: java.lang.reflect.Field): Long = { - @nowarn - val r = unsafe.staticFieldOffset(field) - if (debug) - println(s"getStaticFieldOffset(${field.getDeclaringClass}, ${field.getName}) = $r") - r - } - def getOffsetStatic(field: java.lang.reflect.Field) = @nowarn val r = unsafe.objectFieldOffset(field) From 26567e7ce75347401e818f578523f77d56256998 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 2 Feb 2023 09:07:32 +0000 Subject: [PATCH 068/657] Close test: GADT constraint drives picking the right implicit --- tests/run/i16785.check | 1 + tests/run/i16785.scala | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/run/i16785.check create mode 100644 tests/run/i16785.scala diff --git a/tests/run/i16785.check b/tests/run/i16785.check new file mode 100644 index 000000000000..917d993cb822 --- /dev/null +++ b/tests/run/i16785.check @@ -0,0 +1 @@ +specific: 1 diff --git a/tests/run/i16785.scala b/tests/run/i16785.scala new file mode 100644 index 000000000000..a2217c99f6a4 --- /dev/null +++ b/tests/run/i16785.scala @@ -0,0 +1,14 @@ +object Test { + sealed trait Box[T] { def value: T } + final case class IntBox(value: Int) extends Box[Int] + + implicit def s1[T](implicit box: Box[T]): String = "generic: " + box.value + implicit def s2(implicit box: Box[Int]): String = "specific: " + box.value + + def test[T](implicit box: Box[T]): String = box match { + case IntBox(_) => implicitly[String] + } + + def main(args: Array[String]): Unit = + println(test(IntBox(1))) +} From 85e2844f8fce4a4b53d938e7fce5e0247cf66797 Mon Sep 17 00:00:00 2001 From: Vasil Vasilev Date: Thu, 2 Feb 2023 14:13:09 +0100 Subject: [PATCH 069/657] Add support for disabling redirected output in the REPL driver for usage in worksheets in the Scala Plugin for IntelliJ IDEA - Calling `setOut/setErr` in a concurrent environment without any synchronization (such as the Scala compile server in the Scala Plugin for IntelliJ IDEA, which is used to execute Scala 3 worksheets) can lead to unpredictable outcomes where the out/err streams are not restored properly after changing. - This change adds a new default method `redirectOutput` which can be overriden by others to control the redirecting behavior of the REPL driver. --- .../src/dotty/tools/repl/ReplDriver.scala | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index b072d58f6bb7..0f29591e2121 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -187,19 +187,23 @@ class ReplDriver(settings: Array[String], // TODO: i5069 final def bind(name: String, value: Any)(using state: State): State = state + protected def redirectOutput: Boolean = true + // redirecting the output allows us to test `println` in scripted tests private def withRedirectedOutput(op: => State): State = { - val savedOut = System.out - val savedErr = System.err - try { - System.setOut(out) - System.setErr(out) - op - } - finally { - System.setOut(savedOut) - System.setErr(savedErr) - } + if redirectOutput then + val savedOut = System.out + val savedErr = System.err + try { + System.setOut(out) + System.setErr(out) + op + } + finally { + System.setOut(savedOut) + System.setErr(savedErr) + } + else op } private def newRun(state: State, reporter: StoreReporter = newStoreReporter) = { From b9a02cdceb432b92eb9ba2082fec5b03c10f6cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 2 Feb 2023 15:02:50 +0100 Subject: [PATCH 070/657] Fix #12621: Scala.js: Better error message for JS trait ctor param. JS traits cannot have constructor params any more than they can have concrete term members. This improves the error message in that case. --- .../dotty/tools/dotc/transform/sjs/PrepJSInterop.scala | 3 +++ tests/neg-scalajs/js-trait-ctor-param.check | 4 ++++ tests/neg-scalajs/js-trait-ctor-param.scala | 9 +++++++++ 3 files changed, 16 insertions(+) create mode 100644 tests/neg-scalajs/js-trait-ctor-param.check create mode 100644 tests/neg-scalajs/js-trait-ctor-param.scala diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala index d934dc179989..8a430991e378 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala @@ -888,6 +888,9 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP report.error("A non-native JS trait cannot contain private members", tree) } else if (sym.is(Lazy)) { report.error("A non-native JS trait cannot contain lazy vals", tree) + } else if (sym.is(ParamAccessor)) { + // #12621 + report.error("A non-native JS trait cannot have constructor parameters", tree) } else if (!sym.is(Deferred)) { /* Tell the back-end not to emit this thing. In fact, this only * matters for mixed-in members created from this member. diff --git a/tests/neg-scalajs/js-trait-ctor-param.check b/tests/neg-scalajs/js-trait-ctor-param.check new file mode 100644 index 000000000000..bc5296b3c76f --- /dev/null +++ b/tests/neg-scalajs/js-trait-ctor-param.check @@ -0,0 +1,4 @@ +-- Error: tests/neg-scalajs/js-trait-ctor-param.scala:9:34 ------------------------------------------------------------- +9 |trait NonNativeBagHolderTrait(val bag: Bag) extends js.Any // error + | ^^^^^^^^^^^^ + | A non-native JS trait cannot have constructor parameters diff --git a/tests/neg-scalajs/js-trait-ctor-param.scala b/tests/neg-scalajs/js-trait-ctor-param.scala new file mode 100644 index 000000000000..c907b0d9b606 --- /dev/null +++ b/tests/neg-scalajs/js-trait-ctor-param.scala @@ -0,0 +1,9 @@ +import scala.scalajs.js +import scala.scalajs.js.annotation.* + +@js.native +trait Bag extends js.Any { + val str: String +} + +trait NonNativeBagHolderTrait(val bag: Bag) extends js.Any // error From 4c223479d0561c70ea08035ee962b2c1e4f99ef9 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 2 Feb 2023 16:45:19 +0100 Subject: [PATCH 071/657] Revert deletion of getStaticFieldOffset for now --- library/src/scala/runtime/LazyVals.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index a75042671efa..5d1e8e74b89d 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -142,6 +142,14 @@ object LazyVals { r } + def getStaticFieldOffset(field: java.lang.reflect.Field): Long = { + @nowarn + val r = unsafe.staticFieldOffset(field) + if (debug) + println(s"getStaticFieldOffset(${field.getDeclaringClass}, ${field.getName}) = $r") + r + } + def getOffsetStatic(field: java.lang.reflect.Field) = @nowarn val r = unsafe.objectFieldOffset(field) From df88c0f6cb2f07fa77fbdd8514312f2bdc7e81b4 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Sat, 5 Feb 2022 16:44:09 +0100 Subject: [PATCH 072/657] Document new changes The new syntax allows arbitrary interleaving of type and term clauses for methods TypelessClause{|s} is added for class constructors since they still don't allow interleaving of type parameters Also unifies the affected grammar variable names so that s means "one or more " Implicit clauses are moved apart from term clause to make it more obvious they can only be the last clause --- docs/_docs/internals/syntax.md | 36 +++--- .../reference/contextual/using-clauses.md | 4 +- .../generalized-method-syntax.md | 105 ++++++++++++++++++ docs/_docs/reference/syntax.md | 36 +++--- 4 files changed, 151 insertions(+), 30 deletions(-) create mode 100644 docs/_docs/reference/other-new-features/generalized-method-syntax.md diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 76664569bb17..dfdad496d446 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -347,9 +347,6 @@ ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeDef(Modifiers, name, tparams, bounds) id [HkTypeParamClause] TypeParamBounds Bound(below, above, context) -DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ -DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds - TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds @@ -363,13 +360,24 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -Param ::= id ‘:’ ParamType [‘=’ Expr] -DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] -DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause -UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’ -DefParams ::= DefParam {‘,’ DefParam} -DefParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. +DefParamClauses ::= DefParamClause { DefParamClause } +DefParamClause ::= DefTypeParamClause + | DefTermParamClause + | UsingParamClause +TypelessClauses ::= TypelessClause {TypelessClause} +TypelessClause ::= DefTermParamClause + | UsingParamClause + +DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ +DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds +DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ +UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’ +DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ + +DefTermParams ::= DefTermParam {‘,’ DefTermParam} +DefTermParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. +Param ::= id ‘:’ ParamType [‘=’ Expr] ``` ### Bindings and Imports @@ -420,7 +428,7 @@ Dcl ::= RefineDcl ValDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree) -DefSig ::= id [DefTypeParamClause] DefParamClauses +DefSig ::= id [DefParamClauses] [DefImplicitClause] TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds TypeDefTree(_, name, tparams, bound [‘=’ Type] @@ -431,8 +439,8 @@ Def ::= ‘val’ PatDef | TmplDef PatDef ::= ids [‘:’ Type] ‘=’ Expr | Pattern2 [‘:’ Type] ‘=’ Expr PatDef(_, pats, tpe?, expr) -DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefDef(_, name, tparams, vparamss, tpe, expr) - | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr DefDef(_, , Nil, vparamss, EmptyTree, expr | Block) +DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefDef(_, name, paramss, tpe, expr) + | ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr DefDef(_, , vparamss, EmptyTree, expr | Block) TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef @@ -444,10 +452,10 @@ ConstrMods ::= {Annotation} [AccessModifier] ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor EnumDef ::= id ClassConstr InheritClauses EnumBody GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) -GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present +GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody] Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} - ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods + ‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef | Export diff --git a/docs/_docs/reference/contextual/using-clauses.md b/docs/_docs/reference/contextual/using-clauses.md index 8f410ebc026e..61b80ee27d91 100644 --- a/docs/_docs/reference/contextual/using-clauses.md +++ b/docs/_docs/reference/contextual/using-clauses.md @@ -153,8 +153,8 @@ Here is the new syntax of parameters and arguments seen as a delta from the [sta ``` ClsParamClause ::= ... | UsingClsParamClause -DefParamClauses ::= ... | UsingParamClause +DefParamClause ::= ... | UsingParamClause UsingClsParamClause ::= ‘(’ ‘using’ (ClsParams | Types) ‘)’ -UsingParamClause ::= ‘(’ ‘using’ (DefParams | Types) ‘)’ +UsingParamClause ::= ‘(’ ‘using’ (DefTermParams | Types) ‘)’ ParArgumentExprs ::= ... | ‘(’ ‘using’ ExprsInParens ‘)’ ``` diff --git a/docs/_docs/reference/other-new-features/generalized-method-syntax.md b/docs/_docs/reference/other-new-features/generalized-method-syntax.md new file mode 100644 index 000000000000..dbe9c6eb0a61 --- /dev/null +++ b/docs/_docs/reference/other-new-features/generalized-method-syntax.md @@ -0,0 +1,105 @@ +--- +layout: doc-page +title: "Generalized Method Syntax" +movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/generalized-method-syntax.html +--- + +The inclusion of using clauses is not the only way in which methods have been updated, type parameter clauses are now allowed in any number and at any position. + +## Syntax Changes + +### In Scala 2 + +The old syntax only allowed zero or one type parameter clause, followed by any number of term clauses, optionnally followed by an implicit clause: + +```scala +def foo[T, U](x: T)(y: U)(z: Int, s: String)(a: Array[T])(implicit ordInt: Ord[Int], l: List[U]) +``` + +### In Scala 3 + +The new syntax allows any number of type, term and using clause, in any order, optionnaly followed by an implicit clause: +(do note however that [implicit clause are discouraged, in favor of using clauses](https://docs.scala-lang.org/scala3/reference/contextual/relationship-implicits.html)) + +```scala +def foo[T, U](x: T)(y: U)[V](z: V, s: String)[A](a: Array[A])(implicit List[U]) +``` + +### Unchanged + +Class definitions and type declarations are unaffected, there can only be up to one type clause, in leading posion. + +## Motivation + +The new syntax is a powerful but natural extension of the old one, it allows new design patterns while staying intuitive and legible. + +### Dependent Type Clauses + +As type clauses can come after term clauses, it is now possible to have type parameters that depend on term parameters: + +```scala +trait Key { type Value } +trait DB { + def get(k: Key): Option[k.Value] // dependent result type + def getOrElse(k: Key)[V >: k.Value](default: V): V // dependent type parameter +} +``` + +Note that simply replacing `V` by `k.Value` would not be equivalent. For example, if `k.Value` is `Some[Int]`, only the above allows: +`getOrElse(k)[Option[Int]](None)`, which returns a `Number`. + +### Partial Inference + +It is now possible to only infer some of the type parameters, this reduces boilerplate at the use site: +```scala +trait StaticSizeList[S <: Int & Singleton, T] +def filled[S <: Int & Singleton][T](x: T): StaticSizeList[S,T] = ??? +val helloes = filled[4]("Hello!") // S=4, and T is inferred +``` + +## Details + +### Application + +Method application is unchanged. +When multiple type clauses are expected but not all are passed, the rightmost ones are inferred. + +In particular, the following does not type check, even though the argument `Char` is only valid for `C`: +```scala +def triple[I <: Int](using Ordering[I])[C <: Char](a: I, b: C) = ??? +triple[Char](0, 'c') // error: Char does not conform to upperbound Int +``` + +### Extension Methods + +Extension methods follow the same syntax, for example the following is valid: +```scala +extension [T](l1: List[T]) + def zipWith[U](l2: List[U])[V](l3: List[V]): List[(T,U,V)] +``` + +### When to use + +We recommand to always put a unique type clause at the beginning, unless it is not possible to do so. +For example, the extension method `zipWith` above should be written `zipWith[U, V](l2: List[U], l3: List[V]): List[(T,U,V)]` instead. +On the other hand, the `getOrElse` method is recommended as-is, as it cannot be written with a leading type clause. + +### Formal syntax + +``` +DefDcl ::= DefSig ‘:’ Type +DefDef ::= DefSig [‘:’ Type] ‘=’ Expr +DefSig ::= id [DefParamClauses] [DefImplicitClause] +DefParamClauses ::= DefParamClause { DefParamClause } +DefParamClause ::= DefTypeParamClause + | DefTermParamClause + | UsingParamClause +DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ +DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds +DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ +UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’ +DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ +DefTermParams ::= DefTermParam {‘,’ DefTermParam} +DefTermParam ::= {Annotation} [‘inline’] Param +Param ::= id ‘:’ ParamType [‘=’ Expr] +``` diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 7e4b81b1ef5a..1d0c45030d2c 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -338,9 +338,6 @@ ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] id [HkTypeParamClause] TypeParamBounds -DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ -DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds - TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds @@ -352,13 +349,24 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -Param ::= id ‘:’ ParamType [‘=’ Expr] -DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] -DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause -UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’ -DefParams ::= DefParam {‘,’ DefParam} -DefParam ::= {Annotation} [‘inline’] Param +DefParamClauses ::= DefParamClause { DefParamClause } +DefParamClause ::= DefTypeParamClause + | DefTermParamClause + | UsingParamClause +TypelessClauses ::= TypelessClause {TypelessClause} +TypelessClause ::= DefTermParamClause + | UsingParamClause + +DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ +DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds +DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ +UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’ +DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ + +DefTermParams ::= DefTermParam {‘,’ DefTermParam} +DefTermParam ::= {Annotation} [‘inline’] Param +Param ::= id ‘:’ ParamType [‘=’ Expr] ``` ### Bindings and Imports @@ -409,8 +417,8 @@ Dcl ::= RefineDcl ValDcl ::= ids ‘:’ Type VarDcl ::= ids ‘:’ Type DefDcl ::= DefSig ‘:’ Type -DefSig ::= id [DefTypeParamClause] DefParamClauses -TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type] +DefSig ::= id [DefParamClauses] [DefImplicitClause] +TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds Def ::= ‘val’ PatDef | ‘var’ PatDef @@ -420,7 +428,7 @@ Def ::= ‘val’ PatDef PatDef ::= ids [‘:’ Type] ‘=’ Expr | Pattern2 [‘:’ Type] ‘=’ Expr DefDef ::= DefSig [‘:’ Type] ‘=’ Expr - | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr + | ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef @@ -432,10 +440,10 @@ ConstrMods ::= {Annotation} [AccessModifier] ObjectDef ::= id [Template] EnumDef ::= id ClassConstr InheritClauses EnumBody GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) -GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present +GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody] Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} - ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods + ‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef | Export From 897deaf35f4509b5acebd7a3a1ac87e8122f491c Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 26 Jan 2022 10:08:17 +0100 Subject: [PATCH 073/657] Update Parsers.scala to accomodate new syntax Interweaved methods still fail at use-cite, see next commit --- .../dotty/tools/dotc/parsing/Parsers.scala | 80 ++++++++++++++++--- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ff5e95f3aa03..99359ac06a37 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3079,6 +3079,61 @@ object Parsers { /* -------- PARAMETERS ------------------------------------------- */ + /** DefParamClause ::= DefTypeParamClause + * | DefTermParamClause + * | UsingParamClause + */ + def typeOrTermParamClause(nparams: Int, // number of parameters preceding this clause + ofClass: Boolean = false, // owner is a class + ofCaseClass: Boolean = false, // owner is a case class + prefix: Boolean = false, // clause precedes name of an extension method + givenOnly: Boolean = false, // only given parameters allowed + firstClause: Boolean = false, // clause is the first in regular list of clauses + ownerKind: ParamOwner + ): List[TypeDef] | List[ValDef] = + if (in.token == LPAREN) + paramClause(nparams, ofClass, ofCaseClass, prefix, givenOnly, firstClause) + else if (in.token == LBRACKET) + typeParamClause(ownerKind) + else + Nil + + end typeOrTermParamClause + + /** DefParamClauses ::= DefParamClause { DefParamClause } + */ + def typeOrTermParamClauses( + ownerKind: ParamOwner, + ofClass: Boolean = false, + ofCaseClass: Boolean = false, + givenOnly: Boolean = false, + numLeadParams: Int = 0 + ): List[List[TypeDef] | List[ValDef]] = + + def recur(firstClause: Boolean, nparams: Int): List[List[TypeDef] | List[ValDef]] = + newLineOptWhenFollowedBy(LPAREN) + newLineOptWhenFollowedBy(LBRACKET) + if in.token == LPAREN then + val paramsStart = in.offset + val params = paramClause( + nparams, + ofClass = ofClass, + ofCaseClass = ofCaseClass, + givenOnly = givenOnly, + firstClause = firstClause) + val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) + params :: ( + if lastClause then Nil + else recur(firstClause = false, nparams + params.length)) + else if in.token == LBRACKET then + typeParamClause(ownerKind) :: recur(firstClause, nparams) + else Nil + end recur + + recur(firstClause = true, nparams = numLeadParams) + end typeOrTermParamClauses + + /** ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ * ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] * id [HkTypeParamClause] TypeParamBounds @@ -3143,11 +3198,15 @@ object Parsers { * UsingClsParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ * ClsParams ::= ClsParam {‘,’ ClsParam} * ClsParam ::= {Annotation} + * + * TypelessClause ::= DefTermParamClause + * | UsingParamClause * - * DefParamClause ::= ‘(’ [‘erased’] DefParams ‘)’ | UsingParamClause - * UsingParamClause ::= ‘(’ ‘using’ [‘erased’] (DefParams | ContextTypes) ‘)’ - * DefParams ::= DefParam {‘,’ DefParam} - * DefParam ::= {Annotation} [‘inline’] Param + * DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ + * UsingParamClause ::= ‘(’ ‘using’ [‘erased’] (DefTermParams | ContextTypes) ‘)’ + * DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ + * DefTermParams ::= DefTermParam {‘,’ DefTermParam} + * DefTermParam ::= {Annotation} [‘inline’] Param * * Param ::= id `:' ParamType [`=' Expr] * @@ -3246,7 +3305,7 @@ object Parsers { } /** ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] - * DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] + * TypelessClauses ::= TypelessClause {TypelessClause} * * @return The parameter definitions */ @@ -3515,9 +3574,9 @@ object Parsers { } /** DefDef ::= DefSig [‘:’ Type] ‘=’ Expr - * | this ParamClause ParamClauses `=' ConstrExpr + * | this TypelessClauses [DefImplicitClause] `=' ConstrExpr * DefDcl ::= DefSig `:' Type - * DefSig ::= id [DefTypeParamClause] DefParamClauses + * DefSig ::= id [DefParamClauses] [DefImplicitClause] * | ExtParamClause [nl] [‘.’] id DefParamClauses */ def defDefOrDcl(start: Offset, mods: Modifiers, numLeadParams: Int = 0): DefDef = atSpan(start, nameStart) { @@ -3555,8 +3614,7 @@ object Parsers { val mods1 = addFlag(mods, Method) val ident = termIdent() var name = ident.name.asTermName - val tparams = typeParamClauseOpt(ParamOwner.Def) - val vparamss = paramClauses(numLeadParams = numLeadParams) + val paramss = typeOrTermParamClauses(ParamOwner.Def, numLeadParams = numLeadParams) var tpt = fromWithinReturnType { typedOpt() } if (migrateTo3) newLineOptWhenFollowedBy(LBRACE) val rhs = @@ -3574,7 +3632,7 @@ object Parsers { accept(EQUALS) expr() - val ddef = DefDef(name, joinParams(tparams, vparamss), tpt, rhs) + val ddef = DefDef(name, paramss, tpt, rhs) if (isBackquoted(ident)) ddef.pushAttachment(Backquoted, ()) finalizeDef(ddef, mods1, start) } @@ -3837,7 +3895,7 @@ object Parsers { finalizeDef(gdef, mods1, start) } - /** Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} ‘(’ DefParam ‘)’ + /** Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} ‘(’ DefTermParam ‘)’ * {UsingParamClause} ExtMethods */ def extension(): ExtMethods = From f1c5855d3e3c0af25f1323d10bea474b6e8d0c7a Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 26 Jan 2022 10:17:53 +0100 Subject: [PATCH 074/657] Fix assumptions in Applications.scala A check is removed that forbids interleaved methods at use-site This however breaks named type parameters, some tests are thus removed methType implicitly assumed there could only be one leading term parameter clause, this has been changed --- .../dotty/tools/dotc/typer/Applications.scala | 17 +++++++++++------ tests/neg/namedTypeParams.scala | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index cd33fe9cef24..19b96f00d365 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -444,10 +444,17 @@ trait Applications extends Compatibility { /** The function's type after widening and instantiating polytypes * with TypeParamRefs in constraint set */ - @threadUnsafe lazy val methType: Type = liftedFunType.widen match { - case funType: MethodType => funType - case funType: PolyType => instantiateWithTypeVars(funType) - case tp => tp //was: funType + @threadUnsafe lazy val methType: Type = { + def rec(t: Type): Type = { + t.widen match{ + case funType: MethodType => funType + case funType: PolyType => + rec(instantiateWithTypeVars(funType)) + case tp => tp + } + } + + rec(liftedFunType) } @threadUnsafe lazy val liftedFunType: Type = @@ -1144,8 +1151,6 @@ trait Applications extends Compatibility { val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_)) record("typedTypeApply") typedExpr(tree.fun, PolyProto(typedArgs, pt)) match { - case _: TypeApply if !ctx.isAfterTyper => - errorTree(tree, em"illegal repeated type application") case typedFn => typedFn.tpe.widen match { case pt: PolyType => diff --git a/tests/neg/namedTypeParams.scala b/tests/neg/namedTypeParams.scala index 8ed7c92241ea..197fc6a9a790 100644 --- a/tests/neg/namedTypeParams.scala +++ b/tests/neg/namedTypeParams.scala @@ -17,6 +17,7 @@ object Test { def f[X, Y](x: X, y: Y): Int = ??? f[X = Int, String](1, "") // error // error + /* Conflicts with Clause Interweaving, stems from named type parameters assuming one type clause f[X = Int][X = Int][Y = String](1, "") // error: illegal repeated type application f[X = Int][Y = String](1, "") // error: illegal repeated type application @@ -24,4 +25,5 @@ object Test { f[Y = String][X = Int](1, "") // error: illegal repeated type application f[Y = String][Int](1, "") // error: illegal repeated type application + */ } From b45d44dfd0ffe649cd51ef6d307e5f32c6e6d6b2 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 26 Jan 2022 10:19:36 +0100 Subject: [PATCH 075/657] Add interleaving tests --- tests/neg/interleaving-ab.scala | 8 +++++++ tests/neg/interleaving-nameCollision.scala | 3 +++ tests/neg/interleaving-params.scala | 7 ++++++ tests/neg/interleaving-typeApply.scala | 12 ++++++++++ tests/neg/interleaving-unmatched.scala | 3 +++ tests/neg/overrides.scala | 12 ++++++++++ tests/pos/interleaving-ba.scala | 11 ++++++++++ tests/pos/interleaving-chainedParams.scala | 18 +++++++++++++++ tests/pos/interleaving-classless.scala | 4 ++++ tests/pos/interleaving-functorCurrying.scala | 17 ++++++++++++++ .../interleaving-functorInterleaving.scala | 17 ++++++++++++++ tests/pos/interleaving-nameCollision.scala | 5 +++++ tests/pos/interleaving-newline.scala | 9 ++++++++ tests/pos/interleaving-overload.scala | 19 ++++++++++++++++ tests/pos/interleaving-params.scala | 6 +++++ tests/pos/interleaving-typeApply.scala | 22 +++++++++++++++++++ tests/pos/overrides.scala | 3 +++ 17 files changed, 176 insertions(+) create mode 100644 tests/neg/interleaving-ab.scala create mode 100644 tests/neg/interleaving-nameCollision.scala create mode 100644 tests/neg/interleaving-params.scala create mode 100644 tests/neg/interleaving-typeApply.scala create mode 100644 tests/neg/interleaving-unmatched.scala create mode 100644 tests/pos/interleaving-ba.scala create mode 100644 tests/pos/interleaving-chainedParams.scala create mode 100644 tests/pos/interleaving-classless.scala create mode 100644 tests/pos/interleaving-functorCurrying.scala create mode 100644 tests/pos/interleaving-functorInterleaving.scala create mode 100644 tests/pos/interleaving-nameCollision.scala create mode 100644 tests/pos/interleaving-newline.scala create mode 100644 tests/pos/interleaving-overload.scala create mode 100644 tests/pos/interleaving-params.scala create mode 100644 tests/pos/interleaving-typeApply.scala diff --git a/tests/neg/interleaving-ab.scala b/tests/neg/interleaving-ab.scala new file mode 100644 index 000000000000..0d6649bbb3be --- /dev/null +++ b/tests/neg/interleaving-ab.scala @@ -0,0 +1,8 @@ +object Ab: + given String = "" + given Double = 0 + + def ab[A][B](x: A)(using B): B = summon[B] + + def test = + ab[Int](0: Int) // error \ No newline at end of file diff --git a/tests/neg/interleaving-nameCollision.scala b/tests/neg/interleaving-nameCollision.scala new file mode 100644 index 000000000000..44aa1e169db4 --- /dev/null +++ b/tests/neg/interleaving-nameCollision.scala @@ -0,0 +1,3 @@ +object nameCollision: + def f[T](x: T)[U](y: U) = (x,y) + def f[T](x: T, y: T) = (x,y) // error \ No newline at end of file diff --git a/tests/neg/interleaving-params.scala b/tests/neg/interleaving-params.scala new file mode 100644 index 000000000000..e8fa5361a9ab --- /dev/null +++ b/tests/neg/interleaving-params.scala @@ -0,0 +1,7 @@ +class Params{ + def bar[T](x: T)[T]: String = ??? // error + def zoo(x: Int)[T, U](x: U): T = ??? // error + def bbb[T <: U](x: U)[U]: U = ??? // error // error + def f0[T](implicit x: T)[U](y: U) = (x,y) // error + def f1[T](implicit x: T)[U] = (x,y) // error +} \ No newline at end of file diff --git a/tests/neg/interleaving-typeApply.scala b/tests/neg/interleaving-typeApply.scala new file mode 100644 index 000000000000..2e5f652dc21a --- /dev/null +++ b/tests/neg/interleaving-typeApply.scala @@ -0,0 +1,12 @@ +object typeApply: + + def f3[T <: Int][U <: String](): T => T = ??? + def f5[T <: Int][U <: String]: [X <: Unit] => X => X = ??? + def f7[T <: Int][U <: String]()[X <: Unit]: X => X = ??? + + @main def test = { + f3[String]() // error + f5[Int][Unit] // error + f5[String][Unit] // error // error + f7[String]()[Unit] // error + } \ No newline at end of file diff --git a/tests/neg/interleaving-unmatched.scala b/tests/neg/interleaving-unmatched.scala new file mode 100644 index 000000000000..b2afcf5b5d45 --- /dev/null +++ b/tests/neg/interleaving-unmatched.scala @@ -0,0 +1,3 @@ +object unmatched: + def f1[T (x: T)] = ??? // error + def f2(x: Any[)T] = ??? // error // error \ No newline at end of file diff --git a/tests/neg/overrides.scala b/tests/neg/overrides.scala index 48f3260721e9..ee7a8b73ba0d 100644 --- a/tests/neg/overrides.scala +++ b/tests/neg/overrides.scala @@ -39,6 +39,7 @@ package p2 { // all being in the same package compiles fine class A[T] { def f(x: T)(y: T = x) = y + def b[U <: T](x: Int)[V >: T](y: String) = false def next: T = ??? @@ -49,11 +50,22 @@ class B extends A[Int] { def f(x: Int)(y: Int) = y // error: needs `override' modifier f(2)() + override def b[T <: Int](x: Int)(y: String) = true // error override def next(): Int = ??? // error: incompatible type } +class C extends A[String] { + + override def f(x: String) = x // error + + override def b[T <: String](x: Int)[U >: Int](y: String) = true // error: incompatible type + + override def next: Int = ??? // error: incompatible type + +} + class X { def f: A[Int] = ??? } diff --git a/tests/pos/interleaving-ba.scala b/tests/pos/interleaving-ba.scala new file mode 100644 index 000000000000..74c15d4e87e9 --- /dev/null +++ b/tests/pos/interleaving-ba.scala @@ -0,0 +1,11 @@ + + +object BA { + given String = "" + given Double = 0 + + def ba[B][A](x: A)(using B): B = summon[B] + + def test = ba[String](0) + +} \ No newline at end of file diff --git a/tests/pos/interleaving-chainedParams.scala b/tests/pos/interleaving-chainedParams.scala new file mode 100644 index 000000000000..6ed970a69458 --- /dev/null +++ b/tests/pos/interleaving-chainedParams.scala @@ -0,0 +1,18 @@ +object chainedParams{ + + trait Chain{ + type Tail <: Chain + } + + def f[C1 <: Chain](c1: C1)[C2 <: c1.Tail](c2: C2)[C3 <: c2.Tail](c3: C3): c3.Tail = ??? + + val self = new Chain{ type Tail = this.type } + val res: self.type = f(self)(self)(self) + + type C <: Chain + + val c3 = new Chain{ type Tail = C } + val c2 = new Chain{ type Tail = c3.type } + val c1 = new Chain{ type Tail = c2.type } + val u: C = f(c1)(c2)(c3) +} diff --git a/tests/pos/interleaving-classless.scala b/tests/pos/interleaving-classless.scala new file mode 100644 index 000000000000..4308030f66a6 --- /dev/null +++ b/tests/pos/interleaving-classless.scala @@ -0,0 +1,4 @@ +def f1[T][U](x: T, y: U): (T, U) = (x, y) +def f2[T](x: T)[U](y: U): (T, U) = (x, y) +def f3[T, U][V](x: T): U = ??? +def f4[T](x: T)[U <: x.type](y: U): (T, U) = (x, y) \ No newline at end of file diff --git a/tests/pos/interleaving-functorCurrying.scala b/tests/pos/interleaving-functorCurrying.scala new file mode 100644 index 000000000000..078d65233997 --- /dev/null +++ b/tests/pos/interleaving-functorCurrying.scala @@ -0,0 +1,17 @@ +object functorCurrying: + //taken from https://dotty.epfl.ch/docs/reference/contextual/type-classes.html + //at version 3.1.1-RC1-bin-20210930-01f040b-NIGHTLY + //modified to have type currying + trait Functor[F[_]]: + def map[A][B](x: F[A], f: A => B): F[B] + + + given Functor[List] with + def map[A][B](x: List[A], f: A => B): List[B] = + x.map(f) + + def assertTransformation[F[_]: Functor][A][B](expected: F[B], original: F[A], mapping: A => B): Unit = + assert(expected == summon[Functor[F]].map(original, mapping)) + + @main def testCurrying = + assertTransformation(List("a1", "b1"), List("a", "b"), elt => s"${elt}1") diff --git a/tests/pos/interleaving-functorInterleaving.scala b/tests/pos/interleaving-functorInterleaving.scala new file mode 100644 index 000000000000..fb14c151387c --- /dev/null +++ b/tests/pos/interleaving-functorInterleaving.scala @@ -0,0 +1,17 @@ +object functorInterweaving: + //taken from https://dotty.epfl.ch/docs/reference/contextual/type-classes.html + //at version 3.1.1-RC1-bin-20210930-01f040b-NIGHTLY + //modified to have type interveawing + trait Functor[F[_]]: + def map[A](x: F[A])[B](f: A => B): F[B] + + + given Functor[List] with + def map[A](x: List[A])[B](f: A => B): List[B] = + x.map(f) + + def assertTransformation[F[_]: Functor][A](original: F[A])[B](expected: F[B])(mapping: A => B): Unit = + assert(expected == summon[Functor[F]].map(original)(mapping)) + + @main def testInterweaving = + assertTransformation(List("a", "b"))(List("a1", "b1")){elt => s"${elt}1"} diff --git a/tests/pos/interleaving-nameCollision.scala b/tests/pos/interleaving-nameCollision.scala new file mode 100644 index 000000000000..b66f63b9f92c --- /dev/null +++ b/tests/pos/interleaving-nameCollision.scala @@ -0,0 +1,5 @@ +import scala.annotation.targetName + +object nameCollision: + def f[T](x: T)[U](y: U) = (x,y) + @targetName("g") def f[T](x: T, y: T) = (x,y) \ No newline at end of file diff --git a/tests/pos/interleaving-newline.scala b/tests/pos/interleaving-newline.scala new file mode 100644 index 000000000000..dbd30bca4ef7 --- /dev/null +++ b/tests/pos/interleaving-newline.scala @@ -0,0 +1,9 @@ +object newline { + def multipleLines + [T] + (x: T) + [U] + (using (T,U)) + (y: U) + = ??? +} diff --git a/tests/pos/interleaving-overload.scala b/tests/pos/interleaving-overload.scala new file mode 100644 index 000000000000..3dcb0a911704 --- /dev/null +++ b/tests/pos/interleaving-overload.scala @@ -0,0 +1,19 @@ + +class A{ + + def f1[T][U](x: Any) = ??? + def f1[T][U](x: Int) = ??? + + f1(1) + f1("hello") + + case class B[U](x: Int) + def b[U](x: Int) = B[U](x) + + def f2[T]: [U] => Int => B[U] = [U] => (x: Int) => b[U](x) + + f2(1) + f2[Any](1) + f2[Any][Any](1) + +} \ No newline at end of file diff --git a/tests/pos/interleaving-params.scala b/tests/pos/interleaving-params.scala new file mode 100644 index 000000000000..fb55d451b2bd --- /dev/null +++ b/tests/pos/interleaving-params.scala @@ -0,0 +1,6 @@ +class Params{ + type U + def foo[T](x: T)[U >: x.type <: T][L <: List[U]](l: L): L = ??? + def aaa(x: U): U = ??? + def bbb[T <: U](x: U)[U]: U = ??? +} \ No newline at end of file diff --git a/tests/pos/interleaving-typeApply.scala b/tests/pos/interleaving-typeApply.scala new file mode 100644 index 000000000000..b637d59e2239 --- /dev/null +++ b/tests/pos/interleaving-typeApply.scala @@ -0,0 +1,22 @@ +object typeApply: + + def f0[T]: [U] => T => T = ??? + def f1[T][U]: T => T = ??? + def f2[T][U](): T => T = ??? + def f3[T <: Int][U <: String](): T => T = ??? + def f4[T <: Int][U <: String]: T => T = ??? + def f5[T <: Int][U <: String]: [X <: Unit] => X => X = ??? + def f6[T <: Int][U <: String](): [X <: Unit] => X => X = ??? + def f7[T <: Int][U <: String]()[X <: Unit]: X => X = ??? + + @main def test = { + f0[Int][String] + f1[Int][String] + f2[Int][String]() + f3[Int][String]() + f4[Int][String] + f5[Int][String] + f5[Int][String][Unit] + f6[Int]()[Unit] + f7[Int]()[Unit] + } \ No newline at end of file diff --git a/tests/pos/overrides.scala b/tests/pos/overrides.scala index 97402f773082..0f5d12367ca2 100644 --- a/tests/pos/overrides.scala +++ b/tests/pos/overrides.scala @@ -1,6 +1,7 @@ class A[T] { def f(x: T)(y: T = x) = y + def b[U <: T](x: Int)[V >: T](y: String) = false } @@ -10,4 +11,6 @@ class B extends A[Int] { f(2)() + override def b[T <: Int](x: Int)[U >: Int](y: String) = true + } From 61fef6697ce97864d5fc07e16c3a4851ba8a2306 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 15:51:04 +0200 Subject: [PATCH 076/657] Generalise SymOps to more than one def type clause --- library/src/scala/quoted/Quotes.scala | 2 +- .../dotty/tools/scaladoc/tasty/SymOps.scala | 51 +++++++++---------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 48a387e64169..22ec107aeae8 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -3143,7 +3143,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Extension methods of `MethodType` */ trait MethodTypeMethods: extension (self: MethodType) - /** Is this the type of given parameter clause `(implicit X1, ..., Xn)`, `(given X1, ..., Xn)` or `(given x1: X1, ..., xn: Xn)` */ + /** Is this the type of using parameter clause `(implicit X1, ..., Xn)`, `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */ def isImplicit: Boolean def isErased: Boolean def param(idx: Int): TypeRepr diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index b4a1fc197d9a..584ef1f54267 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -143,7 +143,9 @@ object SymOps: import reflect._ sym.flags.is(Flags.Artifact) - def isLeftAssoc: Boolean = !sym.name.endsWith(":") + def isRightAssoc: Boolean = sym.name.endsWith(":") + + def isLeftAssoc: Boolean = !sym.isRightAssoc def extendedSymbol: Option[reflect.ValDef] = import reflect.* @@ -172,41 +174,36 @@ object SymOps: ) case _ => Nil -> Nil + def extendedParamLists: List[reflect.ParamClause] = sym.splitExtensionParamList._1 + + def extendedTypeParamLists: List[reflect.TypeParamClause] = + sym.extendedParamLists.collect { + case typeClause: reflect.TypeParamClause => typeClause + } + def extendedTypeParams: List[reflect.TypeDef] = - import reflect.* - sym.tree match - case tree: DefDef => - tree.leadingTypeParams - case _ => Nil + sym.extendedTypeParamLists.headOption.map(_.params).getOrElse(List()) def extendedTermParamLists: List[reflect.TermParamClause] = - import reflect.* - sym.splitExtensionParamList._1.collect { - case tpc: TermParamClause => tpc + sym.extendedParamLists.collect { + case tpc: reflect.TermParamClause => tpc } - def nonExtensionTermParamLists: List[reflect.TermParamClause] = - import reflect.* - if sym.nonExtensionLeadingTypeParams.nonEmpty then - sym.nonExtensionParamLists.dropWhile { - case _: TypeParamClause => false - case _ => true - }.drop(1).collect { - case tpc: TermParamClause => tpc - } - else - sym.nonExtensionParamLists.collect { - case tpc: TermParamClause => tpc - } - def nonExtensionParamLists: List[reflect.ParamClause] = sym.splitExtensionParamList._2 + def nonExtensionTermParamLists: List[reflect.TermParamClause] = + sym.nonExtensionParamLists.collect { + case tpc: reflect.TermParamClause => tpc + } + + def nonExtensionTypeParamLists: List[reflect.TypeParamClause] = + sym.nonExtensionParamLists.collect { + case typeClause: reflect.TypeParamClause => typeClause + } + def nonExtensionLeadingTypeParams: List[reflect.TypeDef] = - import reflect.* - sym.nonExtensionParamLists.collectFirst { - case TypeParamClause(params) => params - }.toList.flatten + sym.nonExtensionTypeParamLists.headOption.map(_.params).getOrElse(List()) end extension From 77c53c853ea5fa860dde0e74b18fa00454b6a9f7 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 16:06:39 +0200 Subject: [PATCH 077/657] Uniformize api's Parameter case classes --- scaladoc/src/dotty/tools/scaladoc/api.scala | 20 ++++++++++--------- .../scaladoc/tasty/ClassLikeSupport.scala | 8 ++++---- .../translators/ScalaSignatureUtils.scala | 4 ++-- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/api.scala b/scaladoc/src/dotty/tools/scaladoc/api.scala index 90a03658c90e..9c8f17f7a074 100644 --- a/scaladoc/src/dotty/tools/scaladoc/api.scala +++ b/scaladoc/src/dotty/tools/scaladoc/api.scala @@ -44,24 +44,24 @@ enum Modifier(val name: String, val prefix: Boolean): case Transparent extends Modifier("transparent", true) case Infix extends Modifier("infix", true) -case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList], signature: Signature, dri: DRI, position: Long) +case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[TermParameterList], signature: Signature, dri: DRI, position: Long) case class ImplicitConversion(from: DRI, to: DRI) trait ImplicitConversionProvider { def conversion: Option[ImplicitConversion] } trait Classlike: def typeParams: Seq[TypeParameter] = Seq.empty - def argsLists: Seq[ParametersList] = Seq.empty + def argsLists: Seq[TermParameterList] = Seq.empty enum Kind(val name: String): case RootPackage extends Kind("") case Package extends Kind("package") - case Class(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[ParametersList]) + case Class(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[TermParameterList]) extends Kind("class") with Classlike case Object extends Kind("object") with Classlike - case Trait(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[ParametersList]) + case Trait(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[TermParameterList]) extends Kind("trait") with Classlike - case Enum(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[ParametersList]) extends Kind("enum") with Classlike + case Enum(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[TermParameterList]) extends Kind("enum") with Classlike case EnumCase(kind: Object.type | Kind.Type | Val.type | Class) extends Kind("case") - case Def(typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList]) + case Def(typeParams: Seq[TypeParameter], argsLists: Seq[TermParameterList]) extends Kind("def") case Extension(on: ExtensionTarget, m: Kind.Def) extends Kind("def") case Constructor(base: Kind.Def) extends Kind("def") @@ -97,12 +97,12 @@ object Annotation: case class LinkParameter(name: Option[String] = None, dri: DRI, value: String) extends AnnotationParameter case class UnresolvedParameter(name: Option[String] = None, unresolvedText: String) extends AnnotationParameter -case class ParametersList( - parameters: Seq[Parameter], +case class TermParameterList( + parameters: Seq[TermParameter], modifiers: String ) -case class Parameter( +case class TermParameter( annotations: Seq[Annotation], modifiers: String, name: Option[String], @@ -112,6 +112,8 @@ case class Parameter( isGrouped: Boolean = false ) +type TypeParameterList = Seq[TypeParameter] + case class TypeParameter( annotations: Seq[Annotation], variance: "" | "+" | "-", diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 38cc90330265..8419e0275fa8 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -45,7 +45,7 @@ trait ClassLikeSupport: .filter(s => s.exists && !s.isHiddenByVisibility) .map( _.tree.asInstanceOf[DefDef]) constr.fold(Nil)( - _.termParamss.map(pList => ParametersList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) + _.termParamss.map(pList => TermParameterList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) ) if classDef.symbol.flags.is(Flags.Module) then Kind.Object @@ -146,7 +146,7 @@ trait ClassLikeSupport: memberInfo.paramLists(index) match case EvidenceOnlyParameterList => Nil case info: RegularParameterList => - Seq(ParametersList(paramList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(paramList.params))) + Seq(TermParameterList(paramList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(paramList.params))) } val target = ExtensionTarget( extSym.symbol.normalizedName, @@ -351,7 +351,7 @@ trait ClassLikeSupport: memberInfo.paramLists(index) match case EvidenceOnlyParameterList => Nil case info: RegularParameterList => - Seq(ParametersList(pList.params.map( + Seq(TermParameterList(pList.params.map( mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params) )) } @@ -404,7 +404,7 @@ trait ClassLikeSupport: val inlinePrefix = if argument.symbol.flags.is(Flags.Inline) then "inline " else "" val nameIfNotSynthetic = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName) val name = argument.symbol.normalizedName - Parameter( + TermParameter( argument.symbol.getAnnotations(), inlinePrefix + prefix(argument.symbol), nameIfNotSynthetic, diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala index acbfe87b5d25..4303cf6422b0 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala @@ -26,7 +26,7 @@ case class SignatureBuilder(content: Signature = Nil) extends ScalaSignatureUtil def annotationsBlock(d: Member): SignatureBuilder = d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation)} - def annotationsInline(d: Parameter): SignatureBuilder = + def annotationsInline(d: TermParameter): SignatureBuilder = d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) } def annotationsInline(t: TypeParameter): SignatureBuilder = @@ -78,7 +78,7 @@ case class SignatureBuilder(content: Signature = Nil) extends ScalaSignatureUtil bdr.annotationsInline(e).keyword(e.variance).tpe(e.name, Some(e.dri)).signature(e.signature) } - def functionParameters(params: Seq[ParametersList]) = + def functionParameters(params: Seq[TermParameterList]) = if params.isEmpty then this.plain("") else if params.size == 1 && params(0).parameters == Nil then this.plain("()") else this.list(params, separator = List(Plain(""))) { (bld, pList) => From 908e5c8a656ae399f313d557c0ef6594d5647348 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 16:21:29 +0200 Subject: [PATCH 078/657] Uniformize ScalaSignatureUtils param methods --- .../scaladoc/renderers/MemberRenderer.scala | 4 +-- .../tools/scaladoc/renderers/Resources.scala | 4 +-- .../translators/ScalaSignatureProvider.scala | 10 +++---- .../translators/ScalaSignatureUtils.scala | 30 +++++++++++-------- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index 5d5f3e9b20d5..f2501378bf72 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -409,10 +409,10 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext case (Some(on), members) => val typeSig = SignatureBuilder() .keyword("extension ") - .generics(on.typeParams) + .typeParamList(on.typeParams) .content val argsSig = SignatureBuilder() - .functionParameters(on.argsLists) + .functionTermParameters(on.argsLists) .content val sig = typeSig ++ Signature(Plain(s"(${on.name}: ")) ++ on.signature ++ Signature(Plain(")")) ++ argsSig MGroup(span(cls := "groupHeader")(sig.map(renderElement(_))), members.sortBy(_.name).toSeq, on.name) -> on.position diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala index bae43980a11d..43b4440bce2c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala @@ -180,10 +180,10 @@ trait Resources(using ctx: DocContext) extends Locations, Writer: case Kind.Extension(on, _) => val typeSig = SignatureBuilder() .keyword("extension ") - .generics(on.typeParams) + .typeParamList(on.typeParams) .content val argsSig = SignatureBuilder() - .functionParameters(on.argsLists) + .functionTermParameters(on.argsLists) .content flattenToText(typeSig ++ argsSig) case _ => "" diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala index 88561282afb0..02de12666666 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala @@ -58,8 +58,8 @@ class ScalaSignatureProvider: builder.kind(showKind), builder.name(member.name, member.dri), builder - .generics(kind.typeParams) - .functionParameters(kind.argsLists) + .typeParamList(kind.typeParams) + .functionTermParameters(kind.argsLists) .parentsSignature(member) ) @@ -106,8 +106,8 @@ class ScalaSignatureProvider: builder.kind(showKind), builder.name(method.name, method.dri), builder - .generics(kind.typeParams) - .functionParameters(kind.argsLists) + .typeParamList(kind.typeParams) + .functionTermParameters(kind.argsLists) .pipe { builder => instance.fold(builder)(i => builder.plain(": ").signature(i)) } @@ -151,7 +151,7 @@ class ScalaSignatureProvider: builder.modifiersAndVisibility(typeDef), builder.kind(tpe), builder.name(typeDef.name, typeDef.dri), - builder.generics(tpe.typeParams).pipe { bdr => + builder.typeParamList(tpe.typeParams).pipe { bdr => if (!tpe.opaque) { (if tpe.concreate then bdr.plain(" = ") else bdr) .signature(typeDef.signature) diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala index 4303cf6422b0..d28dd6ca18fe 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala @@ -74,21 +74,27 @@ case class SignatureBuilder(content: Signature = Nil) extends ScalaSignatureUtil def kind(k: Kind) = keyword(k.name + " ") - def generics(on: Seq[TypeParameter]) = list(on.toList, List(Plain("[")), List(Plain("]"))){ (bdr, e) => + + def functionParameters(paramss: Seq[ Either[TermParameterList,TypeParameterList] ]) = + this.list(paramss, separator = List(Plain(""))) { + case (bld, Left(params: TermParameterList)) => bld.termParamList(params) + case (bld, Right(params: TypeParameterList)) => bld.typeParamList(params) + } + + def termParamList(params: TermParameterList) = + this.list(params.parameters, prefix = List(Plain("("), Keyword(params.modifiers)), suffix = List(Plain(")")), forcePrefixAndSuffix = true) { (bld, p) => + val annotationsAndModifiers = bld.annotationsInline(p) + .keyword(p.modifiers) + val name = p.name.fold(annotationsAndModifiers)(annotationsAndModifiers.name(_, p.dri).plain(": ")) + name.signature(p.signature) + } + + def typeParamList(on: TypeParameterList) = list(on.toList, List(Plain("[")), List(Plain("]"))){ (bdr, e) => bdr.annotationsInline(e).keyword(e.variance).tpe(e.name, Some(e.dri)).signature(e.signature) } - def functionParameters(params: Seq[TermParameterList]) = - if params.isEmpty then this.plain("") - else if params.size == 1 && params(0).parameters == Nil then this.plain("()") - else this.list(params, separator = List(Plain(""))) { (bld, pList) => - bld.list(pList.parameters, prefix = List(Plain("("), Keyword(pList.modifiers)), suffix = List(Plain(")")), forcePrefixAndSuffix = true) { (bld, p) => - val annotationsAndModifiers = bld.annotationsInline(p) - .keyword(p.modifiers) - val name = p.name.fold(annotationsAndModifiers)(annotationsAndModifiers.name(_, p.dri).plain(": ")) - name.signature(p.signature) - } - } + def functionTermParameters(paramss: Seq[TermParameterList]) = + this.list(paramss, separator = List(Plain(""))) { (bld, pList) => bld.termParamList(pList) } trait ScalaSignatureUtils: extension (tokens: Seq[String]) def toSignatureString(): String = From a617c483fa7e0f8e100bd3b8f94faec2db9d62e4 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 16:33:31 +0200 Subject: [PATCH 079/657] Refactor Kind.Def to use one paramLists --- scaladoc/src/dotty/tools/scaladoc/api.scala | 2 +- .../src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala | 8 ++++---- .../ImplicitMembersExtensionTransformer.scala | 2 +- .../scaladoc/translators/ScalaSignatureProvider.scala | 3 +-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/api.scala b/scaladoc/src/dotty/tools/scaladoc/api.scala index 9c8f17f7a074..5af55f76a211 100644 --- a/scaladoc/src/dotty/tools/scaladoc/api.scala +++ b/scaladoc/src/dotty/tools/scaladoc/api.scala @@ -61,7 +61,7 @@ enum Kind(val name: String): extends Kind("trait") with Classlike case Enum(override val typeParams: Seq[TypeParameter], override val argsLists: Seq[TermParameterList]) extends Kind("enum") with Classlike case EnumCase(kind: Object.type | Kind.Type | Val.type | Class) extends Kind("case") - case Def(typeParams: Seq[TypeParameter], argsLists: Seq[TermParameterList]) + case Def(paramLists: Seq[Either[TermParameterList,TypeParameterList]]) extends Kind("def") case Extension(on: ExtensionTarget, m: Kind.Def) extends Kind("def") case Constructor(base: Kind.Def) extends Kind("def") diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 8419e0275fa8..a95d5f3e10f2 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -346,14 +346,14 @@ trait ClassLikeSupport: val memberInfo = unwrapMemberInfo(c, methodSymbol) val basicKind: Kind.Def = Kind.Def( - genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes, memberInfo.contextBounds)), + Right(genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes, memberInfo.contextBounds))) +: paramLists.zipWithIndex.flatMap { (pList, index) => memberInfo.paramLists(index) match - case EvidenceOnlyParameterList => Nil + case EvidenceOnlyParameterList => None case info: RegularParameterList => - Seq(TermParameterList(pList.params.map( + Some(Left(TermParameterList(pList.params.map( mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params) - )) + ))) } ) diff --git a/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala b/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala index 44eba3a39807..8ed7436bb11d 100644 --- a/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala @@ -29,7 +29,7 @@ class ImplicitMembersExtensionTransformer(using DocContext) extends(Module => Mo case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, _, _, MyDri, _), _), Origin.RegularlyDefined) => val kind = m.kind match case Kind.Extension(_, d) => d - case _ => Kind.Def(Nil, Nil) + case _ => Kind.Def(Nil) Seq(m.withOrigin(Origin.ExtensionFrom(source.name, source.dri)).withKind(kind)) case m @ Member(_, _, _, conversionProvider: ImplicitConversionProvider, Origin.RegularlyDefined) => diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala index 02de12666666..fd8dfc4f5b6c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala @@ -106,8 +106,7 @@ class ScalaSignatureProvider: builder.kind(showKind), builder.name(method.name, method.dri), builder - .typeParamList(kind.typeParams) - .functionTermParameters(kind.argsLists) + .functionParameters(kind.paramLists) .pipe { builder => instance.fold(builder)(i => builder.plain(": ").signature(i)) } From 2126c4645304b97f5a28896db21f3f3754b5e761 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 16:46:24 +0200 Subject: [PATCH 080/657] Refactor MemberInfo (breaks tests) This commit is only here for the sake of legibility, it breaks tests If you want to come back in the history, go to either the previous commit or the next --- .../scaladoc/tasty/ClassLikeSupport.scala | 62 ++++++++++++------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index a95d5f3e10f2..6a0201f5fb0b 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -12,6 +12,9 @@ import NameNormalizer._ import SyntheticsSupport._ import dotty.tools.dotc.core.NameKinds +// Please use this only for things defined in the api.scala file +import dotty.tools.{scaladoc => api} + trait ClassLikeSupport: self: TastyParser => import qctx.reflect._ @@ -45,7 +48,7 @@ trait ClassLikeSupport: .filter(s => s.exists && !s.isHiddenByVisibility) .map( _.tree.asInstanceOf[DefDef]) constr.fold(Nil)( - _.termParamss.map(pList => TermParameterList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) + _.termParamss.map(pList => api.TermParameterList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) ) if classDef.symbol.flags.is(Flags.Module) then Kind.Object @@ -142,11 +145,12 @@ trait ClassLikeSupport: dd.symbol.extendedSymbol.map { extSym => val memberInfo = unwrapMemberInfo(c, dd.symbol) val typeParams = dd.symbol.extendedTypeParams.map(mkTypeArgument(_, memberInfo.genericTypes)) - val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.flatMap { case (paramList, index) => - memberInfo.paramLists(index) match - case EvidenceOnlyParameterList => Nil - case info: RegularParameterList => - Seq(TermParameterList(paramList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(paramList.params))) + val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.flatMap { case (termParamList, index) => + memberInfo.termParamLists(index) match + case MemberInfo.EvidenceOnlyParameterList => None + case MemberInfo.RegularParameterList(info) => + Some(api.TermParameterList(termParamList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(termParamList.params))) + case _ => assert(false, "memberInfo.termParamLists contains a type parameter list !") } val target = ExtensionTarget( extSym.symbol.normalizedName, @@ -349,11 +353,12 @@ trait ClassLikeSupport: Right(genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes, memberInfo.contextBounds))) +: paramLists.zipWithIndex.flatMap { (pList, index) => memberInfo.paramLists(index) match - case EvidenceOnlyParameterList => None - case info: RegularParameterList => + case MemberInfo.EvidenceOnlyParameterList => None + case MemberInfo.RegularParameterList(info) => Some(Left(TermParameterList(pList.params.map( mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params) ))) + case _ => assert(false, "memberInfo.termParamLists contains a type parameter list !") } ) @@ -404,7 +409,7 @@ trait ClassLikeSupport: val inlinePrefix = if argument.symbol.flags.is(Flags.Inline) then "inline " else "" val nameIfNotSynthetic = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName) val name = argument.symbol.normalizedName - TermParameter( + api.TermParameter( argument.symbol.getAnnotations(), inlinePrefix + prefix(argument.symbol), nameIfNotSynthetic, @@ -514,16 +519,26 @@ trait ClassLikeSupport: experimental = experimental ) - object EvidenceOnlyParameterList - type RegularParameterList = Map[String, TypeRepr] - type ParameterList = RegularParameterList | EvidenceOnlyParameterList.type case class MemberInfo( - genericTypes: Map[String, TypeBounds], - paramLists: List[ParameterList], + paramLists: List[MemberInfo.ParameterList], res: TypeRepr, contextBounds: Map[String, DSignature] = Map.empty, - ) + ){ + val genericTypes: Map[String, TypeBounds] = paramLists.collect{ case MemberInfo.TypeParameterList(types) => types }.headOption.getOrElse(Map()) + + val termParamLists: List[MemberInfo.ParameterList] = paramLists.filter(_.isTerm) + } + + object MemberInfo: + enum ParameterList(val isTerm: Boolean, val isUsing: Boolean): + inline def isType = !isTerm + case EvidenceOnlyParameterList extends ParameterList(isTerm = true, isUsing = false) + case RegularParameterList(m: Map[String, TypeRepr])(isUsing: Boolean) extends ParameterList(isTerm = true, isUsing) + case TypeParameterList(m: Map[String, TypeBounds]) extends ParameterList(isTerm = false, isUsing = false) + + export ParameterList.{RegularParameterList, EvidenceOnlyParameterList, TypeParameterList} + def unwrapMemberInfo(c: ClassDef, symbol: Symbol): MemberInfo = @@ -541,10 +556,12 @@ trait ClassLikeSupport: symbol.paramSymss.flatten.find(_.name == name).exists(_.flags.is(Flags.Implicit)) def handlePolyType(memberInfo: MemberInfo, polyType: PolyType): MemberInfo = - MemberInfo(polyType.paramNames.zip(polyType.paramBounds).toMap, memberInfo.paramLists, polyType.resType) + val typeParamList = MemberInfo.TypeParameterList(polyType.paramNames.zip(polyType.paramBounds).toMap) + MemberInfo(memberInfo.paramLists :+ typeParamList, polyType.resType) def handleMethodType(memberInfo: MemberInfo, methodType: MethodType): MemberInfo = val rawParams = methodType.paramNames.zip(methodType.paramTypes).toMap + val isUsing = methodType.isImplicit val (evidences, notEvidences) = rawParams.partition(e => isSyntheticEvidence(e._1)) def findParamRefs(t: TypeRepr): Seq[ParamRef] = t match @@ -573,14 +590,15 @@ trait ClassLikeSupport: val newParams = notEvidences ++ paramsThatLookLikeContextBounds - val newLists: List[ParameterList] = if newParams.isEmpty && contextBounds.nonEmpty - then memberInfo.paramLists ++ Seq(EvidenceOnlyParameterList) - else memberInfo.paramLists ++ Seq(newParams) + val termParamList = if newParams.isEmpty && contextBounds.nonEmpty + then MemberInfo.EvidenceOnlyParameterList + else MemberInfo.RegularParameterList(newParams)(isUsing) + - MemberInfo(memberInfo.genericTypes, newLists , methodType.resType, contextBounds.toMap) + MemberInfo(memberInfo.paramLists :+ termParamList, methodType.resType, contextBounds.toMap) def handleByNameType(memberInfo: MemberInfo, byNameType: ByNameType): MemberInfo = - MemberInfo(memberInfo.genericTypes, memberInfo.paramLists, byNameType.underlying) + MemberInfo(memberInfo.paramLists, byNameType.underlying) def recursivelyCalculateMemberInfo(memberInfo: MemberInfo): MemberInfo = memberInfo.res match case p: PolyType => recursivelyCalculateMemberInfo(handlePolyType(memberInfo, p)) @@ -588,7 +606,7 @@ trait ClassLikeSupport: case b: ByNameType => handleByNameType(memberInfo, b) case _ => memberInfo - recursivelyCalculateMemberInfo(MemberInfo(Map.empty, List.empty, baseTypeRepr)) + recursivelyCalculateMemberInfo(MemberInfo(List.empty, baseTypeRepr)) private def paramListModifier(parameters: Seq[ValDef]): String = if parameters.size > 0 then From 9e8f91d28ebfc9c0589a468446c63b2e887f43de Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 23 Aug 2022 16:49:58 +0200 Subject: [PATCH 081/657] Add scaladoc support for interleaved clauses --- .../src/tests/extensionParams.scala | 5 ++ .../src/tests/methodsAndConstructors.scala | 3 + .../scaladoc/tasty/ClassLikeSupport.scala | 73 ++++++++++++------- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/scaladoc-testcases/src/tests/extensionParams.scala b/scaladoc-testcases/src/tests/extensionParams.scala index 7892676af2c4..58b95c5dac1f 100644 --- a/scaladoc-testcases/src/tests/extensionParams.scala +++ b/scaladoc-testcases/src/tests/extensionParams.scala @@ -52,3 +52,8 @@ extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Num extension (using String)(using Unit)(a: Animal)(using Int)(using Number) def f11(b: Any)(c: Any): Any = ??? + def f13(b: Any)[T](c: T): T + = ??? + def f14[D](b: D)[T](c: T): T + = ??? + diff --git a/scaladoc-testcases/src/tests/methodsAndConstructors.scala b/scaladoc-testcases/src/tests/methodsAndConstructors.scala index b8925c593b4c..b4c354d174c4 100644 --- a/scaladoc-testcases/src/tests/methodsAndConstructors.scala +++ b/scaladoc-testcases/src/tests/methodsAndConstructors.scala @@ -60,3 +60,6 @@ class Methods: def withImplicitParam2(v: String)(implicit ab: Double, a: Int, b: String): String = ??? + def clauseInterleaving[T](x: T)[U](y: U)(using (T, U)): (T, U) + = ??? + diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 6a0201f5fb0b..c7178acac312 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -339,45 +339,66 @@ trait ClassLikeSupport: def parseMethod( c: ClassDef, methodSymbol: Symbol, - emptyParamsList: Boolean = false, paramPrefix: Symbol => String = _ => "", specificKind: (Kind.Def => Kind) = identity ): Member = val method = methodSymbol.tree.asInstanceOf[DefDef] - val paramLists: List[TermParamClause] = methodSymbol.nonExtensionTermParamLists - val genericTypes: List[TypeDef] = if (methodSymbol.isClassConstructor) Nil else methodSymbol.nonExtensionLeadingTypeParams + val paramLists = methodSymbol.nonExtensionParamLists val memberInfo = unwrapMemberInfo(c, methodSymbol) - val basicKind: Kind.Def = Kind.Def( - Right(genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes, memberInfo.contextBounds))) +: - paramLists.zipWithIndex.flatMap { (pList, index) => - memberInfo.paramLists(index) match - case MemberInfo.EvidenceOnlyParameterList => None - case MemberInfo.RegularParameterList(info) => - Some(Left(TermParameterList(pList.params.map( + val unshuffledMemberInfoParamLists = + if methodSymbol.isExtensionMethod && methodSymbol.isRightAssoc then + // Taken from RefinedPrinter.scala + val (leadingTyParamss, rest1) = memberInfo.paramLists.span(_.isType) + val (leadingUsing, rest2) = rest1.span(_.isUsing) + val (rightTyParamss, rest3) = rest2.span(_.isType) + val (rightParamss, rest4) = rest3.splitAt(1) + val (leftParamss, rest5) = rest4.splitAt(1) + val (trailingUsing, rest6) = rest5.span(_.isUsing) + if leftParamss.nonEmpty then + // leadingTyParamss ::: leadingUsing ::: leftParamss ::: trailingUsing ::: rightTyParamss ::: rightParamss ::: rest6 + // because of takeRight after, this is equivalent to the following: + rightTyParamss ::: rightParamss ::: rest6 + else + memberInfo.paramLists // it wasn't a binary operator, after all. + else + memberInfo.paramLists + + val croppedUnshuffledMemberInfoParamLists = unshuffledMemberInfoParamLists.takeRight(paramLists.length) + + val basicDefKind: Kind.Def = Kind.Def( + paramLists.zip(croppedUnshuffledMemberInfoParamLists).flatMap{ + case (_: TermParamClause, MemberInfo.EvidenceOnlyParameterList) => Nil + case (pList: TermParamClause, MemberInfo.RegularParameterList(info)) => + Some(Left(api.TermParameterList(pList.params.map( mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params) ))) - case _ => assert(false, "memberInfo.termParamLists contains a type parameter list !") + case (TypeParamClause(genericTypeList), MemberInfo.TypeParameterList(memInfoTypes)) => + Some(Right(genericTypeList.map(mkTypeArgument(_, memInfoTypes, memberInfo.contextBounds)))) + case (_,_) => + assert(false, s"croppedUnshuffledMemberInfoParamLists and SymOps.nonExtensionParamLists disagree on whether this clause is a type or term one") } ) val methodKind = - if methodSymbol.isClassConstructor then Kind.Constructor(basicKind) - else if methodSymbol.flags.is(Flags.Implicit) then extractImplicitConversion(method.returnTpt.tpe) match - case Some(conversion) if paramLists.size == 0 || (paramLists.size == 1 && paramLists.head.params.size == 0) => - Kind.Implicit(basicKind, Some(conversion)) - case None if paramLists.size == 1 && paramLists(0).params.size == 1 => - Kind.Implicit(basicKind, Some( - ImplicitConversion( - paramLists(0).params(0).tpt.tpe.typeSymbol.dri, - method.returnTpt.tpe.typeSymbol.dri - ) - )) - case _ => - Kind.Implicit(basicKind, None) - else if methodSymbol.flags.is(Flags.Given) then Kind.Given(basicKind, Some(method.returnTpt.tpe.asSignature), extractImplicitConversion(method.returnTpt.tpe)) - else specificKind(basicKind) + if methodSymbol.isClassConstructor then Kind.Constructor(basicDefKind) + else if methodSymbol.flags.is(Flags.Implicit) then + val termParamLists: List[TermParamClause] = methodSymbol.nonExtensionTermParamLists + extractImplicitConversion(method.returnTpt.tpe) match + case Some(conversion) if termParamLists.size == 0 || (termParamLists.size == 1 && termParamLists.head.params.size == 0) => + Kind.Implicit(basicDefKind, Some(conversion)) + case None if termParamLists.size == 1 && termParamLists(0).params.size == 1 => + Kind.Implicit(basicDefKind, Some( + ImplicitConversion( + termParamLists(0).params(0).tpt.tpe.typeSymbol.dri, + method.returnTpt.tpe.typeSymbol.dri + ) + )) + case _ => + Kind.Implicit(basicDefKind, None) + else if methodSymbol.flags.is(Flags.Given) then Kind.Given(basicDefKind, Some(method.returnTpt.tpe.asSignature), extractImplicitConversion(method.returnTpt.tpe)) + else specificKind(basicDefKind) val origin = if !methodSymbol.isOverridden then Origin.RegularlyDefined else val overriddenSyms = methodSymbol.allOverriddenSymbols.map(_.owner) From b7de2b20519fca6b7a73207755cec1b0a6fbbbda Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 25 Aug 2022 15:03:34 +0200 Subject: [PATCH 082/657] Uniformize Parsers ParamClause methods --- .../dotty/tools/dotc/parsing/Parsers.scala | 118 ++++++++++-------- 1 file changed, 63 insertions(+), 55 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 99359ac06a37..a56c5175be02 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3083,16 +3083,17 @@ object Parsers { * | DefTermParamClause * | UsingParamClause */ - def typeOrTermParamClause(nparams: Int, // number of parameters preceding this clause - ofClass: Boolean = false, // owner is a class - ofCaseClass: Boolean = false, // owner is a case class - prefix: Boolean = false, // clause precedes name of an extension method - givenOnly: Boolean = false, // only given parameters allowed - firstClause: Boolean = false, // clause is the first in regular list of clauses - ownerKind: ParamOwner + def typeOrTermParamClause( + ownerKind: ParamOwner, + nparams: Int, // number of parameters preceding this clause + ofClass: Boolean = false, // owner is a class + ofCaseClass: Boolean = false, // owner is a case class + prefix: Boolean = false, // clause precedes name of an extension method + givenOnly: Boolean = false, // only given parameters allowed + firstClause: Boolean = false // clause is the first in regular list of clauses ): List[TypeDef] | List[ValDef] = if (in.token == LPAREN) - paramClause(nparams, ofClass, ofCaseClass, prefix, givenOnly, firstClause) + termParamClause(nparams, ofClass, ofCaseClass, prefix, givenOnly, firstClause) else if (in.token == LBRACKET) typeParamClause(ownerKind) else @@ -3103,20 +3104,20 @@ object Parsers { /** DefParamClauses ::= DefParamClause { DefParamClause } */ def typeOrTermParamClauses( - ownerKind: ParamOwner, - ofClass: Boolean = false, - ofCaseClass: Boolean = false, - givenOnly: Boolean = false, - numLeadParams: Int = 0 - ): List[List[TypeDef] | List[ValDef]] = - - def recur(firstClause: Boolean, nparams: Int): List[List[TypeDef] | List[ValDef]] = + ownerKind: ParamOwner, + ofClass: Boolean = false, + ofCaseClass: Boolean = false, + givenOnly: Boolean = false, + numLeadParams: Int = 0 + ): List[List[TypeDef] | List[ValDef]] = + + def recur(firstClause: Boolean, numLeadParams: Int): List[List[TypeDef] | List[ValDef]] = newLineOptWhenFollowedBy(LPAREN) newLineOptWhenFollowedBy(LBRACKET) if in.token == LPAREN then val paramsStart = in.offset - val params = paramClause( - nparams, + val params = termParamClause( + numLeadParams, ofClass = ofClass, ofCaseClass = ofCaseClass, givenOnly = givenOnly, @@ -3124,13 +3125,13 @@ object Parsers { val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) params :: ( if lastClause then Nil - else recur(firstClause = false, nparams + params.length)) + else recur(firstClause = false, numLeadParams + params.length)) else if in.token == LBRACKET then - typeParamClause(ownerKind) :: recur(firstClause, nparams) + typeParamClause(ownerKind) :: recur(firstClause, numLeadParams) else Nil end recur - recur(firstClause = true, nparams = numLeadParams) + recur(firstClause = true, numLeadParams = numLeadParams) end typeOrTermParamClauses @@ -3187,15 +3188,15 @@ object Parsers { /** ContextTypes ::= FunArgType {‘,’ FunArgType} */ - def contextTypes(ofClass: Boolean, nparams: Int, impliedMods: Modifiers): List[ValDef] = + def contextTypes(ofClass: Boolean, numLeadParams: Int, impliedMods: Modifiers): List[ValDef] = val tps = commaSeparated(funArgType) - var counter = nparams + var counter = numLeadParams def nextIdx = { counter += 1; counter } val paramFlags = if ofClass then LocalParamAccessor else Param tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | impliedMods.flags)) - /** ClsParamClause ::= ‘(’ [‘erased’] ClsParams ‘)’ | UsingClsParamClause - * UsingClsParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ + /** ClsTermParamClause ::= ‘(’ [‘erased’] ClsParams ‘)’ | UsingClsTermParamClause + * UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ * ClsParams ::= ClsParam {‘,’ ClsParam} * ClsParam ::= {Annotation} * @@ -3212,13 +3213,14 @@ object Parsers { * * @return the list of parameter definitions */ - def paramClause(nparams: Int, // number of parameters preceding this clause - ofClass: Boolean = false, // owner is a class - ofCaseClass: Boolean = false, // owner is a case class - prefix: Boolean = false, // clause precedes name of an extension method - givenOnly: Boolean = false, // only given parameters allowed - firstClause: Boolean = false // clause is the first in regular list of clauses - ): List[ValDef] = { + def termParamClause( + numLeadParams: Int, // number of parameters preceding this clause + ofClass: Boolean = false, // owner is a class + ofCaseClass: Boolean = false, // owner is a case class + prefix: Boolean = false, // clause precedes name of an extension method + givenOnly: Boolean = false, // only given parameters allowed + firstClause: Boolean = false // clause is the first in regular list of clauses + ): List[ValDef] = { var impliedMods: Modifiers = EmptyModifiers def addParamMod(mod: () => Mod) = impliedMods = addMod(impliedMods, atSpan(in.skipToken()) { mod() }) @@ -3283,7 +3285,7 @@ object Parsers { checkVarArgsRules(rest) } - // begin paramClause + // begin termParamClause inParens { if in.token == RPAREN && !prefix && !impliedMods.is(Given) then Nil else @@ -3298,28 +3300,30 @@ object Parsers { || startParamTokens.contains(in.token) || isIdent && (in.name == nme.inline || in.lookahead.isColon) if isParams then commaSeparated(() => param()) - else contextTypes(ofClass, nparams, impliedMods) + else contextTypes(ofClass, numLeadParams, impliedMods) checkVarArgsRules(clause) clause } } - /** ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] - * TypelessClauses ::= TypelessClause {TypelessClause} + /** ClsTermParamClauses ::= {ClsTermParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] + * TypelessClauses ::= TypelessClause {TypelessClause} * * @return The parameter definitions */ - def paramClauses(ofClass: Boolean = false, - ofCaseClass: Boolean = false, - givenOnly: Boolean = false, - numLeadParams: Int = 0): List[List[ValDef]] = + def termParamClauses( + ofClass: Boolean = false, + ofCaseClass: Boolean = false, + givenOnly: Boolean = false, + numLeadParams: Int = 0 + ): List[List[ValDef]] = - def recur(firstClause: Boolean, nparams: Int): List[List[ValDef]] = + def recur(firstClause: Boolean, numLeadParams: Int): List[List[ValDef]] = newLineOptWhenFollowedBy(LPAREN) if in.token == LPAREN then val paramsStart = in.offset - val params = paramClause( - nparams, + val params = termParamClause( + numLeadParams, ofClass = ofClass, ofCaseClass = ofCaseClass, givenOnly = givenOnly, @@ -3327,12 +3331,12 @@ object Parsers { val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) params :: ( if lastClause then Nil - else recur(firstClause = false, nparams + params.length)) + else recur(firstClause = false, numLeadParams + params.length)) else Nil end recur recur(firstClause = true, numLeadParams) - end paramClauses + end termParamClauses /* -------- DEFS ------------------------------------------- */ @@ -3573,11 +3577,15 @@ object Parsers { } } + + /** DefDef ::= DefSig [‘:’ Type] ‘=’ Expr * | this TypelessClauses [DefImplicitClause] `=' ConstrExpr * DefDcl ::= DefSig `:' Type + * DefSig ::= id [DefTypeParamClause] DefTermParamClauses + * + * if clauseInterleaving is enabled: * DefSig ::= id [DefParamClauses] [DefImplicitClause] - * | ExtParamClause [nl] [‘.’] id DefParamClauses */ def defDefOrDcl(start: Offset, mods: Modifiers, numLeadParams: Int = 0): DefDef = atSpan(start, nameStart) { @@ -3596,7 +3604,7 @@ object Parsers { if (in.token == THIS) { in.nextToken() - val vparamss = paramClauses(numLeadParams = numLeadParams) + val vparamss = termParamClauses(numLeadParams = numLeadParams) if (vparamss.isEmpty || vparamss.head.take(1).exists(_.mods.isOneOf(GivenOrImplicit))) in.token match { case LBRACKET => syntaxError(em"no type parameters allowed here") @@ -3753,12 +3761,12 @@ object Parsers { val templ = templateOpt(constr) finalizeDef(TypeDef(name, templ), mods, start) - /** ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses + /** ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsTermParamClauses */ def classConstr(isCaseClass: Boolean = false): DefDef = atSpan(in.lastOffset) { val tparams = typeParamClauseOpt(ParamOwner.Class) val cmods = fromWithinClassConstr(constrModsOpt()) - val vparamss = paramClauses(ofClass = true, ofCaseClass = isCaseClass) + val vparamss = termParamClauses(ofClass = true, ofCaseClass = isCaseClass) makeConstructor(tparams, vparamss).withMods(cmods) } @@ -3860,7 +3868,7 @@ object Parsers { newLineOpt() val vparamss = if in.token == LPAREN && in.lookahead.isIdent(nme.using) - then paramClauses(givenOnly = true) + then termParamClauses(givenOnly = true) else Nil newLinesOpt() val noParams = tparams.isEmpty && vparamss.isEmpty @@ -3902,13 +3910,13 @@ object Parsers { val start = in.skipToken() val tparams = typeParamClauseOpt(ParamOwner.Def) val leadParamss = ListBuffer[List[ValDef]]() - def nparams = leadParamss.map(_.length).sum + def numLeadParams = leadParamss.map(_.length).sum while - val extParams = paramClause(nparams, prefix = true) + val extParams = termParamClause(numLeadParams, prefix = true) leadParamss += extParams isUsingClause(extParams) do () - leadParamss ++= paramClauses(givenOnly = true, numLeadParams = nparams) + leadParamss ++= termParamClauses(givenOnly = true, numLeadParams = numLeadParams) if in.isColon then syntaxError(em"no `:` expected here") in.nextToken() @@ -3916,11 +3924,11 @@ object Parsers { if in.token == EXPORT then exportClause() else if isDefIntro(modifierTokens) then - extMethod(nparams) :: Nil + extMethod(numLeadParams) :: Nil else in.observeIndented() newLineOptWhenFollowedBy(LBRACE) - if in.isNestedStart then inDefScopeBraces(extMethods(nparams)) + if in.isNestedStart then inDefScopeBraces(extMethods(numLeadParams)) else { syntaxErrorOrIncomplete(em"Extension without extension methods") ; Nil } val result = atSpan(start)(ExtMethods(joinParams(tparams, leadParamss.toList), methods)) val comment = in.getDocComment(start) From cd27721adbf3b425dc150bffc3e97274e77d5ac1 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 29 Nov 2022 13:18:29 +0100 Subject: [PATCH 083/657] Remove type currying This adds back the TypeApply case that was present in Applications.scala. With it is an improved error message that shows a potential fix. --- .../dotty/tools/dotc/parsing/Parsers.scala | 15 ++++++++++----- .../dotty/tools/dotc/typer/Applications.scala | 6 ++++++ docs/_docs/internals/syntax.md | 4 ++-- .../generalized-method-syntax.md | 19 +++++-------------- docs/_docs/reference/syntax.md | 2 +- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index a56c5175be02..bac5d4e94d99 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3101,7 +3101,7 @@ object Parsers { end typeOrTermParamClause - /** DefParamClauses ::= DefParamClause { DefParamClause } + /** DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent */ def typeOrTermParamClauses( ownerKind: ParamOwner, @@ -3111,7 +3111,7 @@ object Parsers { numLeadParams: Int = 0 ): List[List[TypeDef] | List[ValDef]] = - def recur(firstClause: Boolean, numLeadParams: Int): List[List[TypeDef] | List[ValDef]] = + def recur(firstClause: Boolean, numLeadParams: Int, prevIsTypeClause: Boolean): List[List[TypeDef] | List[ValDef]] = newLineOptWhenFollowedBy(LPAREN) newLineOptWhenFollowedBy(LBRACKET) if in.token == LPAREN then @@ -3125,13 +3125,18 @@ object Parsers { val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) params :: ( if lastClause then Nil - else recur(firstClause = false, numLeadParams + params.length)) + else recur(firstClause = false, numLeadParams + params.length, prevIsTypeClause = false)) else if in.token == LBRACKET then - typeParamClause(ownerKind) :: recur(firstClause, numLeadParams) + if prevIsTypeClause then + syntaxError( + em"Type parameter lists must be separated by a term or using parameter list", + in.offset + ) + typeParamClause(ownerKind) :: recur(firstClause, numLeadParams, prevIsTypeClause = true) else Nil end recur - recur(firstClause = true, numLeadParams = numLeadParams) + recur(firstClause = true, numLeadParams = numLeadParams, prevIsTypeClause = false) end typeOrTermParamClauses diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 19b96f00d365..e15121ff1636 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1151,6 +1151,12 @@ trait Applications extends Compatibility { val typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_)) record("typedTypeApply") typedExpr(tree.fun, PolyProto(typedArgs, pt)) match { + case fun: TypeApply if !ctx.isAfterTyper => + val function = fun.fun + val args = (fun.args ++ tree.args).map(_.show).mkString(", ") + errorTree(tree, em"""illegal repeated type application + |You might have meant something like: + |${function}[${args}]""") case typedFn => typedFn.tpe.widen match { case pt: PolyType => diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index dfdad496d446..1fbb7a34b078 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -361,7 +361,7 @@ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} ValDef(mods, id, tpe, expr) -- point of mods on val/var [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -DefParamClauses ::= DefParamClause { DefParamClause } +DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent DefParamClause ::= DefTypeParamClause | DefTermParamClause | UsingParamClause @@ -427,7 +427,7 @@ Dcl ::= RefineDcl | ‘var’ VarDcl ValDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) VarDcl ::= ids ‘:’ Type PatDef(_, ids, tpe, EmptyTree) -DefDcl ::= DefSig ‘:’ Type DefDef(_, name, tparams, vparamss, tpe, EmptyTree) +DefDcl ::= DefSig ‘:’ Type DefDef(_, name, paramss, tpe, EmptyTree) DefSig ::= id [DefParamClauses] [DefImplicitClause] TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds TypeDefTree(_, name, tparams, bound [‘=’ Type] diff --git a/docs/_docs/reference/other-new-features/generalized-method-syntax.md b/docs/_docs/reference/other-new-features/generalized-method-syntax.md index dbe9c6eb0a61..c78a5df8a3f4 100644 --- a/docs/_docs/reference/other-new-features/generalized-method-syntax.md +++ b/docs/_docs/reference/other-new-features/generalized-method-syntax.md @@ -18,11 +18,11 @@ def foo[T, U](x: T)(y: U)(z: Int, s: String)(a: Array[T])(implicit ordInt: Ord[I ### In Scala 3 -The new syntax allows any number of type, term and using clause, in any order, optionnaly followed by an implicit clause: +The new syntax allows any number of type clauses, as long as they are not adjacent: (do note however that [implicit clause are discouraged, in favor of using clauses](https://docs.scala-lang.org/scala3/reference/contextual/relationship-implicits.html)) ```scala -def foo[T, U](x: T)(y: U)[V](z: V, s: String)[A](a: Array[A])(implicit List[U]) +def foo[T, U](x: T)(y: U)[V](z: V, s: String)(using Ord[Int])[A](a: Array[A])(implicit List[U]) ``` ### Unchanged @@ -48,15 +48,6 @@ trait DB { Note that simply replacing `V` by `k.Value` would not be equivalent. For example, if `k.Value` is `Some[Int]`, only the above allows: `getOrElse(k)[Option[Int]](None)`, which returns a `Number`. -### Partial Inference - -It is now possible to only infer some of the type parameters, this reduces boilerplate at the use site: -```scala -trait StaticSizeList[S <: Int & Singleton, T] -def filled[S <: Int & Singleton][T](x: T): StaticSizeList[S,T] = ??? -val helloes = filled[4]("Hello!") // S=4, and T is inferred -``` - ## Details ### Application @@ -90,9 +81,9 @@ On the other hand, the `getOrElse` method is recommended as-is, as it cannot be DefDcl ::= DefSig ‘:’ Type DefDef ::= DefSig [‘:’ Type] ‘=’ Expr DefSig ::= id [DefParamClauses] [DefImplicitClause] -DefParamClauses ::= DefParamClause { DefParamClause } -DefParamClause ::= DefTypeParamClause - | DefTermParamClause +DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent +DefParamClause ::= DefTypeParamClause + | DefTermParamClause | UsingParamClause DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 1d0c45030d2c..dde70d2131ad 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -350,7 +350,7 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -DefParamClauses ::= DefParamClause { DefParamClause } +DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent DefParamClause ::= DefTypeParamClause | DefTermParamClause | UsingParamClause From d59a3e61d78a1a5ddc5248d4929b9a3111879335 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 6 Dec 2022 16:41:53 +0100 Subject: [PATCH 084/657] Update tests --- tests/neg/interleaving-ab.scala | 5 +++-- tests/neg/interleaving-typeApply.scala | 6 +++--- tests/neg/namedTypeParams.scala | 7 +++++-- tests/pos/interleaving-ba.scala | 4 ++-- tests/pos/interleaving-classless.scala | 6 +++--- ...leaving.scala => interleaving-functor.scala} | 2 +- tests/pos/interleaving-functorCurrying.scala | 17 ----------------- tests/pos/interleaving-overload.scala | 4 ++-- tests/pos/interleaving-params.scala | 2 +- tests/pos/interleaving-typeApply.scala | 14 +++++++------- tests/pos/namedTypeParams.scala | 6 ++++++ 11 files changed, 33 insertions(+), 40 deletions(-) rename tests/pos/{interleaving-functorInterleaving.scala => interleaving-functor.scala} (90%) delete mode 100644 tests/pos/interleaving-functorCurrying.scala diff --git a/tests/neg/interleaving-ab.scala b/tests/neg/interleaving-ab.scala index 0d6649bbb3be..281002ba37fc 100644 --- a/tests/neg/interleaving-ab.scala +++ b/tests/neg/interleaving-ab.scala @@ -2,7 +2,8 @@ object Ab: given String = "" given Double = 0 - def ab[A][B](x: A)(using B): B = summon[B] - + def illegal[A][B](x: A)(using B): B = summon[B] // error: Type parameter lists must be separated by a term or using parameter list + + def ab[A](x: A)[B](using B): B = summon[B] def test = ab[Int](0: Int) // error \ No newline at end of file diff --git a/tests/neg/interleaving-typeApply.scala b/tests/neg/interleaving-typeApply.scala index 2e5f652dc21a..498873abae91 100644 --- a/tests/neg/interleaving-typeApply.scala +++ b/tests/neg/interleaving-typeApply.scala @@ -1,8 +1,8 @@ object typeApply: - def f3[T <: Int][U <: String](): T => T = ??? - def f5[T <: Int][U <: String]: [X <: Unit] => X => X = ??? - def f7[T <: Int][U <: String]()[X <: Unit]: X => X = ??? + def f3[T <: Int](using DummyImplicit)[U <: String](): T => T = ??? + def f5[T <: Int](using DummyImplicit)[U <: String]: [X <: Unit] => X => X = ??? + def f7[T <: Int](using DummyImplicit)[U <: String]()[X <: Unit]: X => X = ??? @main def test = { f3[String]() // error diff --git a/tests/neg/namedTypeParams.scala b/tests/neg/namedTypeParams.scala index 197fc6a9a790..1b85ee6349f4 100644 --- a/tests/neg/namedTypeParams.scala +++ b/tests/neg/namedTypeParams.scala @@ -17,7 +17,6 @@ object Test { def f[X, Y](x: X, y: Y): Int = ??? f[X = Int, String](1, "") // error // error - /* Conflicts with Clause Interweaving, stems from named type parameters assuming one type clause f[X = Int][X = Int][Y = String](1, "") // error: illegal repeated type application f[X = Int][Y = String](1, "") // error: illegal repeated type application @@ -25,5 +24,9 @@ object Test { f[Y = String][X = Int](1, "") // error: illegal repeated type application f[Y = String][Int](1, "") // error: illegal repeated type application - */ + + def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? + + f2[Y = String][X = Int](1, "") // error: Y is undefined + f2[Y = String](1, "") // error: Y is undefined } diff --git a/tests/pos/interleaving-ba.scala b/tests/pos/interleaving-ba.scala index 74c15d4e87e9..dc2b36202891 100644 --- a/tests/pos/interleaving-ba.scala +++ b/tests/pos/interleaving-ba.scala @@ -4,8 +4,8 @@ object BA { given String = "" given Double = 0 - def ba[B][A](x: A)(using B): B = summon[B] + def ba[A](x: A)[B](using B): B = summon[B] - def test = ba[String](0) + def test = ba(0)[String] } \ No newline at end of file diff --git a/tests/pos/interleaving-classless.scala b/tests/pos/interleaving-classless.scala index 4308030f66a6..924202673029 100644 --- a/tests/pos/interleaving-classless.scala +++ b/tests/pos/interleaving-classless.scala @@ -1,4 +1,4 @@ -def f1[T][U](x: T, y: U): (T, U) = (x, y) +def f1[T]()[U](x: T, y: U): (T, U) = (x, y) def f2[T](x: T)[U](y: U): (T, U) = (x, y) -def f3[T, U][V](x: T): U = ??? -def f4[T](x: T)[U <: x.type](y: U): (T, U) = (x, y) \ No newline at end of file +def f3[T, U](using DummyImplicit)[V](x: T): U = ??? +def f4[T](x: T)[U <: x.type](y: U): (T, U) = (x, y) diff --git a/tests/pos/interleaving-functorInterleaving.scala b/tests/pos/interleaving-functor.scala similarity index 90% rename from tests/pos/interleaving-functorInterleaving.scala rename to tests/pos/interleaving-functor.scala index fb14c151387c..00c26151f5cb 100644 --- a/tests/pos/interleaving-functorInterleaving.scala +++ b/tests/pos/interleaving-functor.scala @@ -10,7 +10,7 @@ object functorInterweaving: def map[A](x: List[A])[B](f: A => B): List[B] = x.map(f) - def assertTransformation[F[_]: Functor][A](original: F[A])[B](expected: F[B])(mapping: A => B): Unit = + def assertTransformation[F[_]: Functor, A](original: F[A])[B](expected: F[B])(mapping: A => B): Unit = assert(expected == summon[Functor[F]].map(original)(mapping)) @main def testInterweaving = diff --git a/tests/pos/interleaving-functorCurrying.scala b/tests/pos/interleaving-functorCurrying.scala deleted file mode 100644 index 078d65233997..000000000000 --- a/tests/pos/interleaving-functorCurrying.scala +++ /dev/null @@ -1,17 +0,0 @@ -object functorCurrying: - //taken from https://dotty.epfl.ch/docs/reference/contextual/type-classes.html - //at version 3.1.1-RC1-bin-20210930-01f040b-NIGHTLY - //modified to have type currying - trait Functor[F[_]]: - def map[A][B](x: F[A], f: A => B): F[B] - - - given Functor[List] with - def map[A][B](x: List[A], f: A => B): List[B] = - x.map(f) - - def assertTransformation[F[_]: Functor][A][B](expected: F[B], original: F[A], mapping: A => B): Unit = - assert(expected == summon[Functor[F]].map(original, mapping)) - - @main def testCurrying = - assertTransformation(List("a1", "b1"), List("a", "b"), elt => s"${elt}1") diff --git a/tests/pos/interleaving-overload.scala b/tests/pos/interleaving-overload.scala index 3dcb0a911704..02074fd82914 100644 --- a/tests/pos/interleaving-overload.scala +++ b/tests/pos/interleaving-overload.scala @@ -1,8 +1,8 @@ class A{ - def f1[T][U](x: Any) = ??? - def f1[T][U](x: Int) = ??? + def f1[T](x: Any)[U] = ??? + def f1[T](x: Int)[U] = ??? f1(1) f1("hello") diff --git a/tests/pos/interleaving-params.scala b/tests/pos/interleaving-params.scala index fb55d451b2bd..dec9f6780370 100644 --- a/tests/pos/interleaving-params.scala +++ b/tests/pos/interleaving-params.scala @@ -1,6 +1,6 @@ class Params{ type U - def foo[T](x: T)[U >: x.type <: T][L <: List[U]](l: L): L = ??? + def foo[T](x: T)[U >: x.type <: T](using U)[L <: List[U]](l: L): L = ??? def aaa(x: U): U = ??? def bbb[T <: U](x: U)[U]: U = ??? } \ No newline at end of file diff --git a/tests/pos/interleaving-typeApply.scala b/tests/pos/interleaving-typeApply.scala index b637d59e2239..5b0be806bf33 100644 --- a/tests/pos/interleaving-typeApply.scala +++ b/tests/pos/interleaving-typeApply.scala @@ -1,13 +1,13 @@ object typeApply: def f0[T]: [U] => T => T = ??? - def f1[T][U]: T => T = ??? - def f2[T][U](): T => T = ??? - def f3[T <: Int][U <: String](): T => T = ??? - def f4[T <: Int][U <: String]: T => T = ??? - def f5[T <: Int][U <: String]: [X <: Unit] => X => X = ??? - def f6[T <: Int][U <: String](): [X <: Unit] => X => X = ??? - def f7[T <: Int][U <: String]()[X <: Unit]: X => X = ??? + def f1[T](using DummyImplicit)[U]: T => T = ??? + def f2[T](using DummyImplicit)[U](): T => T = ??? + def f3[T <: Int](using DummyImplicit)[U <: String](): T => T = ??? + def f4[T <: Int](using DummyImplicit)[U <: String]: T => T = ??? + def f5[T <: Int](using DummyImplicit)[U <: String]: [X <: Unit] => X => X = ??? + def f6[T <: Int](using DummyImplicit)[U <: String](): [X <: Unit] => X => X = ??? + def f7[T <: Int](using DummyImplicit)[U <: String]()[X <: Unit]: X => X = ??? @main def test = { f0[Int][String] diff --git a/tests/pos/namedTypeParams.scala b/tests/pos/namedTypeParams.scala index a8c38972838c..86f35bf3c596 100644 --- a/tests/pos/namedTypeParams.scala +++ b/tests/pos/namedTypeParams.scala @@ -9,4 +9,10 @@ object Test { f[Y = String](1, "") + def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? + + f2[X = Int][Y = String](1, "") + f2[X = Int](1, "") + + } From b9b3ed5a12a13f1852fd32ca5e3dce7995870e22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 2 Feb 2023 16:47:59 +0100 Subject: [PATCH 085/657] Fix #15107: Avoid re-emitting a LineNumber after only LabelNodes. There was already some deduplication code to avoid consecutive `LineNumber` nodes. However, it can happen that `LabelNode`s appear in-between. In that case, we also want to deduplicate the `LineNumber`s, since labels do not actually contribute to the final bytecode. --- .../tools/backend/jvm/BCodeSkelBuilder.scala | 8 +++- .../backend/jvm/DottyBytecodeTests.scala | 2 - .../backend/jvm/InlineBytecodeTests.scala | 48 +++++++++---------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index 1885210a6687..b9f0d4ee302d 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -556,11 +556,17 @@ trait BCodeSkelBuilder extends BCodeHelpers { case _ => false } ) } def lineNumber(tree: Tree): Unit = { + @tailrec + def getNonLabelNode(a: asm.tree.AbstractInsnNode): asm.tree.AbstractInsnNode = a match { + case a: asm.tree.LabelNode => getNonLabelNode(a.getPrevious) + case _ => a + } + if (!emitLines || !tree.span.exists) return; val nr = ctx.source.offsetToLine(tree.span.point) + 1 if (nr != lastEmittedLineNr) { lastEmittedLineNr = nr - lastInsn match { + getNonLabelNode(lastInsn) match { case lnn: asm.tree.LineNumberNode => // overwrite previous landmark as no instructions have been emitted for it lnn.line = nr diff --git a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala index 71bf530fcda5..ac4ba3ee0e75 100644 --- a/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala @@ -1622,7 +1622,6 @@ class DottyBytecodeTests extends DottyBytecodeTest { val instructions = instructionsFromMethod(method).filter(_.isInstanceOf[LineNumber]) val expected = List( - LineNumber(2, Label(0)), LineNumber(3, Label(0)), LineNumber(4, Label(5)), // case y => LineNumber(5, Label(9)), @@ -1664,7 +1663,6 @@ class DottyBytecodeTests extends DottyBytecodeTest { val instructions = instructionsFromMethod(method).filter(_.isInstanceOf[LineNumber]) val expected = List( - LineNumber(2, Label(0)), LineNumber(3, Label(0)), LineNumber(4, Label(5)), // case a if a == 3 => LineNumber(5, Label(15)), diff --git a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala index 33e898718b33..d55d1343ea7e 100644 --- a/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala +++ b/compiler/test/dotty/tools/backend/jvm/InlineBytecodeTests.scala @@ -163,28 +163,27 @@ class InlineBytecodeTests extends DottyBytecodeTest { val expected = List( Label(0), - LineNumber(6, Label(0)), LineNumber(3, Label(0)), VarOp(ALOAD, 0), Ldc(LDC, "tracking"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), - Label(6), - LineNumber(8, Label(6)), + Label(5), + LineNumber(8, Label(5)), VarOp(ALOAD, 0), Ldc(LDC, "abc"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), - Label(11), - LineNumber(3, Label(11)), + Label(10), + LineNumber(3, Label(10)), VarOp(ALOAD, 0), Ldc(LDC, "tracking"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), - Label(16), - LineNumber(10, Label(16)), + Label(15), + LineNumber(10, Label(15)), VarOp(ALOAD, 0), Ldc(LDC, "inner"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Op(RETURN), - Label(22) + Label(21) ) assert(instructions == expected, "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) @@ -228,23 +227,22 @@ class InlineBytecodeTests extends DottyBytecodeTest { val expected = List( Label(0), - LineNumber(12, Label(0)), LineNumber(7, Label(0)), VarOp(ALOAD, 0), Ldc(LDC, "tracking"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), - Label(6), - LineNumber(3, Label(6)), + Label(5), + LineNumber(3, Label(5)), VarOp(ALOAD, 0), Ldc(LDC, "tracking2"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), - Label(11), - LineNumber(14, Label(11)), + Label(10), + LineNumber(14, Label(10)), VarOp(ALOAD, 0), Ldc(LDC, "abc"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Op(RETURN), - Label(17) + Label(16) ) assert(instructions == expected, "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) @@ -288,23 +286,22 @@ class InlineBytecodeTests extends DottyBytecodeTest { val expected = List( Label(0), - LineNumber(12, Label(0)), LineNumber(3, Label(0)), VarOp(ALOAD, 0), Ldc(LDC, "tracking2"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), - Label(6), - LineNumber(8, Label(6)), + Label(5), + LineNumber(8, Label(5)), VarOp(ALOAD, 0), Ldc(LDC, "fgh"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), - Label(11), - LineNumber(14, Label(11)), + Label(10), + LineNumber(14, Label(10)), VarOp(ALOAD, 0), Ldc(LDC, "abc"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Op(RETURN), - Label(17) + Label(16) ) assert(instructions == expected, "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) @@ -349,23 +346,22 @@ class InlineBytecodeTests extends DottyBytecodeTest { val expected = List( Label(0), - LineNumber(13, Label(0)), LineNumber(3, Label(0)), VarOp(ALOAD, 0), Ldc(LDC, "tracking2"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), - Label(6), - LineNumber(3, Label(6)), + Label(5), + LineNumber(3, Label(5)), VarOp(ALOAD, 0), Ldc(LDC, "tracking2"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), - Label(11), - LineNumber(15, Label(11)), + Label(10), + LineNumber(15, Label(10)), VarOp(ALOAD, 0), Ldc(LDC, "abc"), Invoke(INVOKEVIRTUAL, "Foo", "foo", "(Ljava/lang/String;)V", false), Op(RETURN), - Label(17) + Label(16) ) assert(instructions == expected, "`track` was not properly inlined in `main`\n" + diffInstructions(instructions, expected)) From 1a6c5c5552a15458c617cdbe09d4f8ce09c7462b Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 13 Jan 2023 14:45:17 +0100 Subject: [PATCH 086/657] Add and Fix tests --- .../src/tests/extensionParams.scala | 1 - .../src/tests/methodsAndConstructors.scala | 1 - tests/neg/extension-methods.scala | 2 +- tests/neg/interleaving-ab.scala | 14 +-- tests/neg/interleaving-nameCollision.scala | 3 - tests/neg/interleaving-params.scala | 2 +- .../neg/interleaving-signatureCollision.scala | 3 + tests/neg/interleaving-typeApply.check | 30 ++++++ tests/neg/interleaving-typeApply.scala | 2 +- tests/neg/interleaving-unmatched.scala | 2 +- tests/neg/namedTypeParams.check | 102 ++++++++++++++++++ tests/neg/namedTypeParams.scala | 5 +- tests/neg/overrides.scala | 2 - tests/pos/interleaving-ba.scala | 3 +- tests/pos/interleaving-chainedParams.scala | 24 ++--- tests/pos/interleaving-functor.scala | 4 +- tests/pos/interleaving-nameCollision.scala | 5 - tests/pos/interleaving-overload.scala | 2 +- tests/pos/interleaving-params.scala | 2 +- .../pos/interleaving-signatureCollision.scala | 5 + tests/pos/interleaving-typeApply.scala | 2 +- tests/pos/namedTypeParams.scala | 1 - tests/run/interleaving.scala | 101 +++++++++++++++++ 23 files changed, 273 insertions(+), 45 deletions(-) delete mode 100644 tests/neg/interleaving-nameCollision.scala create mode 100644 tests/neg/interleaving-signatureCollision.scala create mode 100644 tests/neg/interleaving-typeApply.check create mode 100644 tests/neg/namedTypeParams.check delete mode 100644 tests/pos/interleaving-nameCollision.scala create mode 100644 tests/pos/interleaving-signatureCollision.scala create mode 100644 tests/run/interleaving.scala diff --git a/scaladoc-testcases/src/tests/extensionParams.scala b/scaladoc-testcases/src/tests/extensionParams.scala index 58b95c5dac1f..e064ff6d56d7 100644 --- a/scaladoc-testcases/src/tests/extensionParams.scala +++ b/scaladoc-testcases/src/tests/extensionParams.scala @@ -56,4 +56,3 @@ extension (using String)(using Unit)(a: Animal)(using Int)(using Number) = ??? def f14[D](b: D)[T](c: T): T = ??? - diff --git a/scaladoc-testcases/src/tests/methodsAndConstructors.scala b/scaladoc-testcases/src/tests/methodsAndConstructors.scala index b4c354d174c4..5fc6af38888b 100644 --- a/scaladoc-testcases/src/tests/methodsAndConstructors.scala +++ b/scaladoc-testcases/src/tests/methodsAndConstructors.scala @@ -62,4 +62,3 @@ class Methods: def clauseInterleaving[T](x: T)[U](y: U)(using (T, U)): (T, U) = ??? - diff --git a/tests/neg/extension-methods.scala b/tests/neg/extension-methods.scala index e075105762f9..a11b2cca5add 100644 --- a/tests/neg/extension-methods.scala +++ b/tests/neg/extension-methods.scala @@ -15,4 +15,4 @@ object Test { def f2[T]: T = ??? // error: T is already defined as type T def f3(xs: List[T]) = ??? // error: xs is already defined as value xs } -} \ No newline at end of file +} diff --git a/tests/neg/interleaving-ab.scala b/tests/neg/interleaving-ab.scala index 281002ba37fc..5516d9b9dd51 100644 --- a/tests/neg/interleaving-ab.scala +++ b/tests/neg/interleaving-ab.scala @@ -1,9 +1,9 @@ object Ab: - given String = "" - given Double = 0 + given String = "" + given Double = 0 - def illegal[A][B](x: A)(using B): B = summon[B] // error: Type parameter lists must be separated by a term or using parameter list - - def ab[A](x: A)[B](using B): B = summon[B] - def test = - ab[Int](0: Int) // error \ No newline at end of file + def illegal[A][B](x: A)(using B): B = summon[B] // error: Type parameter lists must be separated by a term or using parameter list + + def ab[A](x: A)[B](using B): B = summon[B] + def test = + ab[Int](0: Int) // error diff --git a/tests/neg/interleaving-nameCollision.scala b/tests/neg/interleaving-nameCollision.scala deleted file mode 100644 index 44aa1e169db4..000000000000 --- a/tests/neg/interleaving-nameCollision.scala +++ /dev/null @@ -1,3 +0,0 @@ -object nameCollision: - def f[T](x: T)[U](y: U) = (x,y) - def f[T](x: T, y: T) = (x,y) // error \ No newline at end of file diff --git a/tests/neg/interleaving-params.scala b/tests/neg/interleaving-params.scala index e8fa5361a9ab..97de1e6e2948 100644 --- a/tests/neg/interleaving-params.scala +++ b/tests/neg/interleaving-params.scala @@ -4,4 +4,4 @@ class Params{ def bbb[T <: U](x: U)[U]: U = ??? // error // error def f0[T](implicit x: T)[U](y: U) = (x,y) // error def f1[T](implicit x: T)[U] = (x,y) // error -} \ No newline at end of file +} diff --git a/tests/neg/interleaving-signatureCollision.scala b/tests/neg/interleaving-signatureCollision.scala new file mode 100644 index 000000000000..d2df384dc1e7 --- /dev/null +++ b/tests/neg/interleaving-signatureCollision.scala @@ -0,0 +1,3 @@ +object signatureCollision: + def f[T](x: T)[U](y: U) = (x,y) + def f[T](x: T, y: T) = (x,y) // error diff --git a/tests/neg/interleaving-typeApply.check b/tests/neg/interleaving-typeApply.check new file mode 100644 index 000000000000..c932a899dfe6 --- /dev/null +++ b/tests/neg/interleaving-typeApply.check @@ -0,0 +1,30 @@ +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:8:11 --------------------------------------------- +8 | f3[String]() // error + | ^ + | Type argument String does not conform to upper bound Int + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:9:16 --------------------------------------------- +9 | f5[Int][Unit] // error + | ^ + | Type argument Unit does not conform to upper bound String + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:10:19 -------------------------------------------- +10 | f5[String][Unit] // error // error + | ^ + | Type argument Unit does not conform to upper bound String + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:10:11 -------------------------------------------- +10 | f5[String][Unit] // error // error + | ^ + | Type argument String does not conform to upper bound Int + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:11:11 -------------------------------------------- +11 | f7[String]()[Unit] // error + | ^ + | Type argument String does not conform to upper bound Int + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/interleaving-typeApply.scala b/tests/neg/interleaving-typeApply.scala index 498873abae91..18a21b4984e1 100644 --- a/tests/neg/interleaving-typeApply.scala +++ b/tests/neg/interleaving-typeApply.scala @@ -9,4 +9,4 @@ object typeApply: f5[Int][Unit] // error f5[String][Unit] // error // error f7[String]()[Unit] // error - } \ No newline at end of file + } diff --git a/tests/neg/interleaving-unmatched.scala b/tests/neg/interleaving-unmatched.scala index b2afcf5b5d45..c433c0a7210a 100644 --- a/tests/neg/interleaving-unmatched.scala +++ b/tests/neg/interleaving-unmatched.scala @@ -1,3 +1,3 @@ object unmatched: def f1[T (x: T)] = ??? // error - def f2(x: Any[)T] = ??? // error // error \ No newline at end of file + def f2(x: Any[)T] = ??? // error // error diff --git a/tests/neg/namedTypeParams.check b/tests/neg/namedTypeParams.check new file mode 100644 index 000000000000..1252f29c4e9e --- /dev/null +++ b/tests/neg/namedTypeParams.check @@ -0,0 +1,102 @@ +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:2:8 ------------------------------------------------------------ +2 |class D[type T] // error: identifier expected, but `type` found + | ^^^^ + | an identifier expected, but 'type' found + | + | longer explanation available when compiling with `-explain` +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:11:13 ---------------------------------------------------------- +11 | val x: C[T = Int] = // error: ']' expected, but `=` found // error + | ^ + | ']' expected, but '=' found +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:12:12 ---------------------------------------------------------- +12 | new C[T = Int] // error: ']' expected, but `=` found // error + | ^ + | ']' expected, but '=' found +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:14:22 ---------------------------------------------------------- +14 | class E extends C[T = Int] // error: ']' expected, but `=` found // error + | ^ + | ']' expected, but '=' found +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:15:22 ---------------------------------------------------------- +15 | class F extends C[T = Int]() // error: ']' expected, but `=` found // error + | ^ + | ']' expected, but '=' found +-- [E040] Syntax Error: tests/neg/namedTypeParams.scala:19:19 ---------------------------------------------------------- +19 | f[X = Int, String](1, "") // error // error + | ^ + | '=' expected, but ']' found +-- Error: tests/neg/namedTypeParams.scala:6:8 -------------------------------------------------------------------------- +6 | f[X = Int, Y = Int](1, 2) // error: experimental // error: experimental + | ^^^ + | Named type arguments are experimental, + | they must be enabled with a `experimental.namedTypeArguments` language import or setting +-- Error: tests/neg/namedTypeParams.scala:6:17 ------------------------------------------------------------------------- +6 | f[X = Int, Y = Int](1, 2) // error: experimental // error: experimental + | ^^^ + | Named type arguments are experimental, + | they must be enabled with a `experimental.namedTypeArguments` language import or setting +-- [E006] Not Found Error: tests/neg/namedTypeParams.scala:11:11 ------------------------------------------------------- +11 | val x: C[T = Int] = // error: ']' expected, but `=` found // error + | ^ + | Not found: type T + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/namedTypeParams.scala:12:10 ------------------------------------------------------- +12 | new C[T = Int] // error: ']' expected, but `=` found // error + | ^ + | Not found: type T + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/namedTypeParams.scala:14:20 ------------------------------------------------------- +14 | class E extends C[T = Int] // error: ']' expected, but `=` found // error + | ^ + | Not found: type T + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg/namedTypeParams.scala:15:20 ------------------------------------------------------- +15 | class F extends C[T = Int]() // error: ']' expected, but `=` found // error + | ^ + | Not found: type T + | + | longer explanation available when compiling with `-explain` +-- [E102] Syntax Error: tests/neg/namedTypeParams.scala:19:18 ---------------------------------------------------------- +19 | f[X = Int, String](1, "") // error // error + | ^ + | Type parameter String is undefined. Expected one of X, Y. +-- Error: tests/neg/namedTypeParams.scala:20:12 ------------------------------------------------------------------------ +20 | f[X = Int][X = Int][Y = String](1, "") // error: illegal repeated type application + | ^^^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | Test.f[X = Int, X = Int] +-- Error: tests/neg/namedTypeParams.scala:22:12 ------------------------------------------------------------------------ +22 | f[X = Int][Y = String](1, "") // error: illegal repeated type application + | ^^^^^^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | Test.f[X = Int, Y = String] +-- Error: tests/neg/namedTypeParams.scala:23:12 ------------------------------------------------------------------------ +23 | f[X = Int][String](1, "") // error: illegal repeated type application + | ^^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | Test.f[X = Int, String] +-- Error: tests/neg/namedTypeParams.scala:25:15 ------------------------------------------------------------------------ +25 | f[Y = String][X = Int](1, "") // error: illegal repeated type application + | ^^^^^^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | Test.f[Y = String, X = Int] +-- Error: tests/neg/namedTypeParams.scala:26:15 ------------------------------------------------------------------------ +26 | f[Y = String][Int](1, "") // error: illegal repeated type application + | ^^^^^^^^^^^^^^^^^^ + | illegal repeated type application + | You might have meant something like: + | Test.f[Y = String, Int] +-- [E102] Syntax Error: tests/neg/namedTypeParams.scala:32:9 ----------------------------------------------------------- +32 | f2[Y = String][X = Int](1, "") // error: Y is undefined + | ^^^^^^ + | Type parameter Y is undefined. Expected one of X. +-- [E102] Syntax Error: tests/neg/namedTypeParams.scala:33:9 ----------------------------------------------------------- +33 | f2[Y = String](1, "") // error: Y is undefined + | ^^^^^^ + | Type parameter Y is undefined. Expected one of X. diff --git a/tests/neg/namedTypeParams.scala b/tests/neg/namedTypeParams.scala index 1b85ee6349f4..489ac1e8cdb6 100644 --- a/tests/neg/namedTypeParams.scala +++ b/tests/neg/namedTypeParams.scala @@ -5,7 +5,7 @@ object Test0: def f[X, Y](x: X, y: Y): Int = ??? f[X = Int, Y = Int](1, 2) // error: experimental // error: experimental -object Test { +object Test: import language.experimental.namedTypeArguments val x: C[T = Int] = // error: ']' expected, but `=` found // error @@ -25,8 +25,9 @@ object Test { f[Y = String][X = Int](1, "") // error: illegal repeated type application f[Y = String][Int](1, "") // error: illegal repeated type application +object TestInterleaving: + import language.experimental.namedTypeArguments def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? f2[Y = String][X = Int](1, "") // error: Y is undefined f2[Y = String](1, "") // error: Y is undefined -} diff --git a/tests/neg/overrides.scala b/tests/neg/overrides.scala index ee7a8b73ba0d..48dbd41ab839 100644 --- a/tests/neg/overrides.scala +++ b/tests/neg/overrides.scala @@ -53,7 +53,6 @@ class B extends A[Int] { override def b[T <: Int](x: Int)(y: String) = true // error override def next(): Int = ??? // error: incompatible type - } class C extends A[String] { @@ -115,4 +114,3 @@ class C extends A { override def m: Int = 42 // error: has incompatible type } } - diff --git a/tests/pos/interleaving-ba.scala b/tests/pos/interleaving-ba.scala index dc2b36202891..4a7d721c804e 100644 --- a/tests/pos/interleaving-ba.scala +++ b/tests/pos/interleaving-ba.scala @@ -1,5 +1,4 @@ - object BA { given String = "" given Double = 0 @@ -8,4 +7,4 @@ object BA { def test = ba(0)[String] -} \ No newline at end of file +} diff --git a/tests/pos/interleaving-chainedParams.scala b/tests/pos/interleaving-chainedParams.scala index 6ed970a69458..8613120f581a 100644 --- a/tests/pos/interleaving-chainedParams.scala +++ b/tests/pos/interleaving-chainedParams.scala @@ -1,18 +1,18 @@ object chainedParams{ - trait Chain{ - type Tail <: Chain - } + trait Chain{ + type Tail <: Chain + } + + def f[C1 <: Chain](c1: C1)[C2 <: c1.Tail](c2: C2)[C3 <: c2.Tail](c3: C3): c3.Tail = ??? - def f[C1 <: Chain](c1: C1)[C2 <: c1.Tail](c2: C2)[C3 <: c2.Tail](c3: C3): c3.Tail = ??? + val self = new Chain{ type Tail = this.type } + val res: self.type = f(self)(self)(self) - val self = new Chain{ type Tail = this.type } - val res: self.type = f(self)(self)(self) + type C <: Chain - type C <: Chain - - val c3 = new Chain{ type Tail = C } - val c2 = new Chain{ type Tail = c3.type } - val c1 = new Chain{ type Tail = c2.type } - val u: C = f(c1)(c2)(c3) + val c3 = new Chain{ type Tail = C } + val c2 = new Chain{ type Tail = c3.type } + val c1 = new Chain{ type Tail = c2.type } + val u: C = f(c1)(c2)(c3) } diff --git a/tests/pos/interleaving-functor.scala b/tests/pos/interleaving-functor.scala index 00c26151f5cb..c3df1f869850 100644 --- a/tests/pos/interleaving-functor.scala +++ b/tests/pos/interleaving-functor.scala @@ -1,7 +1,7 @@ -object functorInterweaving: +object functorInterleaving: //taken from https://dotty.epfl.ch/docs/reference/contextual/type-classes.html //at version 3.1.1-RC1-bin-20210930-01f040b-NIGHTLY - //modified to have type interveawing + //modified to have type interleaving trait Functor[F[_]]: def map[A](x: F[A])[B](f: A => B): F[B] diff --git a/tests/pos/interleaving-nameCollision.scala b/tests/pos/interleaving-nameCollision.scala deleted file mode 100644 index b66f63b9f92c..000000000000 --- a/tests/pos/interleaving-nameCollision.scala +++ /dev/null @@ -1,5 +0,0 @@ -import scala.annotation.targetName - -object nameCollision: - def f[T](x: T)[U](y: U) = (x,y) - @targetName("g") def f[T](x: T, y: T) = (x,y) \ No newline at end of file diff --git a/tests/pos/interleaving-overload.scala b/tests/pos/interleaving-overload.scala index 02074fd82914..e1c3db1abe37 100644 --- a/tests/pos/interleaving-overload.scala +++ b/tests/pos/interleaving-overload.scala @@ -16,4 +16,4 @@ class A{ f2[Any](1) f2[Any][Any](1) -} \ No newline at end of file +} diff --git a/tests/pos/interleaving-params.scala b/tests/pos/interleaving-params.scala index dec9f6780370..e86f856a728d 100644 --- a/tests/pos/interleaving-params.scala +++ b/tests/pos/interleaving-params.scala @@ -3,4 +3,4 @@ class Params{ def foo[T](x: T)[U >: x.type <: T](using U)[L <: List[U]](l: L): L = ??? def aaa(x: U): U = ??? def bbb[T <: U](x: U)[U]: U = ??? -} \ No newline at end of file +} diff --git a/tests/pos/interleaving-signatureCollision.scala b/tests/pos/interleaving-signatureCollision.scala new file mode 100644 index 000000000000..be016e7bdbfe --- /dev/null +++ b/tests/pos/interleaving-signatureCollision.scala @@ -0,0 +1,5 @@ +import scala.annotation.targetName + +object signatureCollision: + def f[T](x: T)[U](y: U) = (x,y) + @targetName("g") def f[T](x: T, y: T) = (x,y) diff --git a/tests/pos/interleaving-typeApply.scala b/tests/pos/interleaving-typeApply.scala index 5b0be806bf33..ba9486408adb 100644 --- a/tests/pos/interleaving-typeApply.scala +++ b/tests/pos/interleaving-typeApply.scala @@ -19,4 +19,4 @@ object typeApply: f5[Int][String][Unit] f6[Int]()[Unit] f7[Int]()[Unit] - } \ No newline at end of file + } diff --git a/tests/pos/namedTypeParams.scala b/tests/pos/namedTypeParams.scala index 86f35bf3c596..160bfc11f079 100644 --- a/tests/pos/namedTypeParams.scala +++ b/tests/pos/namedTypeParams.scala @@ -8,7 +8,6 @@ object Test { f[X = Int](1, "") f[Y = String](1, "") - def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? f2[X = Int][Y = String](1, "") diff --git a/tests/run/interleaving.scala b/tests/run/interleaving.scala new file mode 100644 index 000000000000..e84f2896e5af --- /dev/null +++ b/tests/run/interleaving.scala @@ -0,0 +1,101 @@ +object Test extends App { + trait Key { type Value } + trait DB { + def getOrElse(k: Key)[V >: k.Value](default: V): V // dependent type parameter + } + + val key1 = new Key{ type Value = Some[Int] } + val key2 = new Key{ type Value = Some[Int] } + val key3 = new Key{ type Value = Some[String] } + + val db1: DB = new DB{ + def getOrElse(k: Key)[V >: k.Value](default: V): V = if k == key1 then Some(4).asInstanceOf[k.Value] else default + } + + // Interleaved method with dependent type bound + val default1: None.type = None + assert(db1.getOrElse(key1)[Option[Int]](default1) == Some(4)) + assert(db1.getOrElse(key2)[Option[Int]](default1) == default1) + assert(db1.getOrElse(key3)[Option[String]](default1) == default1) + assert(db1.getOrElse(key1)(default1) == Some(4)) + assert(db1.getOrElse(key2)(default1) == default1) + assert(db1.getOrElse(key3)(default1) == default1) + + val default2: Any = 3 + assert(db1.getOrElse(key1)[Any](default2) == Some(4)) + assert(db1.getOrElse(key2)[Any](default2) == default2) + assert(db1.getOrElse(key3)[Any](default2) == default2) + assert(db1.getOrElse(key1)(default2) == Some(4)) + assert(db1.getOrElse(key2)(default2) == default2) + assert(db1.getOrElse(key3)(default2) == default2) + + // Extension method and using parameter + extension (k: Key) + def lookupOrElse(using db: DB)[V >: k.Value](default: V): V = db.getOrElse(k)(default) + + object Block1: + given DB = db1 + + assert(key1.lookupOrElse[Option[Int]](default1) == Some(4)) + assert(key2.lookupOrElse[Option[Int]](default1) == default1) + assert(key3.lookupOrElse[Option[String]](default1) == default1) + assert(key1.lookupOrElse(default1) == Some(4)) + assert(key2.lookupOrElse(default1) == default1) + assert(key3.lookupOrElse(default1) == default1) + + assert(key1.lookupOrElse[Any](default2) == Some(4)) + assert(key2.lookupOrElse[Any](default2) == default2) + assert(key3.lookupOrElse[Any](default2) == default2) + assert(key1.lookupOrElse(default2) == Some(4)) + assert(key2.lookupOrElse(default2) == default2) + assert(key3.lookupOrElse(default2) == default2) + end Block1 + + // Right associative extension method + extension (db: DB) + def ?:(k: Key)[V >: k.Value](default: V): V = db.getOrElse(k)(default) + + assert((db1 ?: (key1))[Option[Int]](default1) == Some(4)) + assert((db1 ?: (key2))[Option[Int]](default1) == default1) + assert((db1 ?: (key3))[Option[String]](default1) == default1) + assert((db1 ?: (key1))(default1) == Some(4)) + assert((db1 ?: (key2))(default1) == default1) + assert((db1 ?: (key3))(default1) == default1) + + assert((db1 ?: (key1))[Any](default2) == Some(4)) + assert((db1 ?: (key2))[Any](default2) == default2) + assert((db1 ?: (key3))[Any](default2) == default2) + assert((db1 ?: (key1))(default2) == Some(4)) + assert((db1 ?: (key2))(default2) == default2) + assert((db1 ?: (key3))(default2) == default2) + + + assert(key1.?:(db1)[Option[Int]](default1) == Some(4)) + assert(key2.?:(db1)[Option[Int]](default1) == default1) + assert(key3.?:(db1)[Option[String]](default1) == default1) + assert(key1.?:(db1)(default1) == Some(4)) + assert(key2.?:(db1)(default1) == default1) + assert(key3.?:(db1)(default1) == default1) + + assert(key1.?:(db1)[Any](default2) == Some(4)) + assert(key2.?:(db1)[Any](default2) == default2) + assert(key3.?:(db1)[Any](default2) == default2) + assert(key1.?:(db1)(default2) == Some(4)) + assert(key2.?:(db1)(default2) == default2) + assert(key3.?:(db1)(default2) == default2) + + + assert(?:(key1)(db1)[Option[Int]](default1) == Some(4)) + assert(?:(key2)(db1)[Option[Int]](default1) == default1) + assert(?:(key3)(db1)[Option[String]](default1) == default1) + assert(?:(key1)(db1)(default1) == Some(4)) + assert(?:(key2)(db1)(default1) == default1) + assert(?:(key3)(db1)(default1) == default1) + + assert(?:(key1)(db1)[Any](default2) == Some(4)) + assert(?:(key2)(db1)[Any](default2) == default2) + assert(?:(key3)(db1)[Any](default2) == default2) + assert(?:(key1)(db1)(default2) == Some(4)) + assert(?:(key2)(db1)(default2) == default2) + assert(?:(key3)(db1)(default2) == default2) +} From 35ed4295b2bcf3afca6e7f7bc4163aac6a44f742 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 2 Feb 2023 15:35:57 +0100 Subject: [PATCH 087/657] Fix right-associative-extension-methods.md --- .../right-associative-extension-methods.md | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/docs/_docs/reference/contextual/right-associative-extension-methods.md b/docs/_docs/reference/contextual/right-associative-extension-methods.md index 068123df8cd2..9d711c42ad3d 100644 --- a/docs/_docs/reference/contextual/right-associative-extension-methods.md +++ b/docs/_docs/reference/contextual/right-associative-extension-methods.md @@ -4,46 +4,54 @@ title: "Right-Associative Extension Methods: Details" nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/right-associative-extension-methods.html --- -The most general form of leading parameters of an extension method is as follows: - +The most general signature an extension method can have is as follows: + - An optional type clause `leadingTyParamss` - A possibly empty list of using clauses `leadingUsing` - - A single parameter `extensionParam` + - A single parameter `leftParamss` - A possibly empty list of using clauses `trailingUsing` + - A name (preceded by the `def` keyword) + - An optional type clause `rightTyParamss` + - An optional explicit term clause `rightParamss` + - Any number of any clauses `rest` -This is then followed by `def`, the method name, and possibly further parameters -`otherParams`. An example is: +For example: ```scala - extension (using a: A, b: B)(using c: C) // <-- leadingUsing - (x: X) // <-- extensionParam + extension [T] // <-- leadingTyParamss + (using a: A, b: B)(using c: C) // <-- leadingUsing + (x: X) // <-- leftParamss (using d: D) // <-- trailingUsing - def +:: (y: Y)(using e: E)(z: Z) // <-- otherParams + def +:: [U] // <-- rightTyParamss + (y: Y) // <-- rightParamss + [V](using e: E)[W](z: Z) // <-- rest ``` + An extension method is treated as a right-associative operator (as in [SLS §6.12.3](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#infix-operations)) -if it has a name ending in `:` and is immediately followed by a -single parameter. In the example above, that parameter is `(y: Y)`. +if it has a name ending in `:`, and is immediately followed by a +single explicit term parameter (in other words, `rightParamss` is present). In the example above, that parameter is `(y: Y)`. The Scala compiler pre-processes a right-associative infix operation such as `x +: xs` to `xs.+:(x)` if `x` is a pure expression or a call-by-name parameter and to `val y = x; xs.+:(y)` otherwise. This is necessary since a regular right-associative infix method is defined in the class of its right operand. To make up for this swap, -the expansion of right-associative extension methods performs an analogous parameter swap. More precisely, if `otherParams` consists of a single parameter -`rightParam` followed by `remaining`, the total parameter sequence +the expansion of right-associative extension methods performs the inverse parameter swap. More precisely, if `rightParamss` is present, the total parameter sequence of the extension method's expansion is: ``` - leadingUsing rightParam trailingUsing extensionParam remaining + leadingTyParamss leadingUsing rightTyParamss rightParamss leftParamss trailingUsing rest ``` For instance, the `+::` method above would become ```scala - def +:: (using a: A, b: B)(using c: C) + def +:: [T] + (using a: A, b: B)(using c: C) + [U] (y: Y) - (using d: D) (x: X) - (using e: E)(z: Z) + (using d: D) + [V](using e: E)[W](z: Z) ``` This expansion has to be kept in mind when writing right-associative extension From 4ffe1dad952b7615f00e7457e4e1f0b87e035159 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 2 Feb 2023 16:18:16 +0100 Subject: [PATCH 088/657] Uniformise naming of clauses in right associative extension methods --- .../src/dotty/tools/dotc/ast/Desugar.scala | 12 ++++---- .../tools/dotc/printing/RefinedPrinter.scala | 29 ++++++++++--------- .../right-associative-extension-methods.md | 26 ++++++++++------- .../scaladoc/tasty/ClassLikeSupport.scala | 15 +++++----- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 5326361ada98..2cfd292fd6dd 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -915,16 +915,16 @@ object desugar { name = normalizeName(mdef, mdef.tpt).asTermName, paramss = if mdef.name.isRightAssocOperatorName then - val (typaramss, paramss) = mdef.paramss.span(isTypeParamClause) // first extract type parameters + val (rightTyParams, paramss) = mdef.paramss.span(isTypeParamClause) // first extract type parameters paramss match - case params :: paramss1 => // `params` must have a single parameter and without `given` flag + case rightParam :: paramss1 => // `rightParam` must have a single parameter and without `given` flag def badRightAssoc(problem: String) = report.error(em"right-associative extension method $problem", mdef.srcPos) extParamss ++ mdef.paramss - params match + rightParam match case ValDefs(vparam :: Nil) => if !vparam.mods.is(Given) then // we merge the extension parameters with the method parameters, @@ -934,8 +934,10 @@ object desugar { // def %:[E](f: F)(g: G)(using H): Res = ??? // will be encoded as // def %:[A](using B)[E](f: F)(c: C)(using D)(g: G)(using H): Res = ??? - val (leadingUsing, otherExtParamss) = extParamss.span(isUsingOrTypeParamClause) - leadingUsing ::: typaramss ::: params :: otherExtParamss ::: paramss1 + // + // If you change the names of the clauses below, also change them in right-associative-extension-methods.md + val (leftTyParamsAndLeadingUsing, leftParamAndTrailingUsing) = extParamss.span(isUsingOrTypeParamClause) + leftTyParamsAndLeadingUsing ::: rightTyParams ::: rightParam :: leftParamAndTrailingUsing ::: paramss1 else badRightAssoc("cannot start with using clause") case _ => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index f94ff68d0698..6f258fdddbe9 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -896,30 +896,31 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if isExtension then val paramss = if tree.name.isRightAssocOperatorName then + // If you change the names of the clauses below, also change them in right-associative-extension-methods.md // we have the following encoding of tree.paramss: - // (leadingTyParamss ++ leadingUsing - // ++ rightTyParamss ++ rightParamss - // ++ leftParamss ++ trailingUsing ++ rest) + // (leftTyParams ++ leadingUsing + // ++ rightTyParams ++ rightParam + // ++ leftParam ++ trailingUsing ++ rest) // e.g. // extension [A](using B)(c: C)(using D) // def %:[E](f: F)(g: G)(using H): Res = ??? // will have the following values: - // - leadingTyParamss = List(`[A]`) + // - leftTyParams = List(`[A]`) // - leadingUsing = List(`(using B)`) - // - rightTyParamss = List(`[E]`) - // - rightParamss = List(`(f: F)`) - // - leftParamss = List(`(c: C)`) + // - rightTyParams = List(`[E]`) + // - rightParam = List(`(f: F)`) + // - leftParam = List(`(c: C)`) // - trailingUsing = List(`(using D)`) // - rest = List(`(g: G)`, `(using H)`) - // we need to swap (rightTyParams ++ rightParamss) with (leftParamss ++ trailingUsing) - val (leadingTyParamss, rest1) = tree.paramss.span(isTypeParamClause) + // we need to swap (rightTyParams ++ rightParam) with (leftParam ++ trailingUsing) + val (leftTyParams, rest1) = tree.paramss.span(isTypeParamClause) val (leadingUsing, rest2) = rest1.span(isUsingClause) - val (rightTyParamss, rest3) = rest2.span(isTypeParamClause) - val (rightParamss, rest4) = rest3.splitAt(1) - val (leftParamss, rest5) = rest4.splitAt(1) + val (rightTyParams, rest3) = rest2.span(isTypeParamClause) + val (rightParam, rest4) = rest3.splitAt(1) + val (leftParam, rest5) = rest4.splitAt(1) val (trailingUsing, rest6) = rest5.span(isUsingClause) - if leftParamss.nonEmpty then - leadingTyParamss ::: leadingUsing ::: leftParamss ::: trailingUsing ::: rightTyParamss ::: rightParamss ::: rest6 + if leftParam.nonEmpty then + leftTyParams ::: leadingUsing ::: leftParam ::: trailingUsing ::: rightTyParams ::: rightParam ::: rest6 else tree.paramss // it wasn't a binary operator, after all. else diff --git a/docs/_docs/reference/contextual/right-associative-extension-methods.md b/docs/_docs/reference/contextual/right-associative-extension-methods.md index 9d711c42ad3d..1b878ca2db22 100644 --- a/docs/_docs/reference/contextual/right-associative-extension-methods.md +++ b/docs/_docs/reference/contextual/right-associative-extension-methods.md @@ -4,25 +4,27 @@ title: "Right-Associative Extension Methods: Details" nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/right-associative-extension-methods.html --- + + The most general signature an extension method can have is as follows: - - An optional type clause `leadingTyParamss` + - An optional type clause `leftTyParams` - A possibly empty list of using clauses `leadingUsing` - - A single parameter `leftParamss` + - A single parameter `leftParam` (in an explicit term clause) - A possibly empty list of using clauses `trailingUsing` - A name (preceded by the `def` keyword) - - An optional type clause `rightTyParamss` - - An optional explicit term clause `rightParamss` + - An optional type clause `rightTyParams` + - An optional single parameter `rightParam` (in an explicit term clause) - Any number of any clauses `rest` For example: ```scala - extension [T] // <-- leadingTyParamss + extension [T] // <-- leftTyParams (using a: A, b: B)(using c: C) // <-- leadingUsing - (x: X) // <-- leftParamss + (x: X) // <-- leftParam (using d: D) // <-- trailingUsing - def +:: [U] // <-- rightTyParamss - (y: Y) // <-- rightParamss + def +:: [U] // <-- rightTyParams + (y: Y) // <-- rightParam [V](using e: E)[W](z: Z) // <-- rest ``` @@ -30,18 +32,20 @@ For example: An extension method is treated as a right-associative operator (as in [SLS §6.12.3](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#infix-operations)) if it has a name ending in `:`, and is immediately followed by a -single explicit term parameter (in other words, `rightParamss` is present). In the example above, that parameter is `(y: Y)`. +single explicit term parameter (in other words, `rightParam` is present). In the example above, that parameter is `(y: Y)`. The Scala compiler pre-processes a right-associative infix operation such as `x +: xs` to `xs.+:(x)` if `x` is a pure expression or a call-by-name parameter and to `val y = x; xs.+:(y)` otherwise. This is necessary since a regular right-associative infix method is defined in the class of its right operand. To make up for this swap, -the expansion of right-associative extension methods performs the inverse parameter swap. More precisely, if `rightParamss` is present, the total parameter sequence +the expansion of right-associative extension methods performs the inverse parameter swap. More precisely, if `rightParam` is present, the total parameter sequence of the extension method's expansion is: ``` - leadingTyParamss leadingUsing rightTyParamss rightParamss leftParamss trailingUsing rest + leftTyParams leadingUsing rightTyParams rightParam leftParam trailingUsing rest ``` +In other words, we swap `leftParams trailingUsing` with `rightTyParam rightParam`. + For instance, the `+::` method above would become ```scala diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index c7178acac312..0c85dff0879f 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -350,16 +350,17 @@ trait ClassLikeSupport: val unshuffledMemberInfoParamLists = if methodSymbol.isExtensionMethod && methodSymbol.isRightAssoc then // Taken from RefinedPrinter.scala - val (leadingTyParamss, rest1) = memberInfo.paramLists.span(_.isType) + // If you change the names of the clauses below, also change them in right-associative-extension-methods.md + val (leftTyParams, rest1) = memberInfo.paramLists.span(_.isType) val (leadingUsing, rest2) = rest1.span(_.isUsing) - val (rightTyParamss, rest3) = rest2.span(_.isType) - val (rightParamss, rest4) = rest3.splitAt(1) - val (leftParamss, rest5) = rest4.splitAt(1) + val (rightTyParams, rest3) = rest2.span(_.isType) + val (rightParam, rest4) = rest3.splitAt(1) + val (leftParam, rest5) = rest4.splitAt(1) val (trailingUsing, rest6) = rest5.span(_.isUsing) - if leftParamss.nonEmpty then - // leadingTyParamss ::: leadingUsing ::: leftParamss ::: trailingUsing ::: rightTyParamss ::: rightParamss ::: rest6 + if leftParam.nonEmpty then + // leftTyParams ::: leadingUsing ::: leftParam ::: trailingUsing ::: rightTyParams ::: rightParam ::: rest6 // because of takeRight after, this is equivalent to the following: - rightTyParamss ::: rightParamss ::: rest6 + rightTyParams ::: rightParam ::: rest6 else memberInfo.paramLists // it wasn't a binary operator, after all. else From 6ace5b816a1845993b0f002067e65d98882667ee Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 25 Aug 2022 15:04:36 +0200 Subject: [PATCH 089/657] Make clause interleaving experimental --- compiler/src/dotty/tools/dotc/config/Feature.scala | 3 +++ compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 12 +++++++++++- .../right-associative-extension-methods.md | 4 ++-- .../generalized-method-syntax.md | 8 +++++++- docs/_docs/reference/syntax.md | 6 +----- .../src/scala/runtime/stdLibPatches/language.scala | 8 ++++++++ project/MiMaFilters.scala | 11 ++++++++--- 7 files changed, 40 insertions(+), 12 deletions(-) rename docs/_docs/reference/{other-new-features => experimental}/generalized-method-syntax.md (92%) diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 188526bb094f..419ed5868cbf 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -28,6 +28,7 @@ object Feature: val symbolLiterals = deprecated("symbolLiterals") val fewerBraces = experimental("fewerBraces") val saferExceptions = experimental("saferExceptions") + val clauseInterleaving = experimental("clauseInterleaving") val pureFunctions = experimental("pureFunctions") val captureChecking = experimental("captureChecking") val into = experimental("into") @@ -76,6 +77,8 @@ object Feature: def namedTypeArgsEnabled(using Context) = enabled(namedTypeArguments) + def clauseInterleavingEnabled(using Context) = enabled(clauseInterleaving) + def genericNumberLiteralsEnabled(using Context) = enabled(genericNumberLiterals) def scala2ExperimentalMacroEnabled(using Context) = enabled(scala2macros) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index bac5d4e94d99..af4d0220162b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3627,8 +3627,18 @@ object Parsers { val mods1 = addFlag(mods, Method) val ident = termIdent() var name = ident.name.asTermName - val paramss = typeOrTermParamClauses(ParamOwner.Def, numLeadParams = numLeadParams) + val paramss = + if in.featureEnabled(Feature.clauseInterleaving) then + // If you are making interleaving stable manually, please refer to the PR introducing it instead, section "How to make non-experimental" + typeOrTermParamClauses(ParamOwner.Def, numLeadParams = numLeadParams) + else + val tparams = typeParamClauseOpt(ParamOwner.Def) + val vparamss = termParamClauses(numLeadParams = numLeadParams) + + joinParams(tparams, vparamss) + var tpt = fromWithinReturnType { typedOpt() } + if (migrateTo3) newLineOptWhenFollowedBy(LBRACE) val rhs = if in.token == EQUALS then diff --git a/docs/_docs/reference/contextual/right-associative-extension-methods.md b/docs/_docs/reference/contextual/right-associative-extension-methods.md index 1b878ca2db22..61f0beece6ed 100644 --- a/docs/_docs/reference/contextual/right-associative-extension-methods.md +++ b/docs/_docs/reference/contextual/right-associative-extension-methods.md @@ -25,7 +25,7 @@ For example: (using d: D) // <-- trailingUsing def +:: [U] // <-- rightTyParams (y: Y) // <-- rightParam - [V](using e: E)[W](z: Z) // <-- rest + (using e: E)(z: Z) // <-- rest ``` @@ -55,7 +55,7 @@ For instance, the `+::` method above would become (y: Y) (x: X) (using d: D) - [V](using e: E)[W](z: Z) + (using e: E)(z: Z) ``` This expansion has to be kept in mind when writing right-associative extension diff --git a/docs/_docs/reference/other-new-features/generalized-method-syntax.md b/docs/_docs/reference/experimental/generalized-method-syntax.md similarity index 92% rename from docs/_docs/reference/other-new-features/generalized-method-syntax.md rename to docs/_docs/reference/experimental/generalized-method-syntax.md index c78a5df8a3f4..072052c1ae10 100644 --- a/docs/_docs/reference/other-new-features/generalized-method-syntax.md +++ b/docs/_docs/reference/experimental/generalized-method-syntax.md @@ -1,9 +1,15 @@ --- layout: doc-page title: "Generalized Method Syntax" -movedTo: https://docs.scala-lang.org/scala3/reference/other-new-features/generalized-method-syntax.html +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/generalized-method-syntax.html --- +This feature is not yet part of the Scala 3 language definition. It can be made available by a language import: + +```scala +import scala.language.experimental.clauseInterleaving +``` + The inclusion of using clauses is not the only way in which methods have been updated, type parameter clauses are now allowed in any number and at any position. ## Syntax Changes diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index dde70d2131ad..8da41c7e6d0c 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -350,10 +350,6 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent -DefParamClause ::= DefTypeParamClause - | DefTermParamClause - | UsingParamClause TypelessClauses ::= TypelessClause {TypelessClause} TypelessClause ::= DefTermParamClause | UsingParamClause @@ -417,7 +413,7 @@ Dcl ::= RefineDcl ValDcl ::= ids ‘:’ Type VarDcl ::= ids ‘:’ Type DefDcl ::= DefSig ‘:’ Type -DefSig ::= id [DefParamClauses] [DefImplicitClause] +DefSig ::= id [DefTypeParamClause] [TypelessClauses] [DefImplicitClause] TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds Def ::= ‘val’ PatDef diff --git a/library/src/scala/runtime/stdLibPatches/language.scala b/library/src/scala/runtime/stdLibPatches/language.scala index 401926dbab4d..d92495c6f5aa 100644 --- a/library/src/scala/runtime/stdLibPatches/language.scala +++ b/library/src/scala/runtime/stdLibPatches/language.scala @@ -61,6 +61,14 @@ object language: @compileTimeOnly("`saferExceptions` can only be used at compile time in import statements") object saferExceptions + /** Adds support for clause interleaving: + * Methods can now have as many type clauses as they like, this allows to have type bounds depend on terms: `def f(x: Int)[A <: x.type]: A` + * + * @see [[http://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html]] + */ + @compileTimeOnly("`clauseInterleaving` can only be used at compile time in import statements") + object clauseInterleaving + /** Experimental support for pure function type syntax * * @see [[https://dotty.epfl.ch/docs/reference/experimental/purefuns]] diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 63ce926355bb..0937777595ca 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -12,12 +12,17 @@ object MiMaFilters { ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.3.3"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$3$u002E3$"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$3$u002E3$minusmigration$"), - ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.into"), - ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$into$"), ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary"), ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$"), ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Break"), - ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Label") + ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Label"), + + // New experimental features in 3.3.X + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.clauseInterleaving"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$clauseInterleaving$"), + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.into"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$into$"), + // end of New experimental features in 3.3.X ) val TastyCore: Seq[ProblemFilter] = Seq( ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.TastyBuffer.reset"), From 1e402f3e44310dab4699fcbacbf6afc1eefb261c Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 25 Aug 2022 15:05:32 +0200 Subject: [PATCH 090/657] Add import to tests --- .../src/tests/extensionParams.scala | 5 +++ .../src/tests/methodsAndConstructors.scala | 3 ++ tests/neg/interleaving-ab.scala | 2 ++ tests/neg/interleaving-params.scala | 2 ++ .../neg/interleaving-signatureCollision.scala | 2 ++ tests/neg/interleaving-typeApply.check | 36 +++++++++---------- tests/neg/interleaving-typeApply.scala | 2 ++ tests/neg/interleaving-unmatched.scala | 2 ++ tests/neg/namedTypeParams.check | 8 ++--- tests/neg/namedTypeParams.scala | 1 + tests/neg/overrides.scala | 14 +++++--- tests/pos/interleaving-ba.scala | 1 + tests/pos/interleaving-chainedParams.scala | 2 ++ tests/pos/interleaving-classless.scala | 2 ++ tests/pos/interleaving-functor.scala | 2 ++ tests/pos/interleaving-newline.scala | 2 ++ tests/pos/interleaving-overload.scala | 1 + tests/pos/interleaving-params.scala | 2 ++ .../pos/interleaving-signatureCollision.scala | 1 + tests/pos/interleaving-typeApply.scala | 3 ++ tests/pos/namedTypeParams.scala | 4 +++ tests/pos/overrides.scala | 7 +++- tests/run/interleaving.scala | 1 + 23 files changed, 78 insertions(+), 27 deletions(-) diff --git a/scaladoc-testcases/src/tests/extensionParams.scala b/scaladoc-testcases/src/tests/extensionParams.scala index e064ff6d56d7..9276bf41f067 100644 --- a/scaladoc-testcases/src/tests/extensionParams.scala +++ b/scaladoc-testcases/src/tests/extensionParams.scala @@ -52,7 +52,12 @@ extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Num extension (using String)(using Unit)(a: Animal)(using Int)(using Number) def f11(b: Any)(c: Any): Any = ??? + +import scala.language.experimental.clauseInterleaving + +extension (using String)(using Unit)(a: Animal)(using Int)(using Number) def f13(b: Any)[T](c: T): T = ??? def f14[D](b: D)[T](c: T): T = ??? + diff --git a/scaladoc-testcases/src/tests/methodsAndConstructors.scala b/scaladoc-testcases/src/tests/methodsAndConstructors.scala index 5fc6af38888b..132d35035b30 100644 --- a/scaladoc-testcases/src/tests/methodsAndConstructors.scala +++ b/scaladoc-testcases/src/tests/methodsAndConstructors.scala @@ -60,5 +60,8 @@ class Methods: def withImplicitParam2(v: String)(implicit ab: Double, a: Int, b: String): String = ??? + import scala.language.experimental.clauseInterleaving + def clauseInterleaving[T](x: T)[U](y: U)(using (T, U)): (T, U) = ??? + diff --git a/tests/neg/interleaving-ab.scala b/tests/neg/interleaving-ab.scala index 5516d9b9dd51..e446626a2982 100644 --- a/tests/neg/interleaving-ab.scala +++ b/tests/neg/interleaving-ab.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object Ab: given String = "" given Double = 0 diff --git a/tests/neg/interleaving-params.scala b/tests/neg/interleaving-params.scala index 97de1e6e2948..dc6762cf0214 100644 --- a/tests/neg/interleaving-params.scala +++ b/tests/neg/interleaving-params.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + class Params{ def bar[T](x: T)[T]: String = ??? // error def zoo(x: Int)[T, U](x: U): T = ??? // error diff --git a/tests/neg/interleaving-signatureCollision.scala b/tests/neg/interleaving-signatureCollision.scala index d2df384dc1e7..a6a729ed3b62 100644 --- a/tests/neg/interleaving-signatureCollision.scala +++ b/tests/neg/interleaving-signatureCollision.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object signatureCollision: def f[T](x: T)[U](y: U) = (x,y) def f[T](x: T, y: T) = (x,y) // error diff --git a/tests/neg/interleaving-typeApply.check b/tests/neg/interleaving-typeApply.check index c932a899dfe6..a50c1455bfbb 100644 --- a/tests/neg/interleaving-typeApply.check +++ b/tests/neg/interleaving-typeApply.check @@ -1,29 +1,29 @@ --- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:8:11 --------------------------------------------- -8 | f3[String]() // error - | ^ - | Type argument String does not conform to upper bound Int - | - | longer explanation available when compiling with `-explain` --- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:9:16 --------------------------------------------- -9 | f5[Int][Unit] // error - | ^ - | Type argument Unit does not conform to upper bound String - | - | longer explanation available when compiling with `-explain` --- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:10:19 -------------------------------------------- -10 | f5[String][Unit] // error // error +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:10:11 -------------------------------------------- +10 | f3[String]() // error + | ^ + | Type argument String does not conform to upper bound Int + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:11:16 -------------------------------------------- +11 | f5[Int][Unit] // error + | ^ + | Type argument Unit does not conform to upper bound String + | + | longer explanation available when compiling with `-explain` +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:12:19 -------------------------------------------- +12 | f5[String][Unit] // error // error | ^ | Type argument Unit does not conform to upper bound String | | longer explanation available when compiling with `-explain` --- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:10:11 -------------------------------------------- -10 | f5[String][Unit] // error // error +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:12:11 -------------------------------------------- +12 | f5[String][Unit] // error // error | ^ | Type argument String does not conform to upper bound Int | | longer explanation available when compiling with `-explain` --- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:11:11 -------------------------------------------- -11 | f7[String]()[Unit] // error +-- [E057] Type Mismatch Error: tests/neg/interleaving-typeApply.scala:13:11 -------------------------------------------- +13 | f7[String]()[Unit] // error | ^ | Type argument String does not conform to upper bound Int | diff --git a/tests/neg/interleaving-typeApply.scala b/tests/neg/interleaving-typeApply.scala index 18a21b4984e1..ad21fe2f0329 100644 --- a/tests/neg/interleaving-typeApply.scala +++ b/tests/neg/interleaving-typeApply.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object typeApply: def f3[T <: Int](using DummyImplicit)[U <: String](): T => T = ??? diff --git a/tests/neg/interleaving-unmatched.scala b/tests/neg/interleaving-unmatched.scala index c433c0a7210a..2ce3074d07fa 100644 --- a/tests/neg/interleaving-unmatched.scala +++ b/tests/neg/interleaving-unmatched.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object unmatched: def f1[T (x: T)] = ??? // error def f2(x: Any[)T] = ??? // error // error diff --git a/tests/neg/namedTypeParams.check b/tests/neg/namedTypeParams.check index 1252f29c4e9e..3f6f9f7913e8 100644 --- a/tests/neg/namedTypeParams.check +++ b/tests/neg/namedTypeParams.check @@ -92,11 +92,11 @@ | illegal repeated type application | You might have meant something like: | Test.f[Y = String, Int] --- [E102] Syntax Error: tests/neg/namedTypeParams.scala:32:9 ----------------------------------------------------------- -32 | f2[Y = String][X = Int](1, "") // error: Y is undefined +-- [E102] Syntax Error: tests/neg/namedTypeParams.scala:33:9 ----------------------------------------------------------- +33 | f2[Y = String][X = Int](1, "") // error: Y is undefined | ^^^^^^ | Type parameter Y is undefined. Expected one of X. --- [E102] Syntax Error: tests/neg/namedTypeParams.scala:33:9 ----------------------------------------------------------- -33 | f2[Y = String](1, "") // error: Y is undefined +-- [E102] Syntax Error: tests/neg/namedTypeParams.scala:34:9 ----------------------------------------------------------- +34 | f2[Y = String](1, "") // error: Y is undefined | ^^^^^^ | Type parameter Y is undefined. Expected one of X. diff --git a/tests/neg/namedTypeParams.scala b/tests/neg/namedTypeParams.scala index 489ac1e8cdb6..53ef14188e12 100644 --- a/tests/neg/namedTypeParams.scala +++ b/tests/neg/namedTypeParams.scala @@ -27,6 +27,7 @@ object Test: object TestInterleaving: import language.experimental.namedTypeArguments + import language.experimental.clauseInterleaving def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? f2[Y = String][X = Int](1, "") // error: Y is undefined diff --git a/tests/neg/overrides.scala b/tests/neg/overrides.scala index 48dbd41ab839..c8fc8de97f7c 100644 --- a/tests/neg/overrides.scala +++ b/tests/neg/overrides.scala @@ -39,10 +39,12 @@ package p2 { // all being in the same package compiles fine class A[T] { def f(x: T)(y: T = x) = y - def b[U <: T](x: Int)[V >: T](y: String) = false def next: T = ??? + import scala.language.experimental.clauseInterleaving + + def b[U <: T](x: Int)[V >: T](y: String) = false } class B extends A[Int] { @@ -50,19 +52,23 @@ class B extends A[Int] { def f(x: Int)(y: Int) = y // error: needs `override' modifier f(2)() - override def b[T <: Int](x: Int)(y: String) = true // error override def next(): Int = ??? // error: incompatible type + + import scala.language.experimental.clauseInterleaving + + override def b[T <: Int](x: Int)(y: String) = true // error } class C extends A[String] { override def f(x: String) = x // error - override def b[T <: String](x: Int)[U >: Int](y: String) = true // error: incompatible type - override def next: Int = ??? // error: incompatible type + import scala.language.experimental.clauseInterleaving + + override def b[T <: String](x: Int)[U >: Int](y: String) = true // error: incompatible type } class X { diff --git a/tests/pos/interleaving-ba.scala b/tests/pos/interleaving-ba.scala index 4a7d721c804e..69fe2d9537a0 100644 --- a/tests/pos/interleaving-ba.scala +++ b/tests/pos/interleaving-ba.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.clauseInterleaving object BA { given String = "" diff --git a/tests/pos/interleaving-chainedParams.scala b/tests/pos/interleaving-chainedParams.scala index 8613120f581a..e502888d97c8 100644 --- a/tests/pos/interleaving-chainedParams.scala +++ b/tests/pos/interleaving-chainedParams.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object chainedParams{ trait Chain{ diff --git a/tests/pos/interleaving-classless.scala b/tests/pos/interleaving-classless.scala index 924202673029..5aec92db3409 100644 --- a/tests/pos/interleaving-classless.scala +++ b/tests/pos/interleaving-classless.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + def f1[T]()[U](x: T, y: U): (T, U) = (x, y) def f2[T](x: T)[U](y: U): (T, U) = (x, y) def f3[T, U](using DummyImplicit)[V](x: T): U = ??? diff --git a/tests/pos/interleaving-functor.scala b/tests/pos/interleaving-functor.scala index c3df1f869850..35bed59f77f0 100644 --- a/tests/pos/interleaving-functor.scala +++ b/tests/pos/interleaving-functor.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object functorInterleaving: //taken from https://dotty.epfl.ch/docs/reference/contextual/type-classes.html //at version 3.1.1-RC1-bin-20210930-01f040b-NIGHTLY diff --git a/tests/pos/interleaving-newline.scala b/tests/pos/interleaving-newline.scala index dbd30bca4ef7..de8fb98a2f81 100644 --- a/tests/pos/interleaving-newline.scala +++ b/tests/pos/interleaving-newline.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object newline { def multipleLines [T] diff --git a/tests/pos/interleaving-overload.scala b/tests/pos/interleaving-overload.scala index e1c3db1abe37..1902551f9036 100644 --- a/tests/pos/interleaving-overload.scala +++ b/tests/pos/interleaving-overload.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.clauseInterleaving class A{ diff --git a/tests/pos/interleaving-params.scala b/tests/pos/interleaving-params.scala index e86f856a728d..36963ff2e123 100644 --- a/tests/pos/interleaving-params.scala +++ b/tests/pos/interleaving-params.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + class Params{ type U def foo[T](x: T)[U >: x.type <: T](using U)[L <: List[U]](l: L): L = ??? diff --git a/tests/pos/interleaving-signatureCollision.scala b/tests/pos/interleaving-signatureCollision.scala index be016e7bdbfe..77190284ae6d 100644 --- a/tests/pos/interleaving-signatureCollision.scala +++ b/tests/pos/interleaving-signatureCollision.scala @@ -1,3 +1,4 @@ +import scala.language.experimental.clauseInterleaving import scala.annotation.targetName object signatureCollision: diff --git a/tests/pos/interleaving-typeApply.scala b/tests/pos/interleaving-typeApply.scala index ba9486408adb..3c669cc76bfc 100644 --- a/tests/pos/interleaving-typeApply.scala +++ b/tests/pos/interleaving-typeApply.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.clauseInterleaving + object typeApply: def f0[T]: [U] => T => T = ??? @@ -10,6 +12,7 @@ object typeApply: def f7[T <: Int](using DummyImplicit)[U <: String]()[X <: Unit]: X => X = ??? @main def test = { + import scala.language.experimental.namedTypeArguments f0[Int][String] f1[Int][String] f2[Int][String]() diff --git a/tests/pos/namedTypeParams.scala b/tests/pos/namedTypeParams.scala index 160bfc11f079..388bcfa98bef 100644 --- a/tests/pos/namedTypeParams.scala +++ b/tests/pos/namedTypeParams.scala @@ -1,4 +1,5 @@ import language.experimental.namedTypeArguments + object Test { def f[X, Y](x: X, y: Y): Int = ??? @@ -7,7 +8,10 @@ object Test { f[X = Int, Y = String](1, "") f[X = Int](1, "") f[Y = String](1, "") +} +object TestInterleaving{ + import language.experimental.clauseInterleaving def f2[X](using DummyImplicit)[Y](x: X, y: Y): Int = ??? f2[X = Int][Y = String](1, "") diff --git a/tests/pos/overrides.scala b/tests/pos/overrides.scala index 0f5d12367ca2..146dc06c76a9 100644 --- a/tests/pos/overrides.scala +++ b/tests/pos/overrides.scala @@ -1,16 +1,21 @@ class A[T] { def f(x: T)(y: T = x) = y + + import scala.language.experimental.clauseInterleaving + def b[U <: T](x: Int)[V >: T](y: String) = false } - class B extends A[Int] { override def f(x: Int)(y: Int) = y f(2)() + + import scala.language.experimental.clauseInterleaving + override def b[T <: Int](x: Int)[U >: Int](y: String) = true } diff --git a/tests/run/interleaving.scala b/tests/run/interleaving.scala index e84f2896e5af..557741032e8a 100644 --- a/tests/run/interleaving.scala +++ b/tests/run/interleaving.scala @@ -1,4 +1,5 @@ object Test extends App { + import scala.language.experimental.clauseInterleaving trait Key { type Value } trait DB { def getOrElse(k: Key)[V >: k.Value](default: V): V // dependent type parameter From 65091c31a625d9abb1d12942e2f870be8a8ae094 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 3 Jan 2023 14:21:44 +0100 Subject: [PATCH 091/657] Remove unused methods Not fixup'ed with another commit to keep a trace of the original version of these methods. --- .../dotty/tools/dotc/parsing/Parsers.scala | 31 ++----------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index af4d0220162b..479ae1fa9095 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3079,35 +3079,13 @@ object Parsers { /* -------- PARAMETERS ------------------------------------------- */ - /** DefParamClause ::= DefTypeParamClause - * | DefTermParamClause - * | UsingParamClause - */ - def typeOrTermParamClause( - ownerKind: ParamOwner, - nparams: Int, // number of parameters preceding this clause - ofClass: Boolean = false, // owner is a class - ofCaseClass: Boolean = false, // owner is a case class - prefix: Boolean = false, // clause precedes name of an extension method - givenOnly: Boolean = false, // only given parameters allowed - firstClause: Boolean = false // clause is the first in regular list of clauses - ): List[TypeDef] | List[ValDef] = - if (in.token == LPAREN) - termParamClause(nparams, ofClass, ofCaseClass, prefix, givenOnly, firstClause) - else if (in.token == LBRACKET) - typeParamClause(ownerKind) - else - Nil - - end typeOrTermParamClause - /** DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent + * DefParamClause ::= DefTypeParamClause + * | DefTermParamClause + * | UsingParamClause */ def typeOrTermParamClauses( ownerKind: ParamOwner, - ofClass: Boolean = false, - ofCaseClass: Boolean = false, - givenOnly: Boolean = false, numLeadParams: Int = 0 ): List[List[TypeDef] | List[ValDef]] = @@ -3118,9 +3096,6 @@ object Parsers { val paramsStart = in.offset val params = termParamClause( numLeadParams, - ofClass = ofClass, - ofCaseClass = ofCaseClass, - givenOnly = givenOnly, firstClause = firstClause) val lastClause = params.nonEmpty && params.head.mods.flags.is(Implicit) params :: ( From ff3fab2929fbe20e52df790be18aaa2dcc2a4310 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 3 Feb 2023 12:34:32 +0100 Subject: [PATCH 092/657] Perform Matchable check only if type test is needed Fixes #16808 --- .../src/dotty/tools/dotc/typer/Applications.scala | 11 +++++------ compiler/src/dotty/tools/dotc/typer/Checking.scala | 1 - compiler/test/dotty/tools/dotc/CompilationTests.scala | 4 +--- .../strict}/adhoc-extension/A.scala | 0 .../strict}/adhoc-extension/B.scala | 0 tests/pos-custom-args/{ => strict}/i10383.scala | 0 tests/pos-custom-args/strict/i16808.scala | 2 ++ .../strict}/i7296.scala | 0 8 files changed, 8 insertions(+), 10 deletions(-) rename tests/{pos-special => pos-custom-args/strict}/adhoc-extension/A.scala (100%) rename tests/{pos-special => pos-custom-args/strict}/adhoc-extension/B.scala (100%) rename tests/pos-custom-args/{ => strict}/i10383.scala (100%) create mode 100644 tests/pos-custom-args/strict/i16808.scala rename tests/{pos-special => pos-custom-args/strict}/i7296.scala (100%) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index cd33fe9cef24..d1ca562a5aba 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1258,8 +1258,6 @@ trait Applications extends Compatibility { def typedUnApply(tree: untpd.Apply, selType: Type)(using Context): Tree = { record("typedUnApply") val Apply(qual, args) = tree - if !ctx.mode.is(Mode.InTypeTest) then - checkMatchable(selType, tree.srcPos, pattern = true) def notAnExtractor(tree: Tree): Tree = // prefer inner errors @@ -1398,12 +1396,13 @@ trait Applications extends Compatibility { val unapplyArgType = mt.paramInfos.head unapp.println(i"unapp arg tpe = $unapplyArgType, pt = $selType") val ownType = - if (selType <:< unapplyArgType) { + if selType <:< unapplyArgType then unapp.println(i"case 1 $unapplyArgType ${ctx.typerState.constraint}") fullyDefinedType(unapplyArgType, "pattern selector", tree.srcPos) selType.dropAnnot(defn.UncheckedAnnot) // need to drop @unchecked. Just because the selector is @unchecked, the pattern isn't. - } - else { + else + if !ctx.mode.is(Mode.InTypeTest) then + checkMatchable(selType, tree.srcPos, pattern = true) // We ignore whether constraining the pattern succeeded. // Constraining only fails if the pattern cannot possibly match, // but useless pattern checks detect more such cases, so we simply rely on them instead. @@ -1412,7 +1411,7 @@ trait Applications extends Compatibility { if (patternBound.nonEmpty) unapplyFn = addBinders(unapplyFn, patternBound) unapp.println(i"case 2 $unapplyArgType ${ctx.typerState.constraint}") unapplyArgType - } + val dummyArg = dummyTreeOfType(ownType) val unapplyApp = typedExpr(untpd.TypedSplice(Apply(unapplyFn, dummyArg :: Nil))) def unapplyImplicits(unapp: Tree): List[Tree] = { diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 817fe6f21d24..bff9310dee88 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1461,7 +1461,6 @@ trait Checking { def checkMatchable(tp: Type, pos: SrcPos, pattern: Boolean)(using Context): Unit = if !tp.derivesFrom(defn.MatchableClass) && sourceVersion.isAtLeast(`future-migration`) then - val kind = if pattern then "pattern selector" else "value" report.warning(MatchableWarning(tp, pattern), pos) /** Check that there is an implicit capability to throw a checked exception diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 2fdebef3f5db..dee65e404da9 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -48,6 +48,7 @@ class CompilationTests { compileFilesInDir("tests/pos", defaultOptions.and("-Ysafe-init", "-Ylegacy-lazy-vals", "-Ycheck-constraint-deps"), FileFilter.include(TestSources.posLazyValsAllowlist)), compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes), compileFilesInDir("tests/pos-custom-args/no-experimental", defaultOptions.and("-Yno-experimental")), + compileFilesInDir("tests/pos-custom-args/strict", defaultOptions.and("-source", "future", "-deprecation", "-Xfatal-warnings")), compileDir("tests/pos-special/java-param-names", defaultOptions.withJavacOnlyOptions("-parameters")), compileFile( // succeeds despite -Xfatal-warnings because of -nowarn @@ -55,8 +56,6 @@ class CompilationTests { defaultOptions.and("-nowarn", "-Xfatal-warnings") ), compileFile("tests/pos-special/typeclass-scaling.scala", defaultOptions.and("-Xmax-inlines", "40")), - compileFile("tests/pos-special/i7296.scala", defaultOptions.and("-source", "future", "-deprecation", "-Xfatal-warnings")), - compileDir("tests/pos-special/adhoc-extension", defaultOptions.and("-source", "future", "-feature", "-Xfatal-warnings")), compileFile("tests/pos-special/i7575.scala", defaultOptions.andLanguageFeature("dynamics")), compileFile("tests/pos-special/kind-projector.scala", defaultOptions.and("-Ykind-projector")), compileFile("tests/pos-special/kind-projector-underscores.scala", defaultOptions.and("-Ykind-projector:underscores")), @@ -65,7 +64,6 @@ class CompilationTests { compileFile("tests/pos-custom-args/i9267.scala", defaultOptions.and("-Ystop-after:erasure")), compileFile("tests/pos-special/extend-java-enum.scala", defaultOptions.and("-source", "3.0-migration")), compileFile("tests/pos-custom-args/help.scala", defaultOptions.and("-help", "-V", "-W", "-X", "-Y")), - compileFile("tests/pos-custom-args/i10383.scala", defaultOptions.and("-source", "future", "-deprecation", "-Xfatal-warnings")), compileFile("tests/pos-custom-args/i13044.scala", defaultOptions.and("-Xmax-inlines:33")), compileFile("tests/pos-custom-args/jdk-8-app.scala", defaultOptions.and("-release:8")), ).checkCompile() diff --git a/tests/pos-special/adhoc-extension/A.scala b/tests/pos-custom-args/strict/adhoc-extension/A.scala similarity index 100% rename from tests/pos-special/adhoc-extension/A.scala rename to tests/pos-custom-args/strict/adhoc-extension/A.scala diff --git a/tests/pos-special/adhoc-extension/B.scala b/tests/pos-custom-args/strict/adhoc-extension/B.scala similarity index 100% rename from tests/pos-special/adhoc-extension/B.scala rename to tests/pos-custom-args/strict/adhoc-extension/B.scala diff --git a/tests/pos-custom-args/i10383.scala b/tests/pos-custom-args/strict/i10383.scala similarity index 100% rename from tests/pos-custom-args/i10383.scala rename to tests/pos-custom-args/strict/i10383.scala diff --git a/tests/pos-custom-args/strict/i16808.scala b/tests/pos-custom-args/strict/i16808.scala new file mode 100644 index 000000000000..602ceed94161 --- /dev/null +++ b/tests/pos-custom-args/strict/i16808.scala @@ -0,0 +1,2 @@ +def collectKeys[A, B, C](xs: Map[A, B])(f: PartialFunction[A, C]): Map[C, B] = + xs.collect{ case (f(c) , b) => (c, b) } \ No newline at end of file diff --git a/tests/pos-special/i7296.scala b/tests/pos-custom-args/strict/i7296.scala similarity index 100% rename from tests/pos-special/i7296.scala rename to tests/pos-custom-args/strict/i7296.scala From d28641c19e52ca4059fb2ec00f89e2fc71b9937e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 2 Feb 2023 12:07:40 +0100 Subject: [PATCH 093/657] Fix #16801: Scala.js: Handle Closure's of s.r.FunctionXXL. --- compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala | 7 +++++-- .../src/scala/scalajs/runtime/AnonFunctionXXL.scala | 8 ++++++++ project/MiMaFilters.scala | 3 +++ tests/run/functionXXL.scala | 2 -- tests/run/i2004.scala | 2 -- tests/run/implicitFunctionXXL.scala | 2 -- tests/run/tupled-function-andThen.scala | 2 -- tests/run/tupled-function-apply.scala | 4 +--- tests/run/tupled-function-compose.scala | 2 -- tests/run/tupled-function-extension-method.scala | 10 ++++++---- tests/run/tupled-function-tupled.scala | 2 -- 11 files changed, 23 insertions(+), 21 deletions(-) create mode 100644 library-js/src/scala/scalajs/runtime/AnonFunctionXXL.scala diff --git a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala index 4caf1f6b5fa2..eee791852fde 100644 --- a/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -3532,13 +3532,16 @@ class JSCodeGen()(using genCtx: Context) { val closure = js.Closure(arrow = true, formalCaptures, formalParams, restParam, genBody, actualCaptures) if (!funInterfaceSym.exists || defn.isFunctionClass(funInterfaceSym)) { - assert(!funInterfaceSym.exists || defn.isFunctionClass(funInterfaceSym), - s"Invalid functional interface $funInterfaceSym reached the back-end") val formalCount = formalParams.size val cls = ClassName("scala.scalajs.runtime.AnonFunction" + formalCount) val ctorName = MethodName.constructor( jstpe.ClassRef(ClassName("scala.scalajs.js.Function" + formalCount)) :: Nil) js.New(cls, js.MethodIdent(ctorName), List(closure)) + } else if (funInterfaceSym.name == tpnme.FunctionXXL && funInterfaceSym.owner == defn.ScalaRuntimePackageClass) { + val cls = ClassName("scala.scalajs.runtime.AnonFunctionXXL") + val ctorName = MethodName.constructor( + jstpe.ClassRef(ClassName("scala.scalajs.js.Function1")) :: Nil) + js.New(cls, js.MethodIdent(ctorName), List(closure)) } else { assert(funInterfaceSym.isJSType, s"Invalid functional interface $funInterfaceSym reached the back-end") diff --git a/library-js/src/scala/scalajs/runtime/AnonFunctionXXL.scala b/library-js/src/scala/scalajs/runtime/AnonFunctionXXL.scala new file mode 100644 index 000000000000..87208573eff9 --- /dev/null +++ b/library-js/src/scala/scalajs/runtime/AnonFunctionXXL.scala @@ -0,0 +1,8 @@ +package scala.scalajs.runtime + +import scala.scalajs.js + +@inline +final class AnonFunctionXXL(f: js.Function1[IArray[Object], Object]) extends scala.runtime.FunctionXXL { + override def apply(xs: IArray[Object]): Object = f(xs) +} diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 0937777595ca..8af2de7058ee 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -17,6 +17,9 @@ object MiMaFilters { ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Break"), ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Label"), + // Scala.js only: new runtime support class in 3.2.3; not available to users + ProblemFilters.exclude[MissingClassProblem]("scala.scalajs.runtime.AnonFunctionXXL"), + // New experimental features in 3.3.X ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.clauseInterleaving"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$clauseInterleaving$"), diff --git a/tests/run/functionXXL.scala b/tests/run/functionXXL.scala index 885e7bce8be9..de8c8e3faedd 100644 --- a/tests/run/functionXXL.scala +++ b/tests/run/functionXXL.scala @@ -1,5 +1,3 @@ -// scalajs: --skip --pending - object Test { val f = (x1: Int, diff --git a/tests/run/i2004.scala b/tests/run/i2004.scala index dd829ef24b5b..8682423ef2f0 100644 --- a/tests/run/i2004.scala +++ b/tests/run/i2004.scala @@ -1,5 +1,3 @@ -// scalajs: --skip --pending - object Test { def main(args: Array[String]) = { diff --git a/tests/run/implicitFunctionXXL.scala b/tests/run/implicitFunctionXXL.scala index fcae9c135cda..b4131080577d 100644 --- a/tests/run/implicitFunctionXXL.scala +++ b/tests/run/implicitFunctionXXL.scala @@ -1,5 +1,3 @@ -// scalajs: --skip --pending - object Test { def main(args: Array[String]): Unit = { diff --git a/tests/run/tupled-function-andThen.scala b/tests/run/tupled-function-andThen.scala index a57c3613bb32..94236e9267e1 100644 --- a/tests/run/tupled-function-andThen.scala +++ b/tests/run/tupled-function-andThen.scala @@ -1,5 +1,3 @@ -// scalajs: --skip --pending - import scala.util.TupledFunction object Test { diff --git a/tests/run/tupled-function-apply.scala b/tests/run/tupled-function-apply.scala index 76f6b823df76..7d2162a565ac 100644 --- a/tests/run/tupled-function-apply.scala +++ b/tests/run/tupled-function-apply.scala @@ -1,5 +1,3 @@ -// scalajs: --skip --pending - import scala.util.TupledFunction object Test { @@ -119,4 +117,4 @@ object Test { */ extension [F, Args <: Tuple, R](f: F) def apply (args: Args)(using tf: TupledFunction[F, Args => R]): R = tf.tupled(f)(args) -} \ No newline at end of file +} diff --git a/tests/run/tupled-function-compose.scala b/tests/run/tupled-function-compose.scala index a2ca5c56771a..4cf83563274d 100644 --- a/tests/run/tupled-function-compose.scala +++ b/tests/run/tupled-function-compose.scala @@ -1,5 +1,3 @@ -// scalajs: --skip --pending - import scala.util.TupledFunction object Test { def main(args: Array[String]): Unit = { diff --git a/tests/run/tupled-function-extension-method.scala b/tests/run/tupled-function-extension-method.scala index 0185fc4eb06c..be5ccbd5ca17 100644 --- a/tests/run/tupled-function-extension-method.scala +++ b/tests/run/tupled-function-extension-method.scala @@ -1,5 +1,3 @@ -// scalajs: --skip --pending - import scala.util.TupledFunction object Test { def main(args: Array[String]): Unit = { @@ -13,7 +11,7 @@ object Test { (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25) ) - println(f0()) + portablePrintln(f0()) println(f1(1)) println(f2(1, 2)) println(f3(1, 2, 3)) @@ -34,6 +32,10 @@ object Test { } + def portablePrintln(x: Any): Unit = + if x == () then println("()") + else println(x) + class Expr[T](val x: T) // Specialized only for arity 0 and one as auto tupling will not provide the disired effect @@ -50,4 +52,4 @@ object Test { extension [F, Args <: Tuple, R](e: Expr[F]) def applyGiven (args: Args)(using tf: TupledFunction[F, Args ?=> R]): R = tf.tupled(e.x)(using args) -} \ No newline at end of file +} diff --git a/tests/run/tupled-function-tupled.scala b/tests/run/tupled-function-tupled.scala index 360e3e299770..6e7d94b3ac3d 100644 --- a/tests/run/tupled-function-tupled.scala +++ b/tests/run/tupled-function-tupled.scala @@ -1,5 +1,3 @@ -// scalajs: --skip --pending - import scala.util.TupledFunction object Test { From 917534b5588a33401ae1937416319697163c2980 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 3 Feb 2023 15:21:56 +0100 Subject: [PATCH 094/657] Update printing tests to have matching AST --- .../printing/transformed/lazy-vals-new.check | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/printing/transformed/lazy-vals-new.check b/tests/printing/transformed/lazy-vals-new.check index 406417845c20..4b81cd457a38 100644 --- a/tests/printing/transformed/lazy-vals-new.check +++ b/tests/printing/transformed/lazy-vals-new.check @@ -10,19 +10,19 @@ package { @static private def (): Unit = { A.OFFSET$_m_0 = - scala.runtime.LazyVals.getStaticFieldOffset( + scala.runtime.LazyVals.getOffsetStatic( classOf[Object {...}].getDeclaredField("x$lzy1")) () } @static @static val OFFSET$_m_0: Long = - scala.runtime.LazyVals.getStaticFieldOffset( + scala.runtime.LazyVals.getOffsetStatic( classOf[Object {...}].getDeclaredField("x$lzy1")) private def writeReplace(): Object = new scala.runtime.ModuleSerializationProxy(classOf[A]) - @volatile private lazy var x$lzy1: Object = null + @volatile private lazy var x$lzy1: Object = null lazy def x(): Int = { - val result: Object = A#x$lzy1 + val result: Object = A.x$lzy1 if result.isInstanceOf[Int] then scala.Int.unbox(result) else if result.eq(scala.runtime.LazyVals.NullValue) then scala.Int.unbox(null) else scala.Int.unbox(A.x$lzyINIT1()) @@ -30,10 +30,10 @@ package { private def x$lzyINIT1(): Object = while do { - val current: Object = A#x$lzy1 + val current: Object = A.x$lzy1 if current.eq(null) then if - scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0, null, + scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, null, scala.runtime.LazyVals.Evaluating) then { @@ -49,15 +49,15 @@ package { } finally if - scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0, + scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, scala.runtime.LazyVals.Evaluating, result).unary_!() then { val lock: scala.runtime.LazyVals.LazyVals$Waiting = - A#x$lzy1.asInstanceOf[ + A.x$lzy1.asInstanceOf[ scala.runtime.LazyVals.LazyVals$Waiting] - scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0, - lock, result) + scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, lock, + result) lock.countDown() } else () @@ -71,8 +71,8 @@ package { then if current.eq(scala.runtime.LazyVals.Evaluating) then { - scala.runtime.LazyVals.objCAS(classOf[A], A.OFFSET$_m_0, - current, new scala.runtime.LazyVals.LazyVals$Waiting()) + scala.runtime.LazyVals.objCAS(this, A.OFFSET$_m_0, current, + new scala.runtime.LazyVals.LazyVals$Waiting()) () } else From 11854bbf20d92b74fef82f19c4dcb7dd38f45f70 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 1 Feb 2023 14:48:29 +0000 Subject: [PATCH 095/657] Restrict captureWildcards to only be used if needed Rather than blindly using the newly wildcard-captured type, check that it's compatible with the proto/formal type. That way values that have wildcard types can be passed, uncast, to extension methods that don't require the capture. For instance in specs2, a value of type `Class[? <: Foo]` needn't become `Class[?1.CAP]` just so it can be applied to `def theValue[T](t: => T)`. For the zio-http case, despite knowing that JFuture is morally covariant, we don't have any way to knowing that - so we must be safe and error. --- .../dotty/tools/dotc/typer/Applications.scala | 9 +++++++-- .../dotty/tools/dotc/typer/ProtoTypes.scala | 16 +++++++--------- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/neg/t9419.zio-http.scala | 18 ++++++++++++++++++ tests/pos/t9419.specs2.scala | 13 +++++++++++++ 5 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 tests/neg/t9419.zio-http.scala create mode 100644 tests/pos/t9419.specs2.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index cd33fe9cef24..224359faaf3b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -714,8 +714,8 @@ trait Applications extends Compatibility { || argMatch == ArgMatch.CompatibleCAP && { val argtpe1 = argtpe.widen - val captured = captureWildcards(argtpe1) - (captured ne argtpe1) && isCompatible(captured, formal.widenExpr) + val captured = captureWildcardsCompat(argtpe1, formal.widenExpr) + captured ne argtpe1 } /** The type of the given argument */ @@ -2412,4 +2412,9 @@ trait Applications extends Compatibility { def isApplicableExtensionMethod(methodRef: TermRef, receiverType: Type)(using Context): Boolean = methodRef.symbol.is(ExtensionMethod) && !receiverType.isBottomType && tryApplyingExtensionMethod(methodRef, nullLiteral.asInstance(receiverType)).nonEmpty + + def captureWildcardsCompat(tp: Type, pt: Type)(using Context): Type = + val captured = captureWildcards(tp) + if (captured ne tp) && isCompatible(captured, pt) then captured + else tp } diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index d724ad1708b1..da785e32865a 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -501,15 +501,13 @@ object ProtoTypes { def checkNoWildcardCaptureForCBN(targ1: Tree)(using Context): Tree = { if hasCaptureConversionArg(targ1.tpe) then - stripCast(targ1).tpe match - case tp: AppliedType if tp.hasWildcardArg => - errorTree(targ1, - em"""argument for by-name parameter is not a value - |and contains wildcard arguments: $tp - | - |Assign it to a val and pass that instead. - |""") - case _ => targ1 + val tp = stripCast(targ1).tpe + errorTree(targ1, + em"""argument for by-name parameter is not a value + |and contains wildcard arguments: $tp + | + |Assign it to a val and pass that instead. + |""") else targ1 } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index e7f75eafed94..a87d6dd7e703 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3962,7 +3962,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer return adaptConstant(tree, ConstantType(converted)) case _ => - val captured = captureWildcards(wtp) + val captured = captureWildcardsCompat(wtp, pt) if (captured `ne` wtp) return readapt(tree.cast(captured)) diff --git a/tests/neg/t9419.zio-http.scala b/tests/neg/t9419.zio-http.scala new file mode 100644 index 000000000000..cff9ec51e6f9 --- /dev/null +++ b/tests/neg/t9419.zio-http.scala @@ -0,0 +1,18 @@ +// Minimisation of how the fix for t9419 affected zio-http +import java.util.concurrent.Future as JFuture + +trait Test: + def shutdownGracefully(): JFuture[_] + + def executedWildcard(jFuture: => JFuture[_]): Unit + def executedGeneric[A](jFuture: => JFuture[A]): Unit + def executedWildGen[A](jFuture: => JFuture[? <: A]): Unit + + // Even though JFuture is morally covariant, at least currently, + // there's no definition-side variance, so it's treated as invariant. + // So we have to be concerned that two different values of `JFuture[A]` + // with different types, blowing up together. So error in `fails`. + def works = executedWildcard(shutdownGracefully()) + def fails = executedGeneric(shutdownGracefully()) // error + def fixed = executedGeneric(shutdownGracefully().asInstanceOf[JFuture[Any]]) // fix + def best2 = executedWildGen(shutdownGracefully()) // even better, use use-site variance in the method diff --git a/tests/pos/t9419.specs2.scala b/tests/pos/t9419.specs2.scala new file mode 100644 index 000000000000..fe4a44312594 --- /dev/null +++ b/tests/pos/t9419.specs2.scala @@ -0,0 +1,13 @@ +// Minimisation of how the fix for t9419 affected specs2 +class MustExpectable[T](tm: () => T): + def must_==(other: => Any) = tm() == other + +class Foo + +object Main: + implicit def theValue[T](t: => T): MustExpectable[T] = new MustExpectable(() => t) + def main(args: Array[String]): Unit = + val cls = classOf[Foo] + val instance = new Foo() + val works = cls must_== cls + val fails = instance.getClass must_== cls From 22b49e9365f9a8e045feca1b373af2a4285129d8 Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Fri, 3 Feb 2023 20:43:05 -0500 Subject: [PATCH 096/657] Remove experimental from Mirror#fromProductTyped --- library/src/scala/deriving/Mirror.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/library/src/scala/deriving/Mirror.scala b/library/src/scala/deriving/Mirror.scala index 5de219dfe5c4..57453a516567 100644 --- a/library/src/scala/deriving/Mirror.scala +++ b/library/src/scala/deriving/Mirror.scala @@ -52,7 +52,6 @@ object Mirror { extension [T](p: ProductOf[T]) /** Create a new instance of type `T` with elements taken from product `a`. */ - @annotation.experimental def fromProductTyped[A <: scala.Product, Elems <: p.MirroredElemTypes](a: A)(using m: ProductOf[A] { type MirroredElemTypes = Elems }): T = p.fromProduct(a) From cac782a3bd47640cf1ff435f3c6df51d05dc3eba Mon Sep 17 00:00:00 2001 From: Michael Pilquist Date: Sat, 4 Feb 2023 08:42:54 -0500 Subject: [PATCH 097/657] Update experimental definitions list --- .../tasty-inspector/stdlibExperimentalDefinitions.scala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index 30e7c5af6c2a..062fa25e0ca5 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -58,10 +58,6 @@ val experimentalDefinitionInLibrary = Set( //// New feature: into "scala.annotation.allowConversions", - //// New APIs: Mirror - // Can be stabilized in 3.3.0 or later. - "scala.deriving.Mirror$.fromProductTyped", // This API is a bit convoluted. We may need some more feedback before we can stabilize it. - //// New feature: Macro annotations "scala.annotation.MacroAnnotation", From 11ef198e6d9c50bb31a01480b3877f6dbba545a5 Mon Sep 17 00:00:00 2001 From: Vasil Vasilev Date: Mon, 6 Feb 2023 12:08:05 +0100 Subject: [PATCH 098/657] Add scaladoc documentation for `ReplDriver#redirectOutput` --- compiler/src/dotty/tools/repl/ReplDriver.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 0f29591e2121..905f4f06de08 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -187,6 +187,17 @@ class ReplDriver(settings: Array[String], // TODO: i5069 final def bind(name: String, value: Any)(using state: State): State = state + /** + * Controls whether the `System.out` and `System.err` streams are set to the provided constructor parameter instance + * of [[java.io.PrintStream]] during the execution of the repl. On by default. + * + * Disabling this can be beneficial when executing a repl instance inside a concurrent environment, for example a + * thread pool (such as the Scala compile server in the Scala Plugin for IntelliJ IDEA). + * + * In such environments, indepently executing `System.setOut` and `System.setErr` without any synchronization can + * lead to unpredictable results when restoring the original streams (dependent on the order of execution), leaving + * the Java process in an inconsistent state. + */ protected def redirectOutput: Boolean = true // redirecting the output allows us to test `println` in scripted tests From 35320eb288a625a8504e6d01020e4ea64d61c904 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Mon, 6 Feb 2023 14:12:24 +0100 Subject: [PATCH 099/657] fix: ensure syntax blocks for ebnf are marked as such I see that this was done in the past in https://github.com/lampepfl/dotty/pull/14958/files, but then reverted in https://github.com/lampepfl/dotty/pull/14958. Like many commits, there really isn't an explanation of the revert, but from reading between the lines I assume the `;` was the actual issue, not the syntax highlighting. As it was pointed out, syntax.js doesn't actually support `ebnf`. They do say they support `bnf`, but that didn't really work when I was testing. Either way, this pr makes sure that we _do_ mark the snippets as `ebnf`. The reason for this isn't necessarily so that we _get_ syntax highlighting for these, but so that syntax.js doesn't infer the wrong type of syntax and provide odd highlighting like we currently have. This also helps to ensure screen readers know what type of codeblock this is. fixes #14697 --- docs/_docs/reference/changed-features/imports.md | 2 +- docs/_docs/reference/changed-features/match-syntax.md | 2 +- docs/_docs/reference/changed-features/pattern-bindings.md | 2 +- .../reference/changed-features/structural-types-spec.md | 2 +- docs/_docs/reference/changed-features/vararg-splices.md | 2 +- docs/_docs/reference/contextual/context-bounds.md | 2 +- docs/_docs/reference/contextual/context-functions-spec.md | 2 +- docs/_docs/reference/contextual/derivation.md | 2 +- docs/_docs/reference/contextual/extension-methods.md | 2 +- docs/_docs/reference/contextual/given-imports.md | 2 +- docs/_docs/reference/contextual/givens.md | 4 ++-- docs/_docs/reference/contextual/using-clauses.md | 2 +- docs/_docs/reference/enums/adts.md | 4 ++-- docs/_docs/reference/experimental/named-typeargs-spec.md | 4 ++-- docs/_docs/reference/metaprogramming/macros-spec.md | 6 +++--- docs/_docs/reference/metaprogramming/simple-smp.md | 2 +- .../reference/new-types/dependent-function-types-spec.md | 2 +- docs/_docs/reference/new-types/intersection-types-spec.md | 2 +- docs/_docs/reference/new-types/type-lambdas-spec.md | 2 +- docs/_docs/reference/other-new-features/export.md | 2 +- docs/_docs/reference/other-new-features/indentation.md | 8 ++++---- .../_docs/reference/other-new-features/opaques-details.md | 2 +- 22 files changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/_docs/reference/changed-features/imports.md b/docs/_docs/reference/changed-features/imports.md index 2058ef08b7db..b322a6a58393 100644 --- a/docs/_docs/reference/changed-features/imports.md +++ b/docs/_docs/reference/changed-features/imports.md @@ -46,7 +46,7 @@ are offered under settings `-source 3.1-migration -rewrite`. ## Syntax -``` +```ebnf Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec | SimpleRef `as` id diff --git a/docs/_docs/reference/changed-features/match-syntax.md b/docs/_docs/reference/changed-features/match-syntax.md index dba50e9beb6a..3f4d608e261f 100644 --- a/docs/_docs/reference/changed-features/match-syntax.md +++ b/docs/_docs/reference/changed-features/match-syntax.md @@ -47,7 +47,7 @@ The syntactical precedence of match expressions has been changed. The new syntax of match expressions is as follows. -``` +```ebnf InfixExpr ::= ... | InfixExpr MatchClause SimpleExpr ::= ... diff --git a/docs/_docs/reference/changed-features/pattern-bindings.md b/docs/_docs/reference/changed-features/pattern-bindings.md index 2de338fc1dde..a75d64e7cd2d 100644 --- a/docs/_docs/reference/changed-features/pattern-bindings.md +++ b/docs/_docs/reference/changed-features/pattern-bindings.md @@ -50,7 +50,7 @@ for case (x, y) <- elems yield (y, x) // returns List((2, 1), (4, 3)) ## Syntax Changes Generators in for expressions may be prefixed with `case`. -``` +```ebnf Generator ::= [‘case’] Pattern1 ‘<-’ Expr ``` diff --git a/docs/_docs/reference/changed-features/structural-types-spec.md b/docs/_docs/reference/changed-features/structural-types-spec.md index d456932649fb..18d0f31ee6fe 100644 --- a/docs/_docs/reference/changed-features/structural-types-spec.md +++ b/docs/_docs/reference/changed-features/structural-types-spec.md @@ -6,7 +6,7 @@ nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/structu ## Syntax -``` +```ebnf SimpleType ::= ... | Refinement Refinement ::= ‘{’ RefineStatSeq ‘}’ RefineStatSeq ::= RefineStat {semi RefineStat} diff --git a/docs/_docs/reference/changed-features/vararg-splices.md b/docs/_docs/reference/changed-features/vararg-splices.md index 43c4acc5f880..8f23af771216 100644 --- a/docs/_docs/reference/changed-features/vararg-splices.md +++ b/docs/_docs/reference/changed-features/vararg-splices.md @@ -24,7 +24,7 @@ The old syntax for splice arguments will be phased out. ## Syntax -``` +```ebnf ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ | ‘(’ [Patterns ‘,’] Pattern2 ‘*’ ‘)’ diff --git a/docs/_docs/reference/contextual/context-bounds.md b/docs/_docs/reference/contextual/context-bounds.md index 42479d6802b3..11d57c8cbd52 100644 --- a/docs/_docs/reference/contextual/context-bounds.md +++ b/docs/_docs/reference/contextual/context-bounds.md @@ -47,7 +47,7 @@ done automatically under `-rewrite`. ## Syntax -``` +```ebnf TypeParamBounds ::= [SubtypeBounds] {ContextBound} ContextBound ::= ‘:’ Type ``` diff --git a/docs/_docs/reference/contextual/context-functions-spec.md b/docs/_docs/reference/contextual/context-functions-spec.md index 109513e9da86..385ee3901fd8 100644 --- a/docs/_docs/reference/contextual/context-functions-spec.md +++ b/docs/_docs/reference/contextual/context-functions-spec.md @@ -6,7 +6,7 @@ nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/context-funct ## Syntax -``` +```ebnf Type ::= ... | FunArgTypes ‘?=>’ Type Expr ::= ... diff --git a/docs/_docs/reference/contextual/derivation.md b/docs/_docs/reference/contextual/derivation.md index 31040615d38c..fb8acd0fad93 100644 --- a/docs/_docs/reference/contextual/derivation.md +++ b/docs/_docs/reference/contextual/derivation.md @@ -486,7 +486,7 @@ method please read more at [How to write a type class `derived` method using mac ## Syntax -``` +```ebnf Template ::= InheritClauses [TemplateBody] EnumDef ::= id ClassConstr InheritClauses EnumBody InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] diff --git a/docs/_docs/reference/contextual/extension-methods.md b/docs/_docs/reference/contextual/extension-methods.md index d23cadf513d7..6a1504c25048 100644 --- a/docs/_docs/reference/contextual/extension-methods.md +++ b/docs/_docs/reference/contextual/extension-methods.md @@ -285,7 +285,7 @@ def position(s: String)(ch: Char, n: Int): Int = Here are the syntax changes for extension methods and collective extensions relative to the [current syntax](../syntax.md). -``` +```ebnf BlockStat ::= ... | Extension TemplateStat ::= ... | Extension TopStat ::= ... | Extension diff --git a/docs/_docs/reference/contextual/given-imports.md b/docs/_docs/reference/contextual/given-imports.md index 6a55368979b1..28442581e408 100644 --- a/docs/_docs/reference/contextual/given-imports.md +++ b/docs/_docs/reference/contextual/given-imports.md @@ -103,7 +103,7 @@ given instances once their user base has migrated. ## Syntax -``` +```ebnf Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec diff --git a/docs/_docs/reference/contextual/givens.md b/docs/_docs/reference/contextual/givens.md index 1bfffbc5bf6f..f1333bf8811f 100644 --- a/docs/_docs/reference/contextual/givens.md +++ b/docs/_docs/reference/contextual/givens.md @@ -10,7 +10,7 @@ that serve for synthesizing arguments to [context parameters](./using-clauses.md ```scala trait Ord[T]: def compare(x: T, y: T): Int - extension (x: T) + extension (x: T) def < (y: T) = compare(x, y) < 0 def > (y: T) = compare(x, y) > 0 @@ -174,7 +174,7 @@ is created for each reference. Here is the syntax for given instances: -``` +```ebnf TmplDef ::= ... | ‘given’ GivenDef GivenDef ::= [GivenSig] StructuralInstance diff --git a/docs/_docs/reference/contextual/using-clauses.md b/docs/_docs/reference/contextual/using-clauses.md index 61b80ee27d91..48da98a3ccb5 100644 --- a/docs/_docs/reference/contextual/using-clauses.md +++ b/docs/_docs/reference/contextual/using-clauses.md @@ -151,7 +151,7 @@ def summon[T](using x: T): x.type = x Here is the new syntax of parameters and arguments seen as a delta from the [standard context free syntax of Scala 3](../syntax.md). `using` is a soft keyword, recognized only at the start of a parameter or argument list. It can be used as a normal identifier everywhere else. -``` +```ebnf ClsParamClause ::= ... | UsingClsParamClause DefParamClause ::= ... | UsingParamClause UsingClsParamClause ::= ‘(’ ‘using’ (ClsParams | Types) ‘)’ diff --git a/docs/_docs/reference/enums/adts.md b/docs/_docs/reference/enums/adts.md index 3ab8c9f3b45b..5219e062a633 100644 --- a/docs/_docs/reference/enums/adts.md +++ b/docs/_docs/reference/enums/adts.md @@ -154,7 +154,7 @@ The changes are specified below as deltas with respect to the Scala syntax given 1. Enum definitions are defined as follows: - ``` + ```ebnf TmplDef ::= `enum' EnumDef EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumBody EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’ @@ -164,7 +164,7 @@ The changes are specified below as deltas with respect to the Scala syntax given 2. Cases of enums are defined as follows: - ``` + ```ebnf EnumCase ::= `case' (id ClassConstr [`extends' ConstrApps]] | ids) ``` diff --git a/docs/_docs/reference/experimental/named-typeargs-spec.md b/docs/_docs/reference/experimental/named-typeargs-spec.md index 9e1113bbac86..741836a481f2 100644 --- a/docs/_docs/reference/experimental/named-typeargs-spec.md +++ b/docs/_docs/reference/experimental/named-typeargs-spec.md @@ -10,7 +10,7 @@ In this section we give more details about the [named type arguments](named-type The addition to the grammar is: -``` +```ebnf SimpleExpr1 ::= ... | SimpleExpr (TypeArgs | NamedTypeArgs) NamedTypeArgs ::= ‘[’ NamedTypeArg {‘,’ NamedTypeArg} ‘]’ @@ -19,7 +19,7 @@ NamedTypeArg ::= id ‘=’ Type Note in particular that named arguments cannot be passed to type constructors: -``` scala +```scala class C[T] val x: C[T = Int] = // error diff --git a/docs/_docs/reference/metaprogramming/macros-spec.md b/docs/_docs/reference/metaprogramming/macros-spec.md index aa8f94a9a1f7..d4221365454d 100644 --- a/docs/_docs/reference/metaprogramming/macros-spec.md +++ b/docs/_docs/reference/metaprogramming/macros-spec.md @@ -57,7 +57,7 @@ extends simply-typed lambda calculus with quotes and splices. ### Syntax The syntax of terms, values, and types is given as follows: -``` +```ebnf Terms t ::= x variable (x: T) => t lambda t t application @@ -77,7 +77,7 @@ Typing rules are formulated using a stack of environments `Es`. Individual environments `E` consist as usual of variable bindings `x: T`. Environments can be combined using the two combinators `'` and `$`. -``` +```ebnf Environment E ::= () empty E, x: T @@ -108,7 +108,7 @@ rule says that splice and quotes cancel each other out. The third rule is a context rule; it says that reduction is allowed in the hole `[ ]` position of an evaluation context. Evaluation contexts `e` and splice evaluation context `e_s` are defined syntactically as follows: -``` +```ebnf Eval context e ::= [ ] | e t | v e | 'e_s[${e}] Splice context e_s ::= [ ] | (x: T) => e_s | e_s t | u e_s ``` diff --git a/docs/_docs/reference/metaprogramming/simple-smp.md b/docs/_docs/reference/metaprogramming/simple-smp.md index 2ba0155ad329..61b062f55b87 100644 --- a/docs/_docs/reference/metaprogramming/simple-smp.md +++ b/docs/_docs/reference/metaprogramming/simple-smp.md @@ -23,7 +23,7 @@ replace evaluation contexts with contextual typing rules. While this is more verbose, it makes it easier to set up the meta theory. ## Syntax -``` +```ebnf Terms t ::= x variable (x: T) => t lambda t t application diff --git a/docs/_docs/reference/new-types/dependent-function-types-spec.md b/docs/_docs/reference/new-types/dependent-function-types-spec.md index f3237ddf7b9a..f603200b1ae0 100644 --- a/docs/_docs/reference/new-types/dependent-function-types-spec.md +++ b/docs/_docs/reference/new-types/dependent-function-types-spec.md @@ -8,7 +8,7 @@ Initial implementation in [PR #3464](https://github.com/lampepfl/dotty/pull/3464 ## Syntax -``` +```ebnf FunArgTypes ::= InfixType | ‘(’ [ FunArgType {',' FunArgType } ] ‘)’ | ‘(’ TypedFunParam {',' TypedFunParam } ‘)’ diff --git a/docs/_docs/reference/new-types/intersection-types-spec.md b/docs/_docs/reference/new-types/intersection-types-spec.md index 346c57c004f0..8d332fc6ed29 100644 --- a/docs/_docs/reference/new-types/intersection-types-spec.md +++ b/docs/_docs/reference/new-types/intersection-types-spec.md @@ -12,7 +12,7 @@ with the usual precedence and subject to usual resolving rules. Unless shadowed by another definition, it resolves to the type `scala.&`, which acts as a type alias to an internal representation of intersection types. -``` +```ebnf Type ::= ...| InfixType InfixType ::= RefinedType {id [nl] RefinedType} ``` diff --git a/docs/_docs/reference/new-types/type-lambdas-spec.md b/docs/_docs/reference/new-types/type-lambdas-spec.md index 76937e5160f7..7f7053a13ddd 100644 --- a/docs/_docs/reference/new-types/type-lambdas-spec.md +++ b/docs/_docs/reference/new-types/type-lambdas-spec.md @@ -6,7 +6,7 @@ nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/type-lambdas-s ## Syntax -``` +```ebnf Type ::= ... | TypeParamClause ‘=>>’ Type TypeParamClause ::= ‘[’ TypeParam {‘,’ TypeParam} ‘]’ TypeParam ::= {Annotation} (id [HkTypeParamClause] | ‘_’) TypeBounds diff --git a/docs/_docs/reference/other-new-features/export.md b/docs/_docs/reference/other-new-features/export.md index 41104a54e4a6..e8482cb343d9 100644 --- a/docs/_docs/reference/other-new-features/export.md +++ b/docs/_docs/reference/other-new-features/export.md @@ -176,7 +176,7 @@ extension (x: String) ## Syntax changes: -``` +```ebnf TemplateStat ::= ... | Export TopStat ::= ... diff --git a/docs/_docs/reference/other-new-features/indentation.md b/docs/_docs/reference/other-new-features/indentation.md index 40e2fc6fb38c..f53bf0995727 100644 --- a/docs/_docs/reference/other-new-features/indentation.md +++ b/docs/_docs/reference/other-new-features/indentation.md @@ -174,12 +174,12 @@ The syntax changes allowing this are as follows: Define for an arbitrary sequence of tokens or non-terminals `TS`: -``` +```ebnf :<<< TS >>> ::= ‘{’ TS ‘}’ | ``` Then the grammar changes as follows: -``` +```ebnf TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> EnumBody ::= :<<< [SelfType] EnumStat {semi EnumStat} >>> Refinement ::= :<<< [RefineDcl] {semi [RefineDcl]} >>> @@ -229,7 +229,7 @@ xs.foldLeft(0): (x, y) => The grammar changes for optional braces around arguments are as follows. -``` +```ebnf SimpleExpr ::= ... | SimpleExpr ColonArgument InfixExpr ::= ... @@ -430,7 +430,7 @@ If none of these criteria apply, it's often better to not use an end marker sinc ### Syntax -``` +```ebnf EndMarker ::= ‘end’ EndMarkerTag -- when followed by EOL EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ | ‘try’ | ‘new’ | ‘this’ | ‘given’ | ‘extension’ | ‘val’ diff --git a/docs/_docs/reference/other-new-features/opaques-details.md b/docs/_docs/reference/other-new-features/opaques-details.md index 87e56e240481..d285ec8e8325 100644 --- a/docs/_docs/reference/other-new-features/opaques-details.md +++ b/docs/_docs/reference/other-new-features/opaques-details.md @@ -6,7 +6,7 @@ nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/opaqu ## Syntax -``` +```ebnf Modifier ::= ... | ‘opaque’ ``` From 87f1e82d414e3a16fdc4f8623e25c31fa363e531 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 09:46:55 +0200 Subject: [PATCH 100/657] Copy Scala 2's Spec Scala 2 Spec taken from https://github.com/scala/scala/commit/f15171599298d57618a53e6f4a9a8c26e2d139a3 --- docs/_spec/01-lexical-syntax.md | 652 ++++++ docs/_spec/02-identifiers-names-and-scopes.md | 183 ++ docs/_spec/03-types.md | 1078 ++++++++++ .../04-basic-declarations-and-definitions.md | 945 +++++++++ docs/_spec/05-classes-and-objects.md | 1160 +++++++++++ docs/_spec/06-expressions.md | 1828 +++++++++++++++++ docs/_spec/07-implicits.md | 525 +++++ docs/_spec/08-pattern-matching.md | 817 ++++++++ docs/_spec/09-top-level-definitions.md | 210 ++ docs/_spec/10-xml-expressions-and-patterns.md | 145 ++ docs/_spec/11-annotations.md | 176 ++ docs/_spec/12-the-scala-standard-library.md | 816 ++++++++ docs/_spec/13-syntax-summary.md | 330 +++ docs/_spec/14-references.md | 207 ++ docs/_spec/15-changelog.md | 847 ++++++++ docs/_spec/README.md | 45 + docs/_spec/_config.yml | 11 + docs/_spec/_includes/numbering.css | 60 + docs/_spec/_includes/table-of-contents.yml | 23 + docs/_spec/_includes/version-notice.yml | 3 + docs/_spec/_layouts/default.yml | 51 + docs/_spec/_layouts/toc.yml | 34 + docs/_spec/id_dsa_travis.enc | 68 + docs/_spec/index.md | 55 + docs/_spec/public/favicon.ico | Bin 0 -> 6518 bytes docs/_spec/public/fonts/Heuristica-Bold.woff | Bin 0 -> 106188 bytes .../public/fonts/Heuristica-BoldItalic.woff | Bin 0 -> 104316 bytes .../public/fonts/Heuristica-Regular.woff | Bin 0 -> 141416 bytes .../fonts/Heuristica-RegularItalic.woff | Bin 0 -> 104700 bytes docs/_spec/public/fonts/LuxiMono-Bold.woff | Bin 0 -> 26560 bytes .../public/fonts/LuxiMono-BoldOblique.woff | Bin 0 -> 29480 bytes docs/_spec/public/fonts/LuxiMono-Regular.woff | Bin 0 -> 26432 bytes .../public/fonts/LuxiMono-RegularOblique.woff | Bin 0 -> 29300 bytes docs/_spec/public/fonts/LuxiSans-Bold.woff | Bin 0 -> 13592 bytes docs/_spec/public/fonts/LuxiSans-Regular.woff | Bin 0 -> 13568 bytes docs/_spec/public/images/classhierarchy.pdf | Bin 0 -> 84078 bytes docs/_spec/public/images/classhierarchy.png | Bin 0 -> 117555 bytes docs/_spec/public/images/github-logo@2x.png | Bin 0 -> 1753 bytes .../public/images/scala-spiral-white.png | Bin 0 -> 1442 bytes docs/_spec/public/octicons/LICENSE.txt | 9 + docs/_spec/public/octicons/octicons.css | 235 +++ docs/_spec/public/octicons/octicons.eot | Bin 0 -> 31440 bytes docs/_spec/public/octicons/octicons.svg | 198 ++ docs/_spec/public/octicons/octicons.ttf | Bin 0 -> 31272 bytes docs/_spec/public/octicons/octicons.woff | Bin 0 -> 17492 bytes docs/_spec/public/scripts/LICENSE-highlight | 24 + docs/_spec/public/scripts/LICENSE-toc | 18 + docs/_spec/public/scripts/highlight.pack.js | 1 + docs/_spec/public/scripts/main.js | 71 + docs/_spec/public/scripts/toc.js | 128 ++ docs/_spec/public/stylesheets/fonts.css | 73 + docs/_spec/public/stylesheets/print.css | 42 + .../_spec/public/stylesheets/screen-small.css | 57 + docs/_spec/public/stylesheets/screen-toc.css | 37 + docs/_spec/public/stylesheets/screen.css | 521 +++++ docs/_spec/spec-toc.xslt | 64 + 56 files changed, 11747 insertions(+) create mode 100644 docs/_spec/01-lexical-syntax.md create mode 100644 docs/_spec/02-identifiers-names-and-scopes.md create mode 100644 docs/_spec/03-types.md create mode 100644 docs/_spec/04-basic-declarations-and-definitions.md create mode 100644 docs/_spec/05-classes-and-objects.md create mode 100644 docs/_spec/06-expressions.md create mode 100644 docs/_spec/07-implicits.md create mode 100644 docs/_spec/08-pattern-matching.md create mode 100644 docs/_spec/09-top-level-definitions.md create mode 100644 docs/_spec/10-xml-expressions-and-patterns.md create mode 100644 docs/_spec/11-annotations.md create mode 100644 docs/_spec/12-the-scala-standard-library.md create mode 100644 docs/_spec/13-syntax-summary.md create mode 100644 docs/_spec/14-references.md create mode 100644 docs/_spec/15-changelog.md create mode 100644 docs/_spec/README.md create mode 100644 docs/_spec/_config.yml create mode 100644 docs/_spec/_includes/numbering.css create mode 100644 docs/_spec/_includes/table-of-contents.yml create mode 100644 docs/_spec/_includes/version-notice.yml create mode 100644 docs/_spec/_layouts/default.yml create mode 100644 docs/_spec/_layouts/toc.yml create mode 100644 docs/_spec/id_dsa_travis.enc create mode 100644 docs/_spec/index.md create mode 100644 docs/_spec/public/favicon.ico create mode 100644 docs/_spec/public/fonts/Heuristica-Bold.woff create mode 100644 docs/_spec/public/fonts/Heuristica-BoldItalic.woff create mode 100644 docs/_spec/public/fonts/Heuristica-Regular.woff create mode 100644 docs/_spec/public/fonts/Heuristica-RegularItalic.woff create mode 100644 docs/_spec/public/fonts/LuxiMono-Bold.woff create mode 100644 docs/_spec/public/fonts/LuxiMono-BoldOblique.woff create mode 100644 docs/_spec/public/fonts/LuxiMono-Regular.woff create mode 100644 docs/_spec/public/fonts/LuxiMono-RegularOblique.woff create mode 100644 docs/_spec/public/fonts/LuxiSans-Bold.woff create mode 100644 docs/_spec/public/fonts/LuxiSans-Regular.woff create mode 100644 docs/_spec/public/images/classhierarchy.pdf create mode 100644 docs/_spec/public/images/classhierarchy.png create mode 100644 docs/_spec/public/images/github-logo@2x.png create mode 100644 docs/_spec/public/images/scala-spiral-white.png create mode 100644 docs/_spec/public/octicons/LICENSE.txt create mode 100644 docs/_spec/public/octicons/octicons.css create mode 100644 docs/_spec/public/octicons/octicons.eot create mode 100644 docs/_spec/public/octicons/octicons.svg create mode 100644 docs/_spec/public/octicons/octicons.ttf create mode 100644 docs/_spec/public/octicons/octicons.woff create mode 100644 docs/_spec/public/scripts/LICENSE-highlight create mode 100644 docs/_spec/public/scripts/LICENSE-toc create mode 100644 docs/_spec/public/scripts/highlight.pack.js create mode 100644 docs/_spec/public/scripts/main.js create mode 100644 docs/_spec/public/scripts/toc.js create mode 100644 docs/_spec/public/stylesheets/fonts.css create mode 100644 docs/_spec/public/stylesheets/print.css create mode 100644 docs/_spec/public/stylesheets/screen-small.css create mode 100644 docs/_spec/public/stylesheets/screen-toc.css create mode 100644 docs/_spec/public/stylesheets/screen.css create mode 100644 docs/_spec/spec-toc.xslt diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md new file mode 100644 index 000000000000..921384b40e20 --- /dev/null +++ b/docs/_spec/01-lexical-syntax.md @@ -0,0 +1,652 @@ +--- +title: Lexical Syntax +layout: default +chapter: 1 +--- + +# Lexical Syntax + +Scala source code consists of Unicode text. + +The program text is tokenized as described in this chapter. +See the last section for special support for XML literals, +which are parsed in _XML mode_. + +To construct tokens, characters are distinguished according to the following +classes (Unicode general category given in parentheses): + +1. Whitespace characters. `\u0020 | \u0009 | \u000D | \u000A`. +1. Letters, which include lower case letters (`Ll`), upper case letters (`Lu`), + title case letters (`Lt`), other letters (`Lo`), modifier letters (`Lm`), + letter numerals (`Nl`) and the two characters `\u0024 ‘$’` and `\u005F ‘_’`. +1. Digits `‘0’ | … | ‘9’`. +1. Parentheses `‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ `. +1. Delimiter characters ``‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ ``. +1. Operator characters. These consist of all printable ASCII characters + (`\u0020` - `\u007E`) that are in none of the sets above, mathematical + symbols (`Sm`) and other symbols (`So`). + +## Identifiers + +```ebnf +op ::= opchar {opchar} +varid ::= lower idrest +boundvarid ::= varid + | ‘`’ varid ‘`’ +plainid ::= upper idrest + | varid + | op +id ::= plainid + | ‘`’ { charNoBackQuoteOrNewline | escapeSeq } ‘`’ +idrest ::= {letter | digit} [‘_’ op] +escapeSeq ::= UnicodeEscape | charEscapeSeq +UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit +hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ +``` + +There are three ways to form an identifier. First, an identifier can +start with a letter, followed by an arbitrary sequence of +letters and digits. This may be followed by underscore `‘_‘` +characters and another string composed of either letters and digits or +of operator characters. Second, an identifier can start with an operator +character followed by an arbitrary sequence of operator characters. +The preceding two forms are called _plain_ identifiers. Finally, +an identifier may also be formed by an arbitrary string between +backquotes (host systems may impose some restrictions on which +strings are legal for identifiers). The identifier then is composed +of all characters excluding the backquotes themselves. + +As usual, the longest match rule applies. For instance, the string + +```scala +big_bob++=`def` +``` + +decomposes into the three identifiers `big_bob`, `++=`, and +`def`. + +The rules for pattern matching further distinguish between +_variable identifiers_, which start with a lower case letter +or `_`, and _constant identifiers_, which do not. + +For this purpose, lower case letters include not only a-z, +but also all characters in Unicode category Ll (lowercase letter), +as well as all letters that have contributory property +Other_Lowercase, except characters in category Nl (letter numerals), +which are never taken as lower case. + +The following are examples of variable identifiers: + +> ```scala +> x maxIndex p2p empty_? +> `yield` αρετη _y dot_product_* +> __system _MAX_LEN_ +> ªpple ʰelper +> ``` + +Some examples of constant identifiers are + +> ```scala +> + Object $reserved Džul ǂnûm +> ⅰ_ⅲ Ⅰ_Ⅲ ↁelerious ǃqhàà ʹthatsaletter +> ``` + +The ‘$’ character is reserved for compiler-synthesized identifiers. +User programs should not define identifiers that contain ‘$’ characters. + +The following names are reserved words instead of being members of the +syntactic class `id` of lexical identifiers. + +```scala +abstract case catch class def +do else extends false final +finally for forSome if implicit +import lazy macro match new +null object override package private +protected return sealed super this +throw trait try true type +val var while with yield +_ : = => <- <: <% >: # @ +``` + +The Unicode operators `\u21D2` ‘´\Rightarrow´’ and `\u2190` ‘´\leftarrow´’, which have the ASCII +equivalents `=>` and `<-`, are also reserved. + +> Here are examples of identifiers: +> ```scala +> x Object maxIndex p2p empty_? +> + `yield` αρετη _y dot_product_* +> __system _MAX_LEN_ +> ``` + + + +> When one needs to access Java identifiers that are reserved words in Scala, use backquote-enclosed strings. +> For instance, the statement `Thread.yield()` is illegal, since `yield` is a reserved word in Scala. +> However, here's a work-around: `` Thread.`yield`() `` + +## Newline Characters + +```ebnf +semi ::= ‘;’ | nl {nl} +``` + +Scala is a line-oriented language where statements may be terminated by +semi-colons or newlines. A newline in a Scala source text is treated +as the special token “nl” if the three following criteria are satisfied: + +1. The token immediately preceding the newline can terminate a statement. +1. The token immediately following the newline can begin a statement. +1. The token appears in a region where newlines are enabled. + +The tokens that can terminate a statement are: literals, identifiers +and the following delimiters and reserved words: + +```scala +this null true false return type +_ ) ] } +``` + +The tokens that can begin a statement are all Scala tokens _except_ +the following delimiters and reserved words: + +```scala +catch else extends finally forSome match +with yield , . ; : = => <- <: <% +>: # [ ) ] } +``` + +A `case` token can begin a statement only if followed by a +`class` or `object` token. + +Newlines are enabled in: + +1. all of a Scala source file, except for nested regions where newlines + are disabled, and +1. the interval between matching `{` and `}` brace tokens, + except for nested regions where newlines are disabled. + +Newlines are disabled in: + +1. the interval between matching `(` and `)` parenthesis tokens, except for + nested regions where newlines are enabled, and +1. the interval between matching `[` and `]` bracket tokens, except for nested + regions where newlines are enabled. +1. The interval between a `case` token and its matching + `=>` token, except for nested regions where newlines are + enabled. +1. Any regions analyzed in [XML mode](#xml-mode). + +Note that the brace characters of `{...}` escapes in XML and +string literals are not tokens, +and therefore do not enclose a region where newlines +are enabled. + +Normally, only a single `nl` token is inserted between two +consecutive non-newline tokens which are on different lines, even if there are multiple lines +between the two tokens. However, if two tokens are separated by at +least one completely blank line (i.e a line which contains no +printable characters), then two `nl` tokens are inserted. + +The Scala grammar (given in full [here](13-syntax-summary.html)) +contains productions where optional `nl` tokens, but not +semicolons, are accepted. This has the effect that a new line in one of these +positions does not terminate an expression or statement. These positions can +be summarized as follows: + +Multiple newline tokens are accepted in the following places (note +that a semicolon in place of the newline would be illegal in every one +of these cases): + +- between the condition of a + [conditional expression](06-expressions.html#conditional-expressions) + or [while loop](06-expressions.html#while-loop-expressions) and the next + following expression, +- between the enumerators of a + [for-comprehension](06-expressions.html#for-comprehensions-and-for-loops) + and the next following expression, and +- after the initial `type` keyword in a + [type definition or declaration](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases). + +A single new line token is accepted + +- in front of an opening brace ‘{’, if that brace is a legal + continuation of the current statement or expression, +- after an [infix operator](06-expressions.html#prefix,-infix,-and-postfix-operations), + if the first token on the next line can start an expression, +- in front of a [parameter clause](04-basic-declarations-and-definitions.html#function-declarations-and-definitions), and +- after an [annotation](11-annotations.html#user-defined-annotations). + +> The newline tokens between the two lines are not +> treated as statement separators. +> +> ```scala +> if (x > 0) +> x = x - 1 +> +> while (x > 0) +> x = x / 2 +> +> for (x <- 1 to 10) +> println(x) +> +> type +> IntList = List[Int] +> ``` + + + +> ```scala +> new Iterator[Int] +> { +> private var x = 0 +> def hasNext = true +> def next = { x += 1; x } +> } +> ``` +> +> With an additional newline character, the same code is interpreted as +> an object creation followed by a local block: +> +> ```scala +> new Iterator[Int] +> +> { +> private var x = 0 +> def hasNext = true +> def next = { x += 1; x } +> } +> ``` + + + +> ```scala +> x < 0 || +> x > 10 +> ``` +> +> With an additional newline character, the same code is interpreted as +> two expressions: +> +> ```scala +> x < 0 || +> +> x > 10 +> ``` + + + +> ```scala +> def func(x: Int) +> (y: Int) = x + y +> ``` +> +> With an additional newline character, the same code is interpreted as +> an abstract function definition and a syntactically illegal statement: +> +> ```scala +> def func(x: Int) +> +> (y: Int) = x + y +> ``` + + + +> ```scala +> @serializable +> protected class Data { ... } +> ``` +> +> With an additional newline character, the same code is interpreted as +> an attribute and a separate statement (which is syntactically illegal). +> +> ```scala +> @serializable +> +> protected class Data { ... } +> ``` + +## Literals + +There are literals for integer numbers, floating point numbers, +characters, booleans, symbols, strings. The syntax of these literals is in +each case as in Java. + + + +```ebnf +Literal ::= [‘-’] integerLiteral + | [‘-’] floatingPointLiteral + | booleanLiteral + | characterLiteral + | stringLiteral + | interpolatedString + | symbolLiteral + | ‘null’ +``` + +### Integer Literals + +```ebnf +integerLiteral ::= (decimalNumeral | hexNumeral) + [‘L’ | ‘l’] +decimalNumeral ::= digit {digit} +hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit {hexDigit} +``` + +Values of type `Int` are all integer +numbers between $-2\^{31}$ and $2\^{31}-1$, inclusive. Values of +type `Long` are all integer numbers between $-2\^{63}$ and +$2\^{63}-1$, inclusive. A compile-time error occurs if an integer literal +denotes a number outside these ranges. + +Integer literals are usually of type `Int`, or of type +`Long` when followed by a `L` or `l` suffix. +(Lowercase `l` is deprecated for reasons of legibility.) + +However, if the expected type [_pt_](06-expressions.html#expression-typing) of a literal +in an expression is either `Byte`, `Short`, or `Char` +and the integer number fits in the numeric range defined by the type, +then the number is converted to type _pt_ and the literal's type +is _pt_. The numeric ranges given by these types are: + +| | | +|----------------|--------------------------| +|`Byte` | ´-2\^7´ to ´2\^7-1´ | +|`Short` | ´-2\^{15}´ to ´2\^{15}-1´| +|`Char` | ´0´ to ´2\^{16}-1´ | + +The digits of a numeric literal may be separated by +arbitrarily many underscores for purposes of legibility. + +> ```scala +> 0 21_000 0x7F -42L 0xFFFF_FFFF +> ``` + +### Floating Point Literals + +```ebnf +floatingPointLiteral ::= digit {digit} ‘.’ digit {digit} [exponentPart] [floatType] + | ‘.’ digit {digit} [exponentPart] [floatType] + | digit {digit} exponentPart [floatType] + | digit {digit} [exponentPart] floatType +exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit {digit} +floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ +``` + +Floating point literals are of type `Float` when followed by +a floating point type suffix `F` or `f`, and are +of type `Double` otherwise. The type `Float` +consists of all IEEE 754 32-bit single-precision binary floating point +values, whereas the type `Double` consists of all IEEE 754 +64-bit double-precision binary floating point values. + +If a floating point literal in a program is followed by a token +starting with a letter, there must be at least one intervening +whitespace character between the two tokens. + +> ```scala +> 0.0 1e30f 3.14159f 1.0e-100 .1 +> ``` + + + +> The phrase `1.toString` parses as three different tokens: +> the integer literal `1`, a `.`, and the identifier `toString`. + + + +> `1.` is not a valid floating point literal because the mandatory digit after the `.` is missing. + +### Boolean Literals + +```ebnf +booleanLiteral ::= ‘true’ | ‘false’ +``` + +The boolean literals `true` and `false` are +members of type `Boolean`. + +### Character Literals + +```ebnf +characterLiteral ::= ‘'’ (charNoQuoteOrNewline | escapeSeq) ‘'’ +``` + +A character literal is a single character enclosed in quotes. +The character can be any Unicode character except the single quote +delimiter or `\u000A` (LF) or `\u000D` (CR); +or any Unicode character represented by an +[escape sequence](#escape-sequences). + +> ```scala +> 'a' '\u0041' '\n' '\t' +> ``` + +### String Literals + +```ebnf +stringLiteral ::= ‘"’ {stringElement} ‘"’ +stringElement ::= charNoDoubleQuoteOrNewline | escapeSeq +``` + +A string literal is a sequence of characters in double quotes. +The characters can be any Unicode character except the double quote +delimiter or `\u000A` (LF) or `\u000D` (CR); +or any Unicode character represented by an [escape sequence](#escape-sequences). + +If the string literal contains a double quote character, it must be escaped using +`"\""`. + +The value of a string literal is an instance of class `String`. + +> ```scala +> "Hello, world!\n" +> "\"Hello,\" replied the world." +> ``` + +#### Multi-Line String Literals + +```ebnf +stringLiteral ::= ‘"""’ multiLineChars ‘"""’ +multiLineChars ::= {[‘"’] [‘"’] charNoDoubleQuote} {‘"’} +``` + +A multi-line string literal is a sequence of characters enclosed in +triple quotes `""" ... """`. The sequence of characters is +arbitrary, except that it may contain three or more consecutive quote characters +only at the very end. Characters +must not necessarily be printable; newlines or other +control characters are also permitted. [Escape sequences](#escape-sequences) are +not processed, except for Unicode escapes (this is deprecated since 2.13.2). + +> ```scala +> """the present string +> spans three +> lines.""" +> ``` +> +> This would produce the string: +> +> ```scala +> the present string +> spans three +> lines. +> ``` +> +> The Scala library contains a utility method `stripMargin` +> which can be used to strip leading whitespace from multi-line strings. +> The expression +> +> ```scala +> """the present string +> |spans three +> |lines.""".stripMargin +> ``` +> +> evaluates to +> +> ```scala +> the present string +> spans three +> lines. +> ``` +> +> Method `stripMargin` is defined in class +> [scala.collection.StringOps](https://www.scala-lang.org/api/current/scala/collection/StringOps.html#stripMargin:String). + +#### Interpolated string + +```ebnf +interpolatedString ::= alphaid ‘"’ {[‘\’] interpolatedStringPart | ‘\\’ | ‘\"’} ‘"’ + | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ +interpolatedStringPart ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape +escape ::= ‘$$’ + | ‘$"’ + | ‘$’ alphaid + | ‘$’ BlockExpr +alphaid ::= upper idrest + | varid + +``` + +An interpolated string consists of an identifier starting with a letter immediately +followed by a string literal. There may be no whitespace characters or comments +between the leading identifier and the opening quote `"` of the string. +The string literal in an interpolated string can be standard (single quote) +or multi-line (triple quote). + +Inside an interpolated string none of the usual escape characters are interpreted +no matter whether the string literal is normal (enclosed in single quotes) or +multi-line (enclosed in triple quotes). Note that the sequence `\"` does not +close a normal string literal (enclosed in single quotes). + +There are three forms of dollar sign escape. +The most general form encloses an expression in `${` and `}`, i.e. `${expr}`. +The expression enclosed in the braces that follow the leading `$` character is of +syntactical category BlockExpr. Hence, it can contain multiple statements, +and newlines are significant. Single ‘$’-signs are not permitted in isolation +in an interpolated string. A single ‘$’-sign can still be obtained by doubling the ‘$’ +character: ‘$$’. A single ‘"’-sign can be obtained by the sequence ‘\$"’. + +The simpler form consists of a ‘$’-sign followed by an identifier starting with +a letter and followed only by letters, digits, and underscore characters, e.g., `$id`. +The simpler form is expanded by putting braces around the identifier, +e.g., `$id` is equivalent to `${id}`. In the following, unless we explicitly state otherwise, +we assume that this expansion has already been performed. + +The expanded expression is type checked normally. Usually, `StringContext` will resolve to +the default implementation in the scala package, +but it could also be user-defined. Note that new interpolators can also be added through +implicit conversion of the built-in `scala.StringContext`. + +One could write an extension +```scala +implicit class StringInterpolation(s: StringContext) { + def id(args: Any*) = ??? +} +``` + +### Escape Sequences + +The following character escape sequences are recognized in character and string literals. + +| charEscapeSeq | unicode | name | char | +|---------------|----------|-----------------|--------| +| `‘\‘ ‘b‘` | `\u0008` | backspace | `BS` | +| `‘\‘ ‘t‘` | `\u0009` | horizontal tab | `HT` | +| `‘\‘ ‘n‘` | `\u000a` | linefeed | `LF` | +| `‘\‘ ‘f‘` | `\u000c` | form feed | `FF` | +| `‘\‘ ‘r‘` | `\u000d` | carriage return | `CR` | +| `‘\‘ ‘"‘` | `\u0022` | double quote | `"` | +| `‘\‘ ‘'‘` | `\u0027` | single quote | `'` | +| `‘\‘ ‘\‘` | `\u005c` | backslash | `\` | + +In addition, Unicode escape sequences of the form `\uxxxx`, where each `x` is a hex digit are +recognized in character and string literals. + +It is a compile time error if a backslash character in a character or +string literal does not start a valid escape sequence. + +### Symbol literals + +```ebnf +symbolLiteral ::= ‘'’ plainid +``` + +A symbol literal `'x` is deprecated shorthand for the expression `scala.Symbol("x")`. + +The `apply` method of `Symbol`'s companion object +caches weak references to `Symbol`s, thus ensuring that +identical symbol literals are equivalent with respect to reference +equality. + +## Whitespace and Comments + +Tokens may be separated by whitespace characters +and/or comments. Comments come in two forms: + +A single-line comment is a sequence of characters which starts with +`//` and extends to the end of the line. + +A multi-line comment is a sequence of characters between +`/*` and `*/`. Multi-line comments may be nested, +but are required to be properly nested. Therefore, a comment like +`/* /* */` will be rejected as having an unterminated +comment. + +## Trailing Commas in Multi-line Expressions + +If a comma (`,`) is followed immediately, ignoring whitespace, by a newline and +a closing parenthesis (`)`), bracket (`]`), or brace (`}`), then the comma is +treated as a "trailing comma" and is ignored. For example: + +```scala +foo( + 23, + "bar", + true, +) +``` + +## XML mode + +In order to allow literal inclusion of XML fragments, lexical analysis +switches from Scala mode to XML mode when encountering an opening +angle bracket ‘<’ in the following circumstance: The ‘<’ must be +preceded either by whitespace, an opening parenthesis or an opening +brace and immediately followed by a character starting an XML name. + +```ebnf + ( whitespace | ‘(’ | ‘{’ ) ‘<’ (XNameStart | ‘!’ | ‘?’) + + XNameStart ::= ‘_’ | BaseChar | Ideographic // as in W3C XML, but without ‘:’ +``` + +The scanner switches from XML mode to Scala mode if either + +- the XML expression or the XML pattern started by the initial ‘<’ has been + successfully parsed, or if +- the parser encounters an embedded Scala expression or pattern and + forces the Scanner + back to normal mode, until the Scala expression or pattern is + successfully parsed. In this case, since code and XML fragments can be + nested, the parser has to maintain a stack that reflects the nesting + of XML and Scala expressions adequately. + +Note that no Scala tokens are constructed in XML mode, and that comments are interpreted +as text. + +> The following value definition uses an XML literal with two embedded +> Scala expressions: +> +> ```scala +> val b = +> The Scala Language Specification +> {scalaBook.version} +> {scalaBook.authors.mkList("", ", ", "")} +> +> ``` diff --git a/docs/_spec/02-identifiers-names-and-scopes.md b/docs/_spec/02-identifiers-names-and-scopes.md new file mode 100644 index 000000000000..b8bde8cfd1a9 --- /dev/null +++ b/docs/_spec/02-identifiers-names-and-scopes.md @@ -0,0 +1,183 @@ +--- +title: Identifiers, Names & Scopes +layout: default +chapter: 2 +--- + +# Identifiers, Names and Scopes + +Names in Scala identify types, values, methods, and classes which are +collectively called _entities_. Names are introduced by local +[definitions and declarations](04-basic-declarations-and-definitions.html#basic-declarations-and-definitions), +[inheritance](05-classes-and-objects.html#class-members), +[import clauses](04-basic-declarations-and-definitions.html#import-clauses), or +[package clauses](09-top-level-definitions.html#packagings) +which are collectively called _bindings_. + +Bindings of different kinds have precedence defined on them: + +1. Definitions and declarations that are local, inherited, or made + available by a package clause and also defined in the same compilation unit + as the reference to them, have the highest precedence. +1. Explicit imports have the next highest precedence. +1. Wildcard imports have the next highest precedence. +1. Definitions made available by a package clause, but not also defined in the + same compilation unit as the reference to them, as well as imports which + are supplied by the compiler but not explicitly written in source code, + have the lowest precedence. + +There are two different name spaces, one for [types](03-types.html#types) +and one for [terms](06-expressions.html#expressions). The same name may designate a +type and a term, depending on the context where the name is used. + +A binding has a _scope_ in which the entity defined by a single +name can be accessed using a simple name. Scopes are nested. A binding +in some inner scope _shadows_ bindings of lower precedence in the +same scope as well as bindings of the same or lower precedence in outer +scopes. + +Note that shadowing is only a partial order. In the following example, +neither binding of `x` shadows the other. Consequently, the +reference to `x` in the last line of the block is ambiguous. + +```scala +val x = 1 +locally { + import p.X.x + x +} +``` + +A reference to an unqualified (type- or term-) identifier ´x´ is bound +by the unique binding, which + +- defines an entity with name ´x´ in the same namespace as the identifier, and +- shadows all other bindings that define entities with name ´x´ in that + namespace. + +It is an error if no such binding exists. If ´x´ is bound by an +import clause, then the simple name ´x´ is taken to be equivalent to +the qualified name to which ´x´ is mapped by the import clause. If ´x´ +is bound by a definition or declaration, then ´x´ refers to the entity +introduced by that binding. In that case, the type of ´x´ is the type +of the referenced entity. + +A reference to a qualified (type- or term-) identifier ´e.x´ refers to +the member of the type ´T´ of ´e´ which has the name ´x´ in the same +namespace as the identifier. It is an error if ´T´ is not a [value type](03-types.html#value-types). +The type of ´e.x´ is the member type of the referenced entity in ´T´. + +Binding precedence implies that the way source is bundled in files affects name resolution. +In particular, imported names have higher precedence than names, defined in other files, +that might otherwise be visible because they are defined in +either the current package or an enclosing package. + +Note that a package definition is taken as lowest precedence, since packages +are open and can be defined across arbitrary compilation units. + +```scala +package util { + import scala.util + class Random + object Test extends App { + println(new util.Random) // scala.util.Random + } +} +``` + +The compiler supplies imports in a preamble to every source file. This preamble +conceptually has the following form, where braces indicate nested scopes: + +```scala +import java.lang._ +{ + import scala._ + { + import Predef._ + { /* source */ } + } +} +``` + +These imports are taken as lowest precedence, so that they are always shadowed +by user code, which may contain competing imports and definitions. +They also increase the nesting depth as shown, so that later imports +shadow earlier ones. + +As a convenience, multiple bindings of a type identifier to the same +underlying type is permitted. This is possible when import clauses introduce +a binding of a member type alias with the same binding precedence, typically +through wildcard imports. This allows redundant type aliases to be imported +without introducing an ambiguity. + +```scala +object X { type T = annotation.tailrec } +object Y { type T = annotation.tailrec } +object Z { + import X._, Y._, annotation.{tailrec => T} // OK, all T mean tailrec + @T def f: Int = { f ; 42 } // error, f is not tail recursive +} +``` + +Similarly, imported aliases of names introduced by package statements are +allowed, even though the names are strictly ambiguous: + +```scala +// c.scala +package p { class C } + +// xy.scala +import p._ +package p { class X extends C } +package q { class Y extends C } +``` + +The reference to `C` in the definition of `X` is strictly ambiguous +because `C` is available by virtue of the package clause in +a different file, and can't shadow the imported name. But because +the references are the same, the definition is taken as though it +did shadow the import. + +###### Example + +Assume the following two definitions of objects named `X` in packages `p` and `q` +in separate compilation units. + +```scala +package p { + object X { val x = 1; val y = 2 } +} + +package q { + object X { val x = true; val y = false } +} +``` + +The following program illustrates different kinds of bindings and +precedences between them. + +```scala +package p { // `X' bound by package clause +import Console._ // `println' bound by wildcard import +object Y { + println(s"L4: $X") // `X' refers to `p.X' here + locally { + import q._ // `X' bound by wildcard import + println(s"L7: $X") // `X' refers to `q.X' here + import X._ // `x' and `y' bound by wildcard import + println(s"L9: $x") // `x' refers to `q.X.x' here + locally { + val x = 3 // `x' bound by local definition + println(s"L12: $x") // `x' refers to constant `3' here + locally { + import q.X._ // `x' and `y' bound by wildcard import +// println(s"L15: $x") // reference to `x' is ambiguous here + import X.y // `y' bound by explicit import + println(s"L17: $y") // `y' refers to `q.X.y' here + locally { + val x = "abc" // `x' bound by local definition + import p.X._ // `x' and `y' bound by wildcard import +// println(s"L21: $y") // reference to `y' is ambiguous here + println(s"L22: $x") // `x' refers to string "abc" here +}}}}}} +``` diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md new file mode 100644 index 000000000000..3c78b33e571c --- /dev/null +++ b/docs/_spec/03-types.md @@ -0,0 +1,1078 @@ +--- +title: Types +layout: default +chapter: 3 +--- + +# Types + +```ebnf + Type ::= FunctionArgTypes ‘=>’ Type + | InfixType [ExistentialClause] + FunctionArgTypes ::= InfixType + | ‘(’ [ ParamType {‘,’ ParamType } ] ‘)’ + ExistentialClause ::= ‘forSome’ ‘{’ ExistentialDcl + {semi ExistentialDcl} ‘}’ + ExistentialDcl ::= ‘type’ TypeDcl + | ‘val’ ValDcl + InfixType ::= CompoundType {id [nl] CompoundType} + CompoundType ::= AnnotType {‘with’ AnnotType} [Refinement] + | Refinement + AnnotType ::= SimpleType {Annotation} + SimpleType ::= SimpleType TypeArgs + | SimpleType ‘#’ id + | StableId + | Path ‘.’ ‘type’ + | Literal + | ‘(’ Types ‘)’ + TypeArgs ::= ‘[’ Types ‘]’ + Types ::= Type {‘,’ Type} +``` + +We distinguish between proper types and type constructors, which +take type parameters and yield types. A subset of proper types +called _value types_ represents sets of (first-class) values. +Value types are either _concrete_ or _abstract_. + +Every concrete value type can be represented as a _class type_, i.e. a +[type designator](#type-designators) that refers to a +[class or a trait](05-classes-and-objects.html#class-definitions) [^1], or as a +[compound type](#compound-types) representing an +intersection of types, possibly with a [refinement](#compound-types) +that further constrains the types of its members. + +Abstract value types are introduced by [type parameters](04-basic-declarations-and-definitions.html#type-parameters) +and [abstract type bindings](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases). +Parentheses in types can be used for grouping. + +[^1]: We assume that objects and packages also implicitly + define a class (of the same name as the object or package, but + inaccessible to user programs). + +Non-value types capture properties of identifiers that +[are not values](#non-value-types). For example, a +[type constructor](#type-constructors) does not directly specify a type of +values. However, when a type constructor is applied to the correct type +arguments, it yields a proper type, which may be a value type. + +Non-value types are expressed indirectly in Scala. E.g., a method type is +described by writing down a method signature, which in itself is not a real +type, although it gives rise to a corresponding [method type](#method-types). +Type constructors are another example, as one can write +`type Swap[m[_, _], a,b] = m[b, a]`, but there is no syntax to write +the corresponding anonymous type function directly. + +## Paths + +```ebnf +Path ::= StableId + | [id ‘.’] this +StableId ::= id + | Path ‘.’ id + | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id +ClassQualifier ::= ‘[’ id ‘]’ +``` + +Paths are not types themselves, but they can be a part of named types +and in that function form a central role in Scala's type system. + +A path is one of the following. + +- The empty path ε (which cannot be written explicitly in user programs). +- ´C.´`this`, where ´C´ references a class. + The path `this` is taken as a shorthand for ´C.´`this` where + ´C´ is the name of the class directly enclosing the reference. +- ´p.x´ where ´p´ is a path and ´x´ is a stable member of ´p´. + _Stable members_ are packages or members introduced by object definitions or + by value definitions of [non-volatile types](#volatile-types). +- ´C.´`super`´.x´ or ´C.´`super`´[M].x´ + where ´C´ references a class and ´x´ references a + stable member of the super class or designated parent class ´M´ of ´C´. + The prefix `super` is taken as a shorthand for ´C.´`super` where + ´C´ is the name of the class directly enclosing the reference. + +A _stable identifier_ is a path which ends in an identifier. + +## Value Types + +Every value in Scala has a type which is of one of the following +forms. + +### Singleton Types + +```ebnf +SimpleType ::= Path ‘.’ ‘type’ +``` + +A _singleton type_ is of the form ´p.´`type`. Where ´p´ is a path pointing to a +value which [conforms](06-expressions.html#expression-typing) to +`scala.AnyRef`, the type denotes the set of values consisting of `null` and the +value denoted by ´p´ (i.e., the value ´v´ for which `v eq p`). Where the path +does not conform to `scala.AnyRef` the type denotes the set consisting of only +the value denoted by ´p´. + + + +### Literal Types + +```ebnf +SimpleType ::= Literal +``` + +A literal type `lit` is a special kind of singleton type which denotes the +single literal value `lit`. Thus, the type ascription `1: 1` gives the most +precise type to the literal value `1`: the literal type `1`. + +At run time, an expression `e` is considered to have literal type `lit` if `e == lit`. +Concretely, the result of `e.isInstanceOf[lit]` and `e match { case _ : lit => }` is +determined by evaluating `e == lit`. + +Literal types are available for all types for which there is dedicated syntax +except `Unit`. This includes the numeric types (other than `Byte` and `Short` +which don't currently have syntax), `Boolean`, `Char` and `String`. + +### Stable Types +A _stable type_ is a singleton type, a literal type, +or a type that is declared to be a subtype of trait `scala.Singleton`. + +### Type Projection + +```ebnf +SimpleType ::= SimpleType ‘#’ id +``` + +A _type projection_ ´T´#´x´ references the type member named +´x´ of type ´T´. + + + +### Type Designators + +```ebnf +SimpleType ::= StableId +``` + +A _type designator_ refers to a named value type. It can be simple or +qualified. All such type designators are shorthands for type projections. + +Specifically, the unqualified type name ´t´ where ´t´ is bound in some +class, object, or package ´C´ is taken as a shorthand for +´C.´`this.type#`´t´. If ´t´ is +not bound in a class, object, or package, then ´t´ is taken as a +shorthand for ε`.type#`´t´. + +A qualified type designator has the form `p.t` where `p` is +a [path](#paths) and _t_ is a type name. Such a type designator is +equivalent to the type projection `p.type#t`. + +###### Example + +Some type designators and their expansions are listed below. We assume +a local type parameter ´t´, a value `maintable` +with a type member `Node` and the standard class `scala.Int`, + +| Designator | Expansion | +|-------------------- | --------------------------| +|t | ε.type#t | +|Int | scala.type#Int | +|scala.Int | scala.type#Int | +|data.maintable.Node | data.maintable.type#Node | + +### Parameterized Types + +```ebnf +SimpleType ::= SimpleType TypeArgs +TypeArgs ::= ‘[’ Types ‘]’ +``` + +A _parameterized type_ ´T[ T_1 , \ldots , T_n ]´ consists of a type +designator ´T´ and type parameters ´T_1 , \ldots , T_n´ where +´n \geq 1´. ´T´ must refer to a type constructor which takes ´n´ type +parameters ´a_1 , \ldots , a_n´. + +Say the type parameters have lower bounds ´L_1 , \ldots , L_n´ and +upper bounds ´U_1, \ldots, U_n´. The parameterized type is +well-formed if each actual type parameter +_conforms to its bounds_, i.e. ´\sigma L_i <: T_i <: \sigma U_i´ where ´\sigma´ is the +substitution ´[ a_1 := T_1 , \ldots , a_n := T_n ]´. + +###### Example Parameterized Types + +Given the partial type definitions: + +```scala +class TreeMap[A <: Comparable[A], B] { … } +class List[A] { … } +class I extends Comparable[I] { … } + +class F[M[_], X] { … } +class S[K <: String] { … } +class G[M[ Z <: I ], I] { … } +``` + +the following parameterized types are well-formed: + +```scala +TreeMap[I, String] +List[I] +List[List[Boolean]] + +F[List, Int] +G[S, String] +``` + +###### Example + +Given the [above type definitions](#example-parameterized-types), +the following types are ill-formed: + +```scala +TreeMap[I] // illegal: wrong number of parameters +TreeMap[List[I], Int] // illegal: type parameter not within bound + +F[Int, Boolean] // illegal: Int is not a type constructor +F[TreeMap, Int] // illegal: TreeMap takes two parameters, + // F expects a constructor taking one +G[S, Int] // illegal: S constrains its parameter to + // conform to String, + // G expects type constructor with a parameter + // that conforms to Int +``` + +### Tuple Types + +```ebnf +SimpleType ::= ‘(’ Types ‘)’ +``` + +A _tuple type_ ´(T_1 , \ldots , T_n)´ is an alias for the +class `scala.Tuple´n´[´T_1´, … , ´T_n´]`, where ´n \geq 2´. + +Tuple classes are case classes whose fields can be accessed using +selectors `_1` , … , `_n`. Their functionality is +abstracted in a corresponding `Product` trait. The _n_-ary tuple +class and product trait are defined at least as follows in the +standard Scala library (they might also add other methods and +implement other traits). + +```scala +case class Tuple´_n´[+´T_1´, … , +´T_n´](_1: ´T_1´, … , _n: ´T_n´) +extends Product´_n´[´T_1´, … , ´T_n´] + +trait Product´_n´[+´T_1´, … , +´T_n´] { + override def productArity = ´n´ + def _1: ´T_1´ + … + def _n: ´T_n´ +} +``` + +### Annotated Types + +```ebnf +AnnotType ::= SimpleType {Annotation} +``` + +An _annotated type_ ´T´ ´a_1, \ldots, a_n´ +attaches [annotations](11-annotations.html#user-defined-annotations) +´a_1 , \ldots , a_n´ to the type ´T´. + +###### Example + +The following type adds the `@suspendable` annotation to the type `String`: + +```scala +String @suspendable +``` + +### Compound Types + +```ebnf +CompoundType ::= AnnotType {‘with’ AnnotType} [Refinement] + | Refinement +Refinement ::= [nl] ‘{’ RefineStat {semi RefineStat} ‘}’ +RefineStat ::= Dcl + | ‘type’ TypeDef + | +``` + +A _compound type_ ´T_1´ `with` … `with` ´T_n \\{ R \\}´ +represents objects with members as given in the component types +´T_1 , \ldots , T_n´ and the refinement ´\\{ R \\}´. A refinement +´\\{ R \\}´ contains declarations and type definitions. +If a declaration or definition overrides a declaration or definition in +one of the component types ´T_1 , \ldots , T_n´, the usual rules for +[overriding](05-classes-and-objects.html#overriding) apply; otherwise the declaration +or definition is said to be “structural” [^2]. + +[^2]: A reference to a structurally defined member (method call or access + to a value or variable) may generate binary code that is significantly + slower than an equivalent code to a non-structural member. + +Within a method declaration in a structural refinement, the type of +any value parameter may only refer to type parameters or abstract +types that are contained inside the refinement. That is, it must refer +either to a type parameter of the method itself, or to a type +definition within the refinement. This restriction does not apply to +the method's result type. + +If no refinement is given, the empty refinement is implicitly added, +i.e. ´T_1´ `with` … `with` ´T_n´ is a shorthand for ´T_1´ `with` … `with` ´T_n \\{\\}´. + +A compound type may also consist of just a refinement +´\\{ R \\}´ with no preceding component types. Such a type is +equivalent to `AnyRef` ´\\{ R \\}´. + +###### Example + +The following example shows how to declare and use a method which has +a parameter type that contains a refinement with structural declarations. + +```scala +case class Bird (val name: String) extends Object { + def fly(height: Int) = … +… +} +case class Plane (val callsign: String) extends Object { + def fly(height: Int) = … +… +} +def takeoff( + runway: Int, + r: { val callsign: String; def fly(height: Int) }) = { + tower.print(r.callsign + " requests take-off on runway " + runway) + tower.read(r.callsign + " is clear for take-off") + r.fly(1000) +} +val bird = new Bird("Polly the parrot"){ val callsign = name } +val a380 = new Plane("TZ-987") +takeoff(42, bird) +takeoff(89, a380) +``` + +Although `Bird` and `Plane` do not share any parent class other than +`Object`, the parameter _r_ of method `takeoff` is defined using a +refinement with structural declarations to accept any object that declares +a value `callsign` and a `fly` method. + +### Infix Types + +```ebnf +InfixType ::= CompoundType {id [nl] CompoundType} +``` + +An _infix type_ ´T_1´ `op` ´T_2´ consists of an infix +operator `op` which gets applied to two type operands ´T_1´ and +´T_2´. The type is equivalent to the type application +`op`´[T_1, T_2]´. The infix operator `op` may be an +arbitrary identifier. + +All type infix operators have the same precedence; parentheses have to +be used for grouping. The [associativity](06-expressions.html#prefix,-infix,-and-postfix-operations) +of a type operator is determined as for term operators: type operators +ending in a colon ‘:’ are right-associative; all other +operators are left-associative. + +In a sequence of consecutive type infix operations +´t_0 \, \mathit{op} \, t_1 \, \mathit{op_2} \, \ldots \, \mathit{op_n} \, t_n´, +all operators ´\mathit{op}\_1 , \ldots , \mathit{op}\_n´ must have the same +associativity. If they are all left-associative, the sequence is +interpreted as +´(\ldots (t_0 \mathit{op_1} t_1) \mathit{op_2} \ldots) \mathit{op_n} t_n´, +otherwise it is interpreted as +´t_0 \mathit{op_1} (t_1 \mathit{op_2} ( \ldots \mathit{op_n} t_n) \ldots)´. + +### Function Types + +```ebnf +Type ::= FunctionArgs ‘=>’ Type +FunctionArgs ::= InfixType + | ‘(’ [ ParamType {‘,’ ParamType } ] ‘)’ +``` + +The type ´(T_1 , \ldots , T_n) \Rightarrow U´ represents the set of function +values that take arguments of types ´T_1 , \ldots , Tn´ and yield +results of type ´U´. In the case of exactly one argument type +´T \Rightarrow U´ is a shorthand for ´(T) \Rightarrow U´. +An argument type of the form ´\Rightarrow T´ +represents a [call-by-name parameter](04-basic-declarations-and-definitions.html#by-name-parameters) of type ´T´. + +Function types associate to the right, e.g. +´S \Rightarrow T \Rightarrow U´ is the same as +´S \Rightarrow (T \Rightarrow U)´. + +Function types are shorthands for class types that define `apply` +functions. Specifically, the ´n´-ary function type +´(T_1 , \ldots , T_n) \Rightarrow U´ is a shorthand for the class type +`Function´_n´[´T_1´ , … , ´T_n´, ´U´]`. Such class +types are defined in the Scala library for ´n´ between 0 and 22 as follows. + +```scala +package scala +trait Function´_n´[-´T_1´ , … , -´T_n´, +´U´] { + def apply(´x_1´: ´T_1´ , … , ´x_n´: ´T_n´): ´U´ + override def toString = "" +} +``` + +Hence, function types are [covariant](04-basic-declarations-and-definitions.html#variance-annotations) in their +result type and contravariant in their argument types. + +### Existential Types + +```ebnf +Type ::= InfixType ExistentialClauses +ExistentialClauses ::= ‘forSome’ ‘{’ ExistentialDcl + {semi ExistentialDcl} ‘}’ +ExistentialDcl ::= ‘type’ TypeDcl + | ‘val’ ValDcl +``` + +An _existential type_ has the form `´T´ forSome { ´Q´ }` +where ´Q´ is a sequence of +[type declarations](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases). + +Let +´t_1[\mathit{tps}\_1] >: L_1 <: U_1 , \ldots , t_n[\mathit{tps}\_n] >: L_n <: U_n´ +be the types declared in ´Q´ (any of the +type parameter sections `[ ´\mathit{tps}_i´ ]` might be missing). +The scope of each type ´t_i´ includes the type ´T´ and the existential clause +´Q´. +The type variables ´t_i´ are said to be _bound_ in the type +`´T´ forSome { ´Q´ }`. +Type variables which occur in a type ´T´ but which are not bound in ´T´ are said +to be _free_ in ´T´. + +A _type instance_ of `´T´ forSome { ´Q´ }` +is a type ´\sigma T´ where ´\sigma´ is a substitution over ´t_1 , \ldots , t_n´ +such that, for each ´i´, ´\sigma L_i <: \sigma t_i <: \sigma U_i´. +The set of values denoted by the existential type `´T´ forSome {´\,Q\,´}` +is the union of the set of values of all its type instances. + +A _skolemization_ of `´T´ forSome { ´Q´ }` is +a type instance ´\sigma T´, where ´\sigma´ is the substitution +´[t_1'/t_1 , \ldots , t_n'/t_n]´ and each ´t_i'´ is a fresh abstract type +with lower bound ´\sigma L_i´ and upper bound ´\sigma U_i´. + +#### Simplification Rules + +Existential types obey the following four equivalences: + +1. Multiple for-clauses in an existential type can be merged. E.g., +`´T´ forSome { ´Q´ } forSome { ´Q'´ }` +is equivalent to +`´T´ forSome { ´Q´ ; ´Q'´}`. +1. Unused quantifications can be dropped. E.g., +`´T´ forSome { ´Q´ ; ´Q'´}` +where none of the types defined in ´Q'´ are referred to by ´T´ or ´Q´, +is equivalent to +`´T´ forSome {´ Q ´}`. +1. An empty quantification can be dropped. E.g., +`´T´ forSome { }` is equivalent to ´T´. +1. An existential type `´T´ forSome { ´Q´ }` where ´Q´ contains +a clause `type ´t[\mathit{tps}] >: L <: U´` is equivalent +to the type `´T'´ forSome { ´Q´ }` where ´T'´ results from ´T´ by replacing +every [covariant occurrence](04-basic-declarations-and-definitions.html#variance-annotations) of ´t´ in ´T´ by ´U´ and by +replacing every contravariant occurrence of ´t´ in ´T´ by ´L´. + +#### Existential Quantification over Values + +As a syntactic convenience, the bindings clause +in an existential type may also contain +value declarations `val ´x´: ´T´`. +An existential type `´T´ forSome { ´Q´; val ´x´: ´S\,´;´\,Q'´ }` +is treated as a shorthand for the type +`´T'´ forSome { ´Q´; type ´t´ <: ´S´ with Singleton; ´Q'´ }`, where ´t´ is a +fresh type name and ´T'´ results from ´T´ by replacing every occurrence of +`´x´.type` with ´t´. + +#### Placeholder Syntax for Existential Types + +```ebnf +WildcardType ::= ‘_’ TypeBounds +``` + +Scala supports a placeholder syntax for existential types. +A _wildcard type_ is of the form `_´\;´>:´\,L\,´<:´\,U´`. Both bound +clauses may be omitted. If a lower bound clause `>:´\,L´` is missing, +`>:´\,´scala.Nothing` +is assumed. If an upper bound clause `<:´\,U´` is missing, +`<:´\,´scala.Any` is assumed. A wildcard type is a shorthand for an +existentially quantified type variable, where the existential quantification is +implicit. + +A wildcard type must appear as a type argument of a parameterized type. +Let ´T = p.c[\mathit{targs},T,\mathit{targs}']´ be a parameterized type where +´\mathit{targs}, \mathit{targs}'´ may be empty and +´T´ is a wildcard type `_´\;´>:´\,L\,´<:´\,U´`. Then ´T´ is equivalent to the +existential +type + +```scala +´p.c[\mathit{targs},t,\mathit{targs}']´ forSome { type ´t´ >: ´L´ <: ´U´ } +``` + +where ´t´ is some fresh type variable. +Wildcard types may also appear as parts of [infix types](#infix-types) +, [function types](#function-types), +or [tuple types](#tuple-types). +Their expansion is then the expansion in the equivalent parameterized +type. + +###### Example + +Assume the class definitions + +```scala +class Ref[T] +abstract class Outer { type T } +``` + +Here are some examples of existential types: + +```scala +Ref[T] forSome { type T <: java.lang.Number } +Ref[x.T] forSome { val x: Outer } +Ref[x_type # T] forSome { type x_type <: Outer with Singleton } +``` + +The last two types in this list are equivalent. +An alternative formulation of the first type above using wildcard syntax is: + +```scala +Ref[_ <: java.lang.Number] +``` + +###### Example + +The type `List[List[_]]` is equivalent to the existential type + +```scala +List[List[t] forSome { type t }] +``` + +###### Example + +Assume a covariant type + +```scala +class List[+T] +``` + +The type + +```scala +List[T] forSome { type T <: java.lang.Number } +``` + +is equivalent (by simplification rule 4 above) to + +```scala +List[java.lang.Number] forSome { type T <: java.lang.Number } +``` + +which is in turn equivalent (by simplification rules 2 and 3 above) to +`List[java.lang.Number]`. + +## Non-Value Types + +The types explained in the following do not denote sets of values, nor +do they appear explicitly in programs. They are introduced in this +report as the internal types of defined identifiers. + +### Method Types + +A _method type_ is denoted internally as ´(\mathit{Ps})U´, where ´(\mathit{Ps})´ +is a sequence of parameter names and types ´(p_1:T_1 , \ldots , p_n:T_n)´ +for some ´n \geq 0´ and ´U´ is a (value or method) type. This type +represents named methods that take arguments named ´p_1 , \ldots , p_n´ +of types ´T_1 , \ldots , T_n´ +and that return a result of type ´U´. + +Method types associate to the right: ´(\mathit{Ps}\_1)(\mathit{Ps}\_2)U´ is +treated as ´(\mathit{Ps}\_1)((\mathit{Ps}\_2)U)´. + +A special case are types of methods without any parameters. They are +written here `=> T`. Parameterless methods name expressions +that are re-evaluated each time the parameterless method name is +referenced. + +Method types do not exist as types of values. If a method name is used +as a value, its type is [implicitly converted](06-expressions.html#implicit-conversions) to a +corresponding function type. + +###### Example + +The declarations + +```scala +def a: Int +def b (x: Int): Boolean +def c (x: Int) (y: String, z: String): String +``` + +produce the typings + +```scala +a: => Int +b: (Int) Boolean +c: (Int) (String, String) String +``` + +### Polymorphic Method Types + +A polymorphic method type is denoted internally as `[´\mathit{tps}\,´]´T´` where +`[´\mathit{tps}\,´]` is a type parameter section +`[´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]` +for some ´n \geq 0´ and ´T´ is a +(value or method) type. This type represents named methods that +take type arguments `´S_1 , \ldots , S_n´` which +[conform](#parameterized-types) to the lower bounds +`´L_1 , \ldots , L_n´` and the upper bounds +`´U_1 , \ldots , U_n´` and that yield results of type ´T´. + +###### Example + +The declarations + +```scala +def empty[A]: List[A] +def union[A <: Comparable[A]] (x: Set[A], xs: Set[A]): Set[A] +``` + +produce the typings + +```scala +empty : [A >: Nothing <: Any] List[A] +union : [A >: Nothing <: Comparable[A]] (x: Set[A], xs: Set[A]) Set[A] +``` + +### Type Constructors + +A _type constructor_ is represented internally much like a polymorphic method type. +`[´\pm´ ´a_1´ >: ´L_1´ <: ´U_1 , \ldots , \pm a_n´ >: ´L_n´ <: ´U_n´] ´T´` +represents a type that is expected by a +[type constructor parameter](04-basic-declarations-and-definitions.html#type-parameters) or an +[abstract type constructor binding](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases) with +the corresponding type parameter clause. + +###### Example + +Consider this fragment of the `Iterable[+X]` class: + +```scala +trait Iterable[+X] { + def flatMap[newType[+X] <: Iterable[X], S](f: X => newType[S]): newType[S] +} +``` + +Conceptually, the type constructor `Iterable` is a name for the +anonymous type `[+X] Iterable[X]`, which may be passed to the +`newType` type constructor parameter in `flatMap`. + + + +## Base Types and Member Definitions + +Types of class members depend on the way the members are referenced. +Central here are three notions, namely: +1. the notion of the set of base types of a type ´T´, +1. the notion of a type ´T´ in some class ´C´ seen from some + prefix type ´S´, +1. the notion of the set of member bindings of some type ´T´. + +These notions are defined mutually recursively as follows. + +1. The set of _base types_ of a type is a set of class types, + given as follows. + - The base types of a class type ´C´ with parents ´T_1 , \ldots , T_n´ are + ´C´ itself, as well as the base types of the compound type + `´T_1´ with … with ´T_n´ { ´R´ }`. + - The base types of an aliased type are the base types of its alias. + - The base types of an abstract type are the base types of its upper bound. + - The base types of a parameterized type + `´C´[´T_1 , \ldots , T_n´]` are the base types + of type ´C´, where every occurrence of a type parameter ´a_i´ + of ´C´ has been replaced by the corresponding parameter type ´T_i´. + - The base types of a singleton type `´p´.type` are the base types of + the type of ´p´. + - The base types of a compound type + `´T_1´ with ´\ldots´ with ´T_n´ { ´R´ }` + are the _reduced union_ of the base + classes of all ´T_i´'s. This means: + Let the multi-set ´\mathscr{S}´ be the multi-set-union of the + base types of all ´T_i´'s. + If ´\mathscr{S}´ contains several type instances of the same class, say + `´S^i´#´C´[´T^i_1 , \ldots , T^i_n´]` ´(i \in I)´, then + all those instances + are replaced by one of them which conforms to all + others. It is an error if no such instance exists. It follows that the + reduced union, if it exists, + produces a set of class types, where different types are instances of + different classes. + - The base types of a type selection `´S´#´T´` are + determined as follows. If ´T´ is an alias or abstract type, the + previous clauses apply. Otherwise, ´T´ must be a (possibly + parameterized) class type, which is defined in some class ´B´. Then + the base types of `´S´#´T´` are the base types of ´T´ + in ´B´ seen from the prefix type ´S´. + - The base types of an existential type `´T´ forSome { ´Q´ }` are + all types `´S´ forSome { ´Q´ }` where ´S´ is a base type of ´T´. + +1. The notion of a type ´T´ _in class ´C´ seen from some prefix type ´S´_ + makes sense only if the prefix type ´S´ + has a type instance of class ´C´ as a base type, say + `´S'´#´C´[´T_1 , \ldots , T_n´]`. Then we define as follows. + - If `´S´ = ´\epsilon´.type`, then ´T´ in ´C´ seen from ´S´ is + ´T´ itself. + - Otherwise, if ´S´ is an existential type `´S'´ forSome { ´Q´ }`, and + ´T´ in ´C´ seen from ´S'´ is ´T'´, + then ´T´ in ´C´ seen from ´S´ is `´T'´ forSome {´\,Q\,´}`. + - Otherwise, if ´T´ is the ´i´'th type parameter of some class ´D´, then + - If ´S´ has a base type `´D´[´U_1 , \ldots , U_n´]`, for some type + parameters `[´U_1 , \ldots , U_n´]`, then ´T´ in ´C´ seen from ´S´ + is ´U_i´. + - Otherwise, if ´C´ is defined in a class ´C'´, then + ´T´ in ´C´ seen from ´S´ is the same as ´T´ in ´C'´ seen from ´S'´. + - Otherwise, if ´C´ is not defined in another class, then + ´T´ in ´C´ seen from ´S´ is ´T´ itself. + - Otherwise, if ´T´ is the singleton type `´D´.this.type` for some class ´D´ + then + - If ´D´ is a subclass of ´C´ and ´S´ has a type instance of class ´D´ + among its base types, then ´T´ in ´C´ seen from ´S´ is ´S´. + - Otherwise, if ´C´ is defined in a class ´C'´, then + ´T´ in ´C´ seen from ´S´ is the same as ´T´ in ´C'´ seen from ´S'´. + - Otherwise, if ´C´ is not defined in another class, then + ´T´ in ´C´ seen from ´S´ is ´T´ itself. + - If ´T´ is some other type, then the described mapping is performed + to all its type components. + + If ´T´ is a possibly parameterized class type, where ´T´'s class + is defined in some other class ´D´, and ´S´ is some prefix type, + then we use "´T´ seen from ´S´" as a shorthand for + "´T´ in ´D´ seen from ´S´". + +1. The _member bindings_ of a type ´T´ are + 1. all bindings ´d´ such that there exists a type instance of some class ´C´ among the base types of ´T´ + and there exists a definition or declaration ´d'´ in ´C´ + such that ´d´ results from ´d'´ by replacing every + type ´T'´ in ´d'´ by ´T'´ in ´C´ seen from ´T´, and + 2. all bindings of the type's [refinement](#compound-types), if it has one. + + The _definition_ of a type projection `S#T` is the member + binding ´d_T´ of the type `T` in `S`. In that case, we also say + that `S#T` _is defined by_ ´d_T´. + +## Relations between types + +We define the following relations between types. + +| Name | Symbolically | Interpretation | +|------------------|----------------|----------------------------------------------------| +| Equivalence | ´T \equiv U´ | ´T´ and ´U´ are interchangeable in all contexts. | +| Conformance | ´T <: U´ | Type ´T´ conforms to ("is a subtype of") type ´U´. | +| Weak Conformance | ´T <:_w U´ | Augments conformance for primitive numeric types. | +| Compatibility | | Type ´T´ conforms to type ´U´ after conversions. | + +### Equivalence + +Equivalence ´(\equiv)´ between types is the smallest congruence [^congruence] such that the following holds: + +- If ´t´ is defined by a type alias `type ´t´ = ´T´`, then ´t´ is equivalent to ´T´. +- If a path ´p´ has a singleton type `´q´.type`, then `´p´.type ´\equiv q´.type`. +- If ´O´ is defined by an object definition, and ´p´ is a path consisting only of package or object selectors and ending in ´O´, then `´O´.this.type ´\equiv p´.type`. +- Two [compound types](#compound-types) are equivalent if the sequences + of their component are pairwise equivalent, and occur in the same order, and + their refinements are equivalent. Two refinements are equivalent if they + bind the same names and the modifiers, types and bounds of every + declared entity are equivalent in both refinements. +- Two [method types](#method-types) are equivalent if: + - neither are implicit, or they both are [^implicit]; + - they have equivalent result types; + - they have the same number of parameters; and + - corresponding parameters have equivalent types. + Note that the names of parameters do not matter for method type equivalence. +- Two [polymorphic method types](#polymorphic-method-types) are equivalent if + they have the same number of type parameters, and, after renaming one set of + type parameters by another, the result types as well as lower and upper bounds + of corresponding type parameters are equivalent. +- Two [existential types](#existential-types) + are equivalent if they have the same number of + quantifiers, and, after renaming one list of type quantifiers by + another, the quantified types as well as lower and upper bounds of + corresponding quantifiers are equivalent. +- Two [type constructors](#type-constructors) are equivalent if they have the + same number of type parameters, and, after renaming one list of type + parameters by another, the result types as well as variances, lower and upper + bounds of corresponding type parameters are equivalent. + +[^congruence]: A congruence is an equivalence relation which is closed under formation of contexts. +[^implicit]: A method type is implicit if the parameter section that defines it starts with the `implicit` keyword. + +### Conformance + +The conformance relation ´(<:)´ is the smallest transitive relation that satisfies the following conditions. + + +- Conformance includes equivalence. If `T \equiv U` then `T <: U`. +- For every value type `T`, `scala.Nothing <: ´T´ <: scala.Any`. +- For every type constructor ´T´ (with any number of type parameters), `scala.Nothing <: ´T´ <: scala.Any`. +- For every value type ´T´, `scala.Null <: ´T´` unless `´T´ <: scala.AnyVal`. +- A type variable or abstract type ´t´ conforms to its upper bound and + its lower bound conforms to ´t´. +- A class type or parameterized type conforms to any of its base-types. +- A singleton type `´p´.type` conforms to the type of the path ´p´. +- A singleton type `´p´.type` conforms to the type `scala.Singleton`. +- A type projection `´T´#´t´` conforms to `´U´#´t´` if ´T´ conforms to ´U´. +- A parameterized type `´T´[´T_1´ , … , ´T_n´]` conforms to + `´T´[´U_1´ , … , ´U_n´]` if + the following three conditions hold for ´i \in \{ 1 , \ldots , n \}´: + 1. If the ´i´'th type parameter of ´T´ is declared covariant, then + ´T_i <: U_i´. + 1. If the ´i´'th type parameter of ´T´ is declared contravariant, then + ´U_i <: T_i´. + 1. If the ´i´'th type parameter of ´T´ is declared neither covariant + nor contravariant, then ´U_i \equiv T_i´. +- A compound type `´T_1´ with ´\ldots´ with ´T_n´ {´R\,´}` conforms to + each of its component types ´T_i´. +- If ´T <: U_i´ for ´i \in \{ 1 , \ldots , n \}´ and for every + binding ´d´ of a type or value ´x´ in ´R´ there exists a member + binding of ´x´ in ´T´ which subsumes ´d´, then ´T´ conforms to the + compound type `´U_1´ with ´\ldots´ with ´U_n´ {´R\,´}`. +- The existential type `´T´ forSome {´\,Q\,´}` conforms to + ´U´ if its [skolemization](#existential-types) + conforms to ´U´. +- The type ´T´ conforms to the existential type `´U´ forSome {´\,Q\,´}` + if ´T´ conforms to one of the [type instances](#existential-types) + of `´U´ forSome {´\,Q\,´}`. +- If + ´T_i \equiv T_i'´ for ´i \in \{ 1 , \ldots , n\}´ and ´U´ conforms to ´U'´ + then the method type ´(p_1:T_1 , \ldots , p_n:T_n) U´ conforms to + ´(p_1':T_1' , \ldots , p_n':T_n') U'´. +- The polymorphic type + ´[a_1 >: L_1 <: U_1 , \ldots , a_n >: L_n <: U_n] T´ conforms to the + polymorphic type + ´[a_1 >: L_1' <: U_1' , \ldots , a_n >: L_n' <: U_n'] T'´ if, assuming + ´L_1' <: a_1 <: U_1' , \ldots , L_n' <: a_n <: U_n'´ + one has ´T <: T'´ and ´L_i <: L_i'´ and ´U_i' <: U_i´ + for ´i \in \{ 1 , \ldots , n \}´. +- Type constructors ´T´ and ´T'´ follow a similar discipline. We characterize + ´T´ and ´T'´ by their type parameter clauses + ´[a_1 , \ldots , a_n]´ and + ´[a_1' , \ldots , a_n']´, where an ´a_i´ or ´a_i'´ may include a variance + annotation, a higher-order type parameter clause, and bounds. Then, ´T´ + conforms to ´T'´ if any list ´[t_1 , \ldots , t_n]´ -- with declared + variances, bounds and higher-order type parameter clauses -- of valid type + arguments for ´T'´ is also a valid list of type arguments for ´T´ and + ´T[t_1 , \ldots , t_n] <: T'[t_1 , \ldots , t_n]´. Note that this entails + that: + - The bounds on ´a_i´ must be weaker than the corresponding bounds declared + for ´a'_i´. + - The variance of ´a_i´ must match the variance of ´a'_i´, where covariance + matches covariance, contravariance matches contravariance and any variance + matches invariance. + - Recursively, these restrictions apply to the corresponding higher-order + type parameter clauses of ´a_i´ and ´a'_i´. + +A declaration or definition in some compound type of class type ´C´ +_subsumes_ another declaration of the same name in some compound type or class +type ´C'´, if one of the following holds. + +- A value declaration or definition that defines a name ´x´ with type ´T´ + subsumes a value or method declaration that defines ´x´ with type ´T'´, provided + ´T <: T'´. +- A method declaration or definition that defines a name ´x´ with type ´T´ + subsumes a method declaration that defines ´x´ with type ´T'´, provided + ´T <: T'´. +- A type alias + `type ´t´[´T_1´ , … , ´T_n´] = ´T´` subsumes a type alias + `type ´t´[´T_1´ , … , ´T_n´] = ´T'´` if ´T \equiv T'´. +- A type declaration `type ´t´[´T_1´ , … , ´T_n´] >: ´L´ <: ´U´` subsumes + a type declaration `type ´t´[´T_1´ , … , ´T_n´] >: ´L'´ <: ´U'´` if + ´L' <: L´ and ´U <: U'´. +- A type or class definition that binds a type name ´t´ subsumes an abstract + type declaration `type t[´T_1´ , … , ´T_n´] >: L <: U` if + ´L <: t <: U´. + + +#### Least upper bounds and greatest lower bounds +The ´(<:)´ relation forms pre-order between types, i.e. it is transitive and reflexive. +This allows us to define _least upper bounds_ and _greatest lower bounds_ of a set of types in terms of that order. +The least upper bound or greatest lower bound of a set of types does not always exist. +For instance, consider the class definitions: + +```scala +class A[+T] {} +class B extends A[B] +class C extends A[C] +``` + +Then the types `A[Any], A[A[Any]], A[A[A[Any]]], ...` form +a descending sequence of upper bounds for `B` and `C`. The +least upper bound would be the infinite limit of that sequence, which +does not exist as a Scala type. Since cases like this are in general +impossible to detect, a Scala compiler is free to reject a term +which has a type specified as a least upper or greatest lower bound, +and that bound would be more complex than some compiler-set +limit [^4]. + +The least upper bound or greatest lower bound might also not be +unique. For instance `A with B` and `B with A` are both +greatest lower bounds of `A` and `B`. If there are several +least upper bounds or greatest lower bounds, the Scala compiler is +free to pick any one of them. + +[^4]: The current Scala compiler limits the nesting level + of parameterization in such bounds to be at most two deeper than the + maximum nesting level of the operand types + +### Weak Conformance + +In some situations Scala uses a more general conformance relation. +A type ´S´ _weakly conforms_ to a type ´T´, written ´S <:_w T´, +if ´S <: T´ or both ´S´ and ´T´ are primitive number types and ´S´ precedes ´T´ in the following ordering. + +```scala +Byte ´<:_w´ Short +Short ´<:_w´ Int +Char ´<:_w´ Int +Int ´<:_w´ Long +Long ´<:_w´ Float +Float ´<:_w´ Double +``` + +A _weak least upper bound_ is a least upper bound with respect to weak conformance. + +### Compatibility +A type ´T´ is _compatible_ to a type ´U´ if ´T´ (or its corresponding function type) [weakly conforms](#weak-conformance) to ´U´ +after applying [eta-expansion](06-expressions.html#eta-expansion). If ´T´ is a method type, it's converted to the corresponding function type. If the types do not weakly conform, the following alternatives are checked in order: + - [view application](07-implicits.html#views): there's an implicit view from ´T´ to ´U´; + - dropping by-name modifiers: if ´U´ is of the shape `´=> U'´` (and ´T´ is not), `´T <:_w U'´`; + - SAM conversion: if ´T´ corresponds to a function type, and ´U´ declares a single abstract method whose type [corresponds](06-expressions.html#sam-conversion) to the function type ´U'´, `´T <:_w U'´`. + + + +#### Examples + +##### Function compatibility via SAM conversion + +Given the definitions + +```scala +def foo(x: Int => String): Unit +def foo(x: ToString): Unit + +trait ToString { def convert(x: Int): String } +``` + +The application `foo((x: Int) => x.toString)` [resolves](06-expressions.html#overloading-resolution) to the first overload, +as it's more specific: + - `Int => String` is compatible to `ToString` -- when expecting a value of type `ToString`, you may pass a function literal from `Int` to `String`, as it will be SAM-converted to said function; + - `ToString` is not compatible to `Int => String` -- when expecting a function from `Int` to `String`, you may not pass a `ToString`. + +## Volatile Types + +Type volatility approximates the possibility that a type parameter or +abstract type instance of a type does not have any non-null values. +A value member of a volatile type cannot appear in a [path](#paths). + +A type is _volatile_ if it falls into one of four categories: + +A compound type `´T_1´ with … with ´T_n´ {´R\,´}` +is volatile if one of the following three conditions hold. + +1. One of ´T_2 , \ldots , T_n´ is a type parameter or abstract type, or +1. ´T_1´ is an abstract type and either the refinement ´R´ + or a type ´T_j´ for ´j > 1´ contributes an abstract member + to the compound type, or +1. one of ´T_1 , \ldots , T_n´ is a singleton type. + +Here, a type ´S´ _contributes an abstract member_ to a type ´T´ if +´S´ contains an abstract member that is also a member of ´T´. +A refinement ´R´ contributes an abstract member to a type ´T´ if ´R´ +contains an abstract declaration which is also a member of ´T´. + +A type designator is volatile if it is an alias of a volatile type, or +if it designates a type parameter or abstract type that has a volatile type as +its upper bound. + +A singleton type `´p´.type` is volatile, if the underlying +type of path ´p´ is volatile. + +An existential type `´T´ forSome {´\,Q\,´}` is volatile if +´T´ is volatile. + +## Type Erasure + +A type is called _generic_ if it contains type arguments or type variables. +_Type erasure_ is a mapping from (possibly generic) types to +non-generic types. We write ´|T|´ for the erasure of type ´T´. +The erasure mapping is defined as follows. + +- The erasure of an alias type is the erasure of its right-hand side. +- The erasure of an abstract type is the erasure of its upper bound. +- The erasure of the parameterized type `scala.Array´[T_1]´` is + `scala.Array´[|T_1|]´`. +- The erasure of every other parameterized type ´T[T_1 , \ldots , T_n]´ is ´|T|´. +- The erasure of a singleton type `´p´.type` is the + erasure of the type of ´p´. +- The erasure of a type projection `´T´#´x´` is `|´T´|#´x´`. +- The erasure of a compound type + `´T_1´ with ´\ldots´ with ´T_n´ {´R\,´}` is the erasure of the intersection + dominator of ´T_1 , \ldots , T_n´. +- The erasure of an existential type `´T´ forSome {´\,Q\,´}` is ´|T|´. + +The _intersection dominator_ of a list of types ´T_1 , \ldots , T_n´ is computed +as follows. +Let ´T_{i_1} , \ldots , T_{i_m}´ be the subsequence of types ´T_i´ +which are not supertypes of some other type ´T_j´. +If this subsequence contains a type designator ´T_c´ that refers to a class +which is not a trait, +the intersection dominator is ´T_c´. Otherwise, the intersection +dominator is the first element of the subsequence, ´T_{i_1}´. diff --git a/docs/_spec/04-basic-declarations-and-definitions.md b/docs/_spec/04-basic-declarations-and-definitions.md new file mode 100644 index 000000000000..acaf2491d99b --- /dev/null +++ b/docs/_spec/04-basic-declarations-and-definitions.md @@ -0,0 +1,945 @@ +--- +title: Basic Declarations & Definitions +layout: default +chapter: 4 +--- + +# Basic Declarations and Definitions + +```ebnf +Dcl ::= ‘val’ ValDcl + | ‘var’ VarDcl + | ‘def’ FunDcl + | ‘type’ {nl} TypeDcl +PatVarDef ::= ‘val’ PatDef + | ‘var’ VarDef +Def ::= PatVarDef + | ‘def’ FunDef + | ‘type’ {nl} TypeDef + | TmplDef +``` + +A _declaration_ introduces names and assigns them types. It can +form part of a [class definition](05-classes-and-objects.html#templates) or of a +refinement in a [compound type](03-types.html#compound-types). + +A _definition_ introduces names that denote terms or types. It can +form part of an object or class definition or it can be local to a +block. Both declarations and definitions produce _bindings_ that +associate type names with type definitions or bounds, and that +associate term names with types. + +The scope of a name introduced by a declaration or definition is the +whole statement sequence containing the binding. However, there is a +restriction on forward references in blocks: In a statement sequence +´s_1 \ldots s_n´ making up a block, if a simple name in ´s_i´ refers +to an entity defined by ´s_j´ where ´j \geq i´, then for all ´s_k´ +between and including ´s_i´ and ´s_j´, + +- ´s_k´ cannot be a variable definition. +- If ´s_k´ is a value definition, it must be lazy. + + + +## Value Declarations and Definitions + +```ebnf +Dcl ::= ‘val’ ValDcl +ValDcl ::= ids ‘:’ Type +PatVarDef ::= ‘val’ PatDef +PatDef ::= Pattern2 {‘,’ Pattern2} [‘:’ Type] ‘=’ Expr +ids ::= id {‘,’ id} +``` + +A value declaration `val ´x´: ´T´` introduces ´x´ as a name of a value of +type ´T´. + +A value definition `val ´x´: ´T´ = ´e´` defines ´x´ as a +name of the value that results from the evaluation of ´e´. +If the value definition is not recursive, the type +´T´ may be omitted, in which case the [packed type](06-expressions.html#expression-typing) of +expression ´e´ is assumed. If a type ´T´ is given, then ´e´ is expected to +conform to it. + +Evaluation of the value definition implies evaluation of its +right-hand side ´e´, unless it has the modifier `lazy`. The +effect of the value definition is to bind ´x´ to the value of ´e´ +converted to type ´T´. A `lazy` value definition evaluates +its right hand side ´e´ the first time the value is accessed. + +A _constant value definition_ is of the form + +```scala +final val x = e +``` + +where `e` is a [constant expression](06-expressions.html#constant-expressions). +The `final` modifier must be +present and no type annotation may be given. References to the +constant value `x` are themselves treated as constant expressions; in the +generated code they are replaced by the definition's right-hand side `e`. + +Value definitions can alternatively have a [pattern](08-pattern-matching.html#patterns) +as left-hand side. If ´p´ is some pattern other +than a simple name or a name followed by a colon and a type, then the +value definition `val ´p´ = ´e´` is expanded as follows: + +1. If the pattern ´p´ has bound variables ´x_1 , \ldots , x_n´, where ´n > 1´: + +```scala +val ´\$x´ = ´e´ match {case ´p´ => (´x_1 , \ldots , x_n´)} +val ´x_1´ = ´\$x´._1 +´\ldots´ +val ´x_n´ = ´\$x´._n +``` + +Here, ´\$x´ is a fresh name. + +2. If ´p´ has a unique bound variable ´x´: + +```scala +val ´x´ = ´e´ match { case ´p´ => ´x´ } +``` + +3. If ´p´ has no bound variables: + +```scala +´e´ match { case ´p´ => ()} +``` + +###### Example + +The following are examples of value definitions + +```scala +val pi = 3.1415 +val pi: Double = 3.1415 // equivalent to first definition +val Some(x) = f() // a pattern definition +val x :: xs = mylist // an infix pattern definition +``` + +The last two definitions have the following expansions. + +```scala +val x = f() match { case Some(x) => x } + +val x´\$´ = mylist match { case x :: xs => (x, xs) } +val x = x´\$´._1 +val xs = x´\$´._2 +``` + +The name of any declared or defined value may not end in `_=`. + +A value declaration `val ´x_1 , \ldots , x_n´: ´T´` is a shorthand for the +sequence of value declarations `val ´x_1´: ´T´; ...; val ´x_n´: ´T´`. +A value definition `val ´p_1 , \ldots , p_n´ = ´e´` is a shorthand for the +sequence of value definitions `val ´p_1´ = ´e´; ...; val ´p_n´ = ´e´`. +A value definition `val ´p_1 , \ldots , p_n: T´ = ´e´` is a shorthand for the +sequence of value definitions `val ´p_1: T´ = ´e´; ...; val ´p_n: T´ = ´e´`. + +## Variable Declarations and Definitions + +```ebnf +Dcl ::= ‘var’ VarDcl +PatVarDef ::= ‘var’ VarDef +VarDcl ::= ids ‘:’ Type +VarDef ::= PatDef + | ids ‘:’ Type ‘=’ ‘_’ +``` + +A variable declaration `var ´x´: ´T´` is equivalent to the declarations +of both a _getter function_ ´x´ *and* a _setter function_ `´x´_=`: + +```scala +def ´x´: ´T´ +def ´x´_= (´y´: ´T´): Unit +``` + +An implementation of a class may _define_ a declared variable +using a variable definition, or by defining the corresponding setter and getter methods. + +A variable definition `var ´x´: ´T´ = ´e´` introduces a +mutable variable with type ´T´ and initial value as given by the +expression ´e´. The type ´T´ can be omitted, in which case the type of +´e´ is assumed. If ´T´ is given, then ´e´ is expected to +[conform to it](06-expressions.html#expression-typing). + +Variable definitions can alternatively have a [pattern](08-pattern-matching.html#patterns) +as left-hand side. A variable definition + `var ´p´ = ´e´` where ´p´ is a pattern other +than a simple name or a name followed by a colon and a type is expanded in the same way +as a [value definition](#value-declarations-and-definitions) +`val ´p´ = ´e´`, except that +the free names in ´p´ are introduced as mutable variables, not values. + +The name of any declared or defined variable may not end in `_=`. + +A variable definition `var ´x´: ´T´ = _` can appear only as a member of a template. +It introduces a mutable field with type ´T´ and a default initial value. +The default value depends on the type ´T´ as follows: + +| default | type ´T´ | +|----------|------------------------------------| +|`0` | `Int` or one of its subrange types | +|`0L` | `Long` | +|`0.0f` | `Float` | +|`0.0d` | `Double` | +|`false` | `Boolean` | +|`()` | `Unit` | +|`null` | all other types | + +When they occur as members of a template, both forms of variable +definition also introduce a getter function ´x´ which returns the +value currently assigned to the variable, as well as a setter function +`´x´_=` which changes the value currently assigned to the variable. +The functions have the same signatures as for a variable declaration. +The template then has these getter and setter functions as +members, whereas the original variable cannot be accessed directly as +a template member. + +###### Example + +The following example shows how _properties_ can be +simulated in Scala. It defines a class `TimeOfDayVar` of time +values with updatable integer fields representing hours, minutes, and +seconds. Its implementation contains tests that allow only legal +values to be assigned to these fields. The user code, on the other +hand, accesses these fields just like normal variables. + +```scala +class TimeOfDayVar { + private var h: Int = 0 + private var m: Int = 0 + private var s: Int = 0 + + def hours = h + def hours_= (h: Int) = if (0 <= h && h < 24) this.h = h + else throw new DateError() + + def minutes = m + def minutes_= (m: Int) = if (0 <= m && m < 60) this.m = m + else throw new DateError() + + def seconds = s + def seconds_= (s: Int) = if (0 <= s && s < 60) this.s = s + else throw new DateError() +} +val d = new TimeOfDayVar +d.hours = 8; d.minutes = 30; d.seconds = 0 +d.hours = 25 // throws a DateError exception +``` + +A variable declaration `var ´x_1 , \ldots , x_n´: ´T´` is a shorthand for the +sequence of variable declarations `var ´x_1´: ´T´; ...; var ´x_n´: ´T´`. +A variable definition `var ´x_1 , \ldots , x_n´ = ´e´` is a shorthand for the +sequence of variable definitions `var ´x_1´ = ´e´; ...; var ´x_n´ = ´e´`. +A variable definition `var ´x_1 , \ldots , x_n: T´ = ´e´` is a shorthand for +the sequence of variable definitions +`var ´x_1: T´ = ´e´; ...; var ´x_n: T´ = ´e´`. + +## Type Declarations and Type Aliases + + + +```ebnf +Dcl ::= ‘type’ {nl} TypeDcl +TypeDcl ::= id [TypeParamClause] [‘>:’ Type] [‘<:’ Type] +Def ::= ‘type’ {nl} TypeDef +TypeDef ::= id [TypeParamClause] ‘=’ Type +``` + +A _type declaration_ `type ´t´[´\mathit{tps}\,´] >: ´L´ <: ´U´` declares +´t´ to be an abstract type with lower bound type ´L´ and upper bound +type ´U´. If the type parameter clause `[´\mathit{tps}\,´]` is omitted, ´t´ abstracts over a proper type, otherwise ´t´ stands for a type constructor that accepts type arguments as described by the type parameter clause. + +If a type declaration appears as a member declaration of a +type, implementations of the type may implement ´t´ with any type ´T´ +for which ´L <: T <: U´. It is a compile-time error if +´L´ does not conform to ´U´. Either or both bounds may be omitted. +If the lower bound ´L´ is absent, the bottom type +`scala.Nothing` is assumed. If the upper bound ´U´ is absent, +the top type `scala.Any` is assumed. + +A type constructor declaration imposes additional restrictions on the +concrete types for which ´t´ may stand. Besides the bounds ´L´ and +´U´, the type parameter clause may impose higher-order bounds and +variances, as governed by the [conformance of type constructors](03-types.html#conformance). + +The scope of a type parameter extends over the bounds `>: ´L´ <: ´U´` and the type parameter clause ´\mathit{tps}´ itself. A +higher-order type parameter clause (of an abstract type constructor +´tc´) has the same kind of scope, restricted to the declaration of the +type parameter ´tc´. + +To illustrate nested scoping, these declarations are all equivalent: `type t[m[x] <: Bound[x], Bound[x]]`, `type t[m[x] <: Bound[x], Bound[y]]` and `type t[m[x] <: Bound[x], Bound[_]]`, as the scope of, e.g., the type parameter of ´m´ is limited to the declaration of ´m´. In all of them, ´t´ is an abstract type member that abstracts over two type constructors: ´m´ stands for a type constructor that takes one type parameter and that must be a subtype of ´Bound´, ´t´'s second type constructor parameter. `t[MutableList, Iterable]` is a valid use of ´t´. + +A _type alias_ `type ´t´ = ´T´` defines ´t´ to be an alias +name for the type ´T´. The left hand side of a type alias may +have a type parameter clause, e.g. `type ´t´[´\mathit{tps}\,´] = ´T´`. The scope +of a type parameter extends over the right hand side ´T´ and the +type parameter clause ´\mathit{tps}´ itself. + +The scope rules for [definitions](#basic-declarations-and-definitions) +and [type parameters](#function-declarations-and-definitions) +make it possible that a type name appears in its +own bound or in its right-hand side. However, it is a static error if +a type alias refers recursively to the defined type constructor itself. +That is, the type ´T´ in a type alias `type ´t´[´\mathit{tps}\,´] = ´T´` may not +refer directly or indirectly to the name ´t´. It is also an error if +an abstract type is directly or indirectly its own upper or lower bound. + +###### Example + +The following are legal type declarations and definitions: + +```scala +type IntList = List[Integer] +type T <: Comparable[T] +type Two[A] = Tuple2[A, A] +type MyCollection[+X] <: Iterable[X] +``` + +The following are illegal: + +```scala +type Abs = Comparable[Abs] // recursive type alias + +type S <: T // S, T are bounded by themselves. +type T <: S + +type T >: Comparable[T.That] // Cannot select from T. + // T is a type, not a value +type MyCollection <: Iterable // Type constructor members must explicitly + // state their type parameters. +``` + +If a type alias `type ´t´[´\mathit{tps}\,´] = ´S´` refers to a class type +´S´, the name ´t´ can also be used as a constructor for +objects of type ´S´. + +###### Example + +Suppose we make `Pair` an alias of the parameterized class `Tuple2`, +as follows: + +```scala +type Pair[+A, +B] = Tuple2[A, B] +object Pair { + def apply[A, B](x: A, y: B) = Tuple2(x, y) + def unapply[A, B](x: Tuple2[A, B]): Option[Tuple2[A, B]] = Some(x) +} +``` + +As a consequence, for any two types ´S´ and ´T´, the type +`Pair[´S´, ´T\,´]` is equivalent to the type `Tuple2[´S´, ´T\,´]`. +`Pair` can also be used as a constructor instead of `Tuple2`, as in: + +```scala +val x: Pair[Int, String] = new Pair(1, "abc") +``` + +## Type Parameters + +```ebnf +TypeParamClause ::= ‘[’ VariantTypeParam {‘,’ VariantTypeParam} ‘]’ +VariantTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeParam +TypeParam ::= (id | ‘_’) [TypeParamClause] [‘>:’ Type] [‘<:’ Type] [‘:’ Type] +``` + +Type parameters appear in type definitions, class definitions, and +function definitions. In this section we consider only type parameter +definitions with lower bounds `>: ´L´` and upper bounds +`<: ´U´` whereas a discussion of context bounds +`: ´U´` and view bounds `<% ´U´` +is deferred to [here](07-implicits.html#context-bounds-and-view-bounds). + +The most general form of a proper type parameter is +`´@a_1 \ldots @a_n´ ´\pm´ ´t´ >: ´L´ <: ´U´`. +Here, ´L´, and ´U´ are lower and upper bounds that +constrain possible type arguments for the parameter. It is a +compile-time error if ´L´ does not conform to ´U´. ´\pm´ is a _variance_, i.e. an optional prefix of either `+`, or +`-`. One or more annotations may precede the type parameter. + + + + + +The names of all type parameters must be pairwise different in their enclosing type parameter clause. The scope of a type parameter includes in each case the whole type parameter clause. Therefore it is possible that a type parameter appears as part of its own bounds or the bounds of other type parameters in the same clause. However, a type parameter may not be bounded directly or indirectly by itself. + +A type constructor parameter adds a nested type parameter clause to the type parameter. The most general form of a type constructor parameter is `´@a_1 \ldots @a_n \pm t[\mathit{tps}\,]´ >: ´L´ <: ´U´`. + +The above scoping restrictions are generalized to the case of nested type parameter clauses, which declare higher-order type parameters. Higher-order type parameters (the type parameters of a type parameter ´t´) are only visible in their immediately surrounding parameter clause (possibly including clauses at a deeper nesting level) and in the bounds of ´t´. Therefore, their names must only be pairwise different from the names of other visible parameters. Since the names of higher-order type parameters are thus often irrelevant, they may be denoted with a `‘_’`, which is nowhere visible. + +###### Example +Here are some well-formed type parameter clauses: + +```scala +[S, T] +[@specialized T, U] +[Ex <: Throwable] +[A <: Comparable[B], B <: A] +[A, B >: A, C >: A <: B] +[M[X], N[X]] +[M[_], N[_]] // equivalent to previous clause +[M[X <: Bound[X]], Bound[_]] +[M[+X] <: Iterable[X]] +``` + +The following type parameter clauses are illegal: + +```scala +[A >: A] // illegal, `A' has itself as bound +[A <: B, B <: C, C <: A] // illegal, `A' has itself as bound +[A, B, C >: A <: B] // illegal lower bound `A' of `C' does + // not conform to upper bound `B'. +``` + +## Variance Annotations + +Variance annotations indicate how instances of parameterized types +vary with respect to [subtyping](03-types.html#conformance). A +‘+’ variance indicates a covariant dependency, a +‘-’ variance indicates a contravariant dependency, and a +missing variance indication indicates an invariant dependency. + +A variance annotation constrains the way the annotated type variable +may appear in the type or class which binds the type parameter. In a +type definition `type ´T´[´\mathit{tps}\,´] = ´S´`, or a type +declaration `type ´T´[´\mathit{tps}\,´] >: ´L´ <: ´U´` type parameters labeled +‘+’ must only appear in covariant position whereas +type parameters labeled ‘-’ must only appear in contravariant +position. Analogously, for a class definition +`class ´C´[´\mathit{tps}\,´](´\mathit{ps}\,´) extends ´T´ { ´x´: ´S´ => ...}`, +type parameters labeled +‘+’ must only appear in covariant position in the +self type ´S´ and the template ´T´, whereas type +parameters labeled ‘-’ must only appear in contravariant +position. + +The variance position of a type parameter in a type or template is +defined as follows. Let the opposite of covariance be contravariance, +and the opposite of invariance be itself. The top-level of the type +or template is always in covariant position. The variance position +changes at the following constructs. + +- The variance position of a method parameter is the opposite of the + variance position of the enclosing parameter clause. +- The variance position of a type parameter is the opposite of the + variance position of the enclosing type parameter clause. +- The variance position of the lower bound of a type declaration or type parameter + is the opposite of the variance position of the type declaration or parameter. +- The type of a mutable variable is always in invariant position. +- The right-hand side of a type alias is always in invariant position. +- The prefix ´S´ of a type selection `´S´#´T´` is always in invariant position. +- For a type argument ´T´ of a type `´S´[´\ldots T \ldots´ ]`: If the + corresponding type parameter is invariant, then ´T´ is in + invariant position. If the corresponding type parameter is + contravariant, the variance position of ´T´ is the opposite of + the variance position of the enclosing type `´S´[´\ldots T \ldots´ ]`. + + + +References to the type parameters in +[object-private or object-protected values, types, variables, or methods](05-classes-and-objects.html#modifiers) of the class are not +checked for their variance position. In these members the type parameter may +appear anywhere without restricting its legal variance annotations. + +###### Example +The following variance annotation is legal. + +```scala +abstract class P[+A, +B] { + def fst: A; def snd: B +} +``` + +With this variance annotation, type instances +of ´P´ subtype covariantly with respect to their arguments. +For instance, + +```scala +P[IOException, String] <: P[Throwable, AnyRef] +``` + +If the members of ´P´ are mutable variables, +the same variance annotation becomes illegal. + +```scala +abstract class Q[+A, +B](x: A, y: B) { + var fst: A = x // **** error: illegal variance: + var snd: B = y // `A', `B' occur in invariant position. +} +``` + +If the mutable variables are object-private, the class definition +becomes legal again: + +```scala +abstract class R[+A, +B](x: A, y: B) { + private[this] var fst: A = x // OK + private[this] var snd: B = y // OK +} +``` + +###### Example + +The following variance annotation is illegal, since ´a´ appears +in contravariant position in the parameter of `append`: + +```scala +abstract class Sequence[+A] { + def append(x: Sequence[A]): Sequence[A] + // **** error: illegal variance: + // `A' occurs in contravariant position. +} +``` + +The problem can be avoided by generalizing the type of `append` +by means of a lower bound: + +```scala +abstract class Sequence[+A] { + def append[B >: A](x: Sequence[B]): Sequence[B] +} +``` + +###### Example + +```scala +abstract class OutputChannel[-A] { + def write(x: A): Unit +} +``` + +With that annotation, we have that +`OutputChannel[AnyRef]` conforms to `OutputChannel[String]`. +That is, a +channel on which one can write any object can substitute for a channel +on which one can write only strings. + +## Function Declarations and Definitions + +```ebnf +Dcl ::= ‘def’ FunDcl +FunDcl ::= FunSig ‘:’ Type +Def ::= ‘def’ FunDef +FunDef ::= FunSig [‘:’ Type] ‘=’ Expr +FunSig ::= id [FunTypeParamClause] ParamClauses +FunTypeParamClause ::= ‘[’ TypeParam {‘,’ TypeParam} ‘]’ +ParamClauses ::= {ParamClause} [[nl] ‘(’ ‘implicit’ Params ‘)’] +ParamClause ::= [nl] ‘(’ [Params] ‘)’ +Params ::= Param {‘,’ Param} +Param ::= {Annotation} id [‘:’ ParamType] [‘=’ Expr] +ParamType ::= Type + | ‘=>’ Type + | Type ‘*’ +``` + +A _function declaration_ has the form `def ´f\,\mathit{psig}´: ´T´`, where +´f´ is the function's name, ´\mathit{psig}´ is its parameter +signature and ´T´ is its result type. A _function definition_ +`def ´f\,\mathit{psig}´: ´T´ = ´e´` also includes a _function body_ ´e´, +i.e. an expression which defines the function's result. A parameter +signature consists of an optional type parameter clause `[´\mathit{tps}\,´]`, +followed by zero or more value parameter clauses +`(´\mathit{ps}_1´)´\ldots´(´\mathit{ps}_n´)`. Such a declaration or definition +introduces a value with a (possibly polymorphic) method type whose +parameter types and result type are as given. + +The type of the function body is expected to [conform](06-expressions.html#expression-typing) +to the function's declared +result type, if one is given. If the function definition is not +recursive, the result type may be omitted, in which case it is +determined from the packed type of the function body. + +A _type parameter clause_ ´\mathit{tps}´ consists of one or more +[type declarations](#type-declarations-and-type-aliases), which introduce type +parameters, possibly with bounds. The scope of a type parameter includes +the whole signature, including any of the type parameter bounds as +well as the function body, if it is present. + +A _value parameter clause_ ´\mathit{ps}´ consists of zero or more formal +parameter bindings such as `´x´: ´T´` or `´x: T = e´`, which bind value +parameters and associate them with their types. + +### Default Arguments + +Each value parameter +declaration may optionally define a default argument. The default argument +expression ´e´ is type-checked with an expected type ´T'´ obtained +by replacing all occurrences of the function's type parameters in ´T´ by +the undefined type. + +For every parameter ´p_{i,j}´ with a default argument a method named +`´f\$´default´\$´n` is generated which computes the default argument +expression. Here, ´n´ denotes the parameter's position in the method +declaration. These methods are parametrized by the type parameter clause +`[´\mathit{tps}\,´]` and all value parameter clauses +`(´\mathit{ps}_1´)´\ldots´(´\mathit{ps}_{i-1}´)` preceding ´p_{i,j}´. +The `´f\$´default´\$´n` methods are inaccessible for user programs. + +###### Example +In the method + +```scala +def compare[T](a: T = 0)(b: T = a) = (a == b) +``` + +the default expression `0` is type-checked with an undefined expected +type. When applying `compare()`, the default value `0` is inserted +and `T` is instantiated to `Int`. The methods computing the default +arguments have the form: + +```scala +def compare´\$´default´\$´1[T]: Int = 0 +def compare´\$´default´\$´2[T](a: T): T = a +``` + +The scope of a formal value parameter name ´x´ comprises all subsequent +parameter clauses, as well as the method return type and the function body, if +they are given. Both type parameter names and value parameter names must +be pairwise distinct. + +A default value which depends on earlier parameters uses the actual arguments +if they are provided, not the default arguments. + +```scala +def f(a: Int = 0)(b: Int = a + 1) = b // OK +// def f(a: Int = 0, b: Int = a + 1) // "error: not found: value a" +f(10)() // returns 11 (not 1) +``` + +If an [implicit argument](07-implicits.html#implicit-parameters) +is not found by implicit search, it may be supplied using a default argument. + +```scala +implicit val i: Int = 2 +def f(implicit x: Int, s: String = "hi") = s * x +f // "hihi" +``` + +### By-Name Parameters + +```ebnf +ParamType ::= ‘=>’ Type +``` + +The type of a value parameter may be prefixed by `=>`, e.g. +`´x´: => ´T´`. The type of such a parameter is then the +parameterless method type `=> ´T´`. This indicates that the +corresponding argument is not evaluated at the point of function +application, but instead is evaluated at each use within the +function. That is, the argument is evaluated using _call-by-name_. + +The by-name modifier is disallowed for parameters of classes that +carry a `val` or `var` prefix, including parameters of case +classes for which a `val` prefix is implicitly generated. + +###### Example +The declaration + +```scala +def whileLoop (cond: => Boolean) (stat: => Unit): Unit +``` + +indicates that both parameters of `whileLoop` are evaluated using +call-by-name. + +### Repeated Parameters + +```ebnf +ParamType ::= Type ‘*’ +``` + +The last value parameter of a parameter section may be suffixed by +`'*'`, e.g. `(..., ´x´:´T´*)`. The type of such a +_repeated_ parameter inside the method is then the sequence type +`scala.Seq[´T´]`. Methods with repeated parameters +`´T´*` take a variable number of arguments of type ´T´. +That is, if a method ´m´ with type +`(´p_1:T_1 , \ldots , p_n:T_n, p_s:S´*)´U´` is applied to arguments +´(e_1 , \ldots , e_k)´ where ´k \geq n´, then ´m´ is taken in that application +to have type ´(p_1:T_1 , \ldots , p_n:T_n, p_s:S , \ldots , p_{s'}:S)U´, with +´k - n´ occurrences of type +´S´ where any parameter names beyond ´p_s´ are fresh. The only exception to +this rule is if the last argument is +marked to be a _sequence argument_ via a `_*` type +annotation. If ´m´ above is applied to arguments +`(´e_1 , \ldots , e_n, e'´: _*)`, then the type of ´m´ in +that application is taken to be +`(´p_1:T_1, \ldots , p_n:T_n,p_{s}:´scala.Seq[´S´])`. + +It is not allowed to define any default arguments in a parameter section +with a repeated parameter. + +###### Example +The following method definition computes the sum of the squares of a +variable number of integer arguments. + +```scala +def sum(args: Int*) = { + var result = 0 + for (arg <- args) result += arg + result +} +``` + +The following applications of this method yield `0`, `1`, +`6`, in that order. + +```scala +sum() +sum(1) +sum(1, 2, 3) +``` + +Furthermore, assume the definition: + +```scala +val xs = List(1, 2, 3) +``` + +The following application of method `sum` is ill-formed: + +```scala +sum(xs) // ***** error: expected: Int, found: List[Int] +``` + +By contrast, the following application is well formed and yields again +the result `6`: + +```scala +sum(xs: _*) +``` + +### Procedures + +```ebnf +FunDcl ::= FunSig +FunDef ::= FunSig [nl] ‘{’ Block ‘}’ +``` + +Special syntax exists for procedures, i.e. functions that return the +`Unit` value `()`. +A _procedure declaration_ is a function declaration where the result type +is omitted. The result type is then implicitly completed to the +`Unit` type. E.g., `def ´f´(´\mathit{ps}´)` is equivalent to +`def ´f´(´\mathit{ps}´): Unit`. + +A _procedure definition_ is a function definition where the result type +and the equals sign are omitted; its defining expression must be a block. +E.g., `def ´f´(´\mathit{ps}´) {´\mathit{stats}´}` is equivalent to +`def ´f´(´\mathit{ps}´): Unit = {´\mathit{stats}´}`. + +###### Example +Here is a declaration and a definition of a procedure named `write`: + +```scala +trait Writer { + def write(str: String) +} +object Terminal extends Writer { + def write(str: String) { System.out.println(str) } +} +``` + +The code above is implicitly completed to the following code: + +```scala +trait Writer { + def write(str: String): Unit +} +object Terminal extends Writer { + def write(str: String): Unit = { System.out.println(str) } +} +``` + +### Method Return Type Inference + +A class member definition ´m´ that overrides some other function ´m'´ +in a base class of ´C´ may leave out the return type, even if it is +recursive. In this case, the return type ´R'´ of the overridden +function ´m'´, seen as a member of ´C´, is taken as the return type of +´m´ for each recursive invocation of ´m´. That way, a type ´R´ for the +right-hand side of ´m´ can be determined, which is then taken as the +return type of ´m´. Note that ´R´ may be different from ´R'´, as long +as ´R´ conforms to ´R'´. + +###### Example +Assume the following definitions: + +```scala +trait I { + def factorial(x: Int): Int +} +class C extends I { + def factorial(x: Int) = if (x == 0) 1 else x * factorial(x - 1) +} +``` + +Here, it is OK to leave out the result type of `factorial` +in `C`, even though the method is recursive. + + + +## Import Clauses + +```ebnf +Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} +ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors) +ImportSelectors ::= ‘{’ {ImportSelector ‘,’} + (ImportSelector | ‘_’) ‘}’ +ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’] +``` + +An import clause has the form `import ´p´.´I´` where ´p´ is a +[stable identifier](03-types.html#paths) and ´I´ is an import expression. +The import expression determines a set of names of importable members of ´p´ +which are made available without qualification. A member ´m´ of ´p´ is +_importable_ if it is [accessible](05-classes-and-objects.html#modifiers). +The most general form of an import expression is a list of _import selectors_ + +```scala +{ ´x_1´ => ´y_1 , \ldots , x_n´ => ´y_n´, _ } +``` + +for ´n \geq 0´, where the final wildcard `‘_’` may be absent. It +makes available each importable member `´p´.´x_i´` under the unqualified name +´y_i´. I.e. every import selector `´x_i´ => ´y_i´` renames +`´p´.´x_i´` to +´y_i´. If a final wildcard is present, all importable members ´z´ of +´p´ other than `´x_1 , \ldots , x_n,y_1 , \ldots , y_n´` are also made available +under their own unqualified names. + +Import selectors work in the same way for type and term members. For +instance, an import clause `import ´p´.{´x´ => ´y\,´}` renames the term +name `´p´.´x´` to the term name ´y´ and the type name `´p´.´x´` +to the type name ´y´. At least one of these two names must +reference an importable member of ´p´. + +If the target in an import selector is a wildcard, the import selector +hides access to the source member. For instance, the import selector +`´x´ => _` “renames” ´x´ to the wildcard symbol (which is +unaccessible as a name in user programs), and thereby effectively +prevents unqualified access to ´x´. This is useful if there is a +final wildcard in the same import selector list, which imports all +members not mentioned in previous import selectors. + +The scope of a binding introduced by an import-clause starts +immediately after the import clause and extends to the end of the +enclosing block, template, package clause, or compilation unit, +whichever comes first. + +Several shorthands exist. An import selector may be just a simple name +´x´. In this case, ´x´ is imported without renaming, so the +import selector is equivalent to `´x´ => ´x´`. Furthermore, it is +possible to replace the whole import selector list by a single +identifier or wildcard. The import clause `import ´p´.´x´` is +equivalent to `import ´p´.{´x\,´}`, i.e. it makes available without +qualification the member ´x´ of ´p´. The import clause +`import ´p´._` is equivalent to +`import ´p´.{_}`, +i.e. it makes available without qualification all members of ´p´ +(this is analogous to `import ´p´.*` in Java). + +An import clause with multiple import expressions +`import ´p_1´.´I_1 , \ldots , p_n´.´I_n´` is interpreted as a +sequence of import clauses +`import ´p_1´.´I_1´; ´\ldots´; import ´p_n´.´I_n´`. + +###### Example +Consider the object definition: + +```scala +object M { + def z = 0, one = 1 + def add(x: Int, y: Int): Int = x + y +} +``` + +Then the block + +```scala +{ import M.{one, z => zero, _}; add(zero, one) } +``` + +is equivalent to the block + +```scala +{ M.add(M.z, M.one) } +``` diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md new file mode 100644 index 000000000000..5c3a74e608a2 --- /dev/null +++ b/docs/_spec/05-classes-and-objects.md @@ -0,0 +1,1160 @@ +--- +title: Classes & Objects +layout: default +chapter: 5 +--- + +# Classes and Objects + +```ebnf +TmplDef ::= [‘case’] ‘class’ ClassDef + | [‘case’] ‘object’ ObjectDef + | ‘trait’ TraitDef +``` + +[Classes](#class-definitions) and [objects](#object-definitions) +are both defined in terms of _templates_. + +## Templates + +```ebnf +ClassTemplate ::= [EarlyDefs] ClassParents [TemplateBody] +TraitTemplate ::= [EarlyDefs] TraitParents [TemplateBody] +ClassParents ::= Constr {‘with’ AnnotType} +TraitParents ::= AnnotType {‘with’ AnnotType} +TemplateBody ::= [nl] ‘{’ [SelfType] TemplateStat {semi TemplateStat} ‘}’ +SelfType ::= id [‘:’ Type] ‘=>’ + | this ‘:’ Type ‘=>’ +``` + +A _template_ defines the type signature, behavior and initial state of a +trait or class of objects or of a single object. Templates form part of +instance creation expressions, class definitions, and object +definitions. A template +`´sc´ with ´mt_1´ with ´\ldots´ with ´mt_n´ { ´\mathit{stats}´ }` +consists of a constructor invocation ´sc´ +which defines the template's _superclass_, trait references +`´mt_1 , \ldots , mt_n´` ´(n \geq 0)´, which define the +template's _traits_, and a statement sequence ´\mathit{stats}´ which +contains initialization code and additional member definitions for the +template. + +Each trait reference ´mt_i´ must denote a [trait](#traits). +By contrast, the superclass constructor ´sc´ normally refers to a +class which is not a trait. It is possible to write a list of +parents that starts with a trait reference, e.g. +`´mt_1´ with ´\ldots´ with ´mt_n´`. In that case the list +of parents is implicitly extended to include the supertype of ´mt_1´ +as the first parent type. The new supertype must have at least one +constructor that does not take parameters. In the following, we will +always assume that this implicit extension has been performed, so that +the first parent class of a template is a regular superclass +constructor, not a trait reference. + +The list of parents of a template must be well-formed. This means that +the class denoted by the superclass constructor ´sc´ must be a +subclass of the superclasses of all the traits ´mt_1 , \ldots , mt_n´. +In other words, the non-trait classes inherited by a template form a +chain in the inheritance hierarchy which starts with the template's +superclass. + +The _least proper supertype_ of a template is the class type or +[compound type](03-types.html#compound-types) consisting of all its parent +class types. + +The statement sequence ´\mathit{stats}´ contains member definitions that +define new members or overwrite members in the parent classes. If the +template forms part of an abstract class or trait definition, the +statement part ´\mathit{stats}´ may also contain declarations of abstract +members. If the template forms part of a concrete class definition, +´\mathit{stats}´ may still contain declarations of abstract type members, but +not of abstract term members. Furthermore, ´\mathit{stats}´ may in any case +also contain expressions; these are executed in the order they are +given as part of the initialization of a template. + +The sequence of template statements may be prefixed with a formal +parameter definition and an arrow, e.g. `´x´ =>`, or +`´x´:´T´ =>`. If a formal parameter is given, it can be +used as an alias for the reference `this` throughout the +body of the template. +If the formal parameter comes with a type ´T´, this definition affects +the _self type_ ´S´ of the underlying class or object as follows: Let ´C´ be the type +of the class or trait or object defining the template. +If a type ´T´ is given for the formal self parameter, ´S´ +is the greatest lower bound of ´T´ and ´C´. +If no type ´T´ is given, ´S´ is just ´C´. +Inside the template, the type of `this` is assumed to be ´S´. + +The self type of a class or object must conform to the self types of +all classes which are inherited by the template ´t´. + +A second form of self type annotation reads just +`this: ´S´ =>`. It prescribes the type ´S´ for `this` +without introducing an alias name for it. + +###### Example +Consider the following class definitions: + +```scala +class Base extends Object {} +trait Mixin extends Base {} +object O extends Mixin {} +``` + +In this case, the definition of `O` is expanded to: + +```scala +object O extends Base with Mixin {} +``` + + + +**Inheriting from Java Types** + +A template may have a Java class as its superclass and Java interfaces as its mixins. + +**Template Evaluation** + +Consider a template `´sc´ with ´mt_1´ with ´mt_n´ { ´\mathit{stats}´ }`. + +If this is the template of a [trait](#traits) then its _mixin-evaluation_ +consists of an evaluation of the statement sequence ´\mathit{stats}´. + +If this is not a template of a trait, then its _evaluation_ +consists of the following steps. + +- First, the superclass constructor ´sc´ is + [evaluated](#constructor-invocations). +- Then, all base classes in the template's [linearization](#class-linearization) + up to the template's superclass denoted by ´sc´ are + mixin-evaluated. Mixin-evaluation happens in reverse order of + occurrence in the linearization. +- Finally, the statement sequence ´\mathit{stats}\,´ is evaluated. + +###### Delayed Initialization +This statement sequence constitutes the initialization code for an object +or class after the superclass constructor invocation and the mixin-evaluation +of the template's base classes as described above. +Normally, this code is passed to a special hook, inaccessible to user code, +which simply executes it. + +However, in objects and classes (but not traits) which extend `scala.DelayedInit`, +the initialization code is passed to a `delayedInit` method which can be +overridden to implement arbitrary semantics. + +```scala +def delayedInit(body: => Unit): Unit +``` + +### Constructor Invocations + +```ebnf +Constr ::= AnnotType {‘(’ [Exprs] ‘)’} +``` + +Constructor invocations define the type, members, and initial state of +objects created by an instance creation expression, or of parts of an +object's definition which are inherited by a class or object +definition. A constructor invocation is a function application +`´x´.´c´[´\mathit{targs}´](´\mathit{args}_1´)´\ldots´(´\mathit{args}_n´)`, where ´x´ is a +[stable identifier](03-types.html#paths), ´c´ is a type name which either designates a +class or defines an alias type for one, ´\mathit{targs}´ is a type argument +list, ´\mathit{args}_1 , \ldots , \mathit{args}_n´ are argument lists, and there is a +constructor of that class which is [applicable](06-expressions.html#function-applications) +to the given arguments. If the constructor invocation uses named or +default arguments, it is transformed into a block expression using the +same transformation as described [here](sec:named-default). + +The prefix `´x´.` can be omitted. A type argument list +can be given only if the class ´c´ takes type parameters. Even then +it can be omitted, in which case a type argument list is synthesized +using [local type inference](06-expressions.html#local-type-inference). If no explicit +arguments are given, an empty list `()` is implicitly supplied. + +An evaluation of a constructor invocation +`´x´.´c´[´\mathit{targs}´](´\mathit{args}_1´)´\ldots´(´\mathit{args}_n´)` +consists of the following steps: + +- First, the prefix ´x´ is evaluated. +- Then, the arguments ´\mathit{args}_1 , \ldots , \mathit{args}_n´ are evaluated from + left to right. +- Finally, the class being constructed is initialized by evaluating the + template of the class referred to by ´c´. + +### Class Linearization + +The classes reachable through transitive closure of the direct +inheritance relation from a class ´C´ are called the _base classes_ of ´C´. Because of mixins, the inheritance relationship +on base classes forms in general a directed acyclic graph. A +linearization of this graph is defined as follows. + +###### Definition: linearization +Let ´C´ be a class with template +´C_1´ with ... with ´C_n´ { ´\mathit{stats}´ }`. +The _linearization_ of ´C´, ´\mathcal{L}(C)´ is defined as follows: +$$ +\mathcal{L}(C) = C, \mathcal{L}(C_n) \; \vec{+} \; \ldots \; \vec{+} \; \mathcal{L}(C_1) +$$ + +Here ´\vec{+}´ denotes concatenation where elements of the right operand +replace identical elements of the left operand: + +$$ +\begin{array}{lcll} +\{a, A\} \;\vec{+}\; B &=& a, (A \;\vec{+}\; B) &{\bf if} \; a \not\in B \\\\ + &=& A \;\vec{+}\; B &{\bf if} \; a \in B +\end{array} +$$ + +###### Example +Consider the following class definitions. + +```scala +abstract class AbsIterator extends AnyRef { ... } +trait RichIterator extends AbsIterator { ... } +class StringIterator extends AbsIterator { ... } +class Iter extends StringIterator with RichIterator { ... } +``` + +Then the linearization of class `Iter` is + +```scala +{ Iter, RichIterator, StringIterator, AbsIterator, AnyRef, Any } +``` + +Note that the linearization of a class refines the inheritance +relation: if ´C´ is a subclass of ´D´, then ´C´ precedes ´D´ in any +linearization where both ´C´ and ´D´ occur. +[Linearization](#definition:-linearization) also satisfies the property that +a linearization of a class always contains the linearization of its direct superclass as a suffix. + +For instance, the linearization of `StringIterator` is + +```scala +{ StringIterator, AbsIterator, AnyRef, Any } +``` + +which is a suffix of the linearization of its subclass `Iter`. +The same is not true for the linearization of mixins. +For instance, the linearization of `RichIterator` is + +```scala +{ RichIterator, AbsIterator, AnyRef, Any } +``` + +which is not a suffix of the linearization of `Iter`. + +### Class Members + +A class ´C´ defined by a template `´C_1´ with ´\ldots´ with ´C_n´ { ´\mathit{stats}´ }` +can define members in its statement sequence +´\mathit{stats}´ and can inherit members from all parent classes. Scala +adopts Java and C\#'s conventions for static overloading of +methods. It is thus possible that a class defines and/or inherits +several methods with the same name. To decide whether a defined +member of a class ´C´ overrides a member of a parent class, or whether +the two co-exist as overloaded variants in ´C´, Scala uses the +following definition of _matching_ on members: + +###### Definition: matching +A member definition ´M´ _matches_ a member definition ´M'´, if ´M´ +and ´M'´ bind the same name, and one of following holds. + +1. Neither ´M´ nor ´M'´ is a method definition. +2. ´M´ and ´M'´ define both monomorphic methods with equivalent argument types. +3. ´M´ defines a parameterless method and ´M'´ defines a method + with an empty parameter list `()` or _vice versa_. +4. ´M´ and ´M'´ define both polymorphic methods with + equal number of argument types ´\overline T´, ´\overline T'´ + and equal numbers of type parameters + ´\overline t´, ´\overline t'´, say, and ´\overline T' = [\overline t'/\overline t]\overline T´. + + + +Member definitions fall into two categories: concrete and abstract. +Members of class ´C´ are either _directly defined_ (i.e. they appear in +´C´'s statement sequence ´\mathit{stats}´) or they are _inherited_. There are two rules +that determine the set of members of a class, one for each category: + +A _concrete member_ of a class ´C´ is any concrete definition ´M´ in +some class ´C_i \in \mathcal{L}(C)´, except if there is a preceding class +´C_j \in \mathcal{L}(C)´ where ´j < i´ which directly defines a concrete +member ´M'´ matching ´M´. + +An _abstract member_ of a class ´C´ is any abstract definition ´M´ +in some class ´C_i \in \mathcal{L}(C)´, except if ´C´ contains already a +concrete member ´M'´ matching ´M´, or if there is a preceding class +´C_j \in \mathcal{L}(C)´ where ´j < i´ which directly defines an abstract +member ´M'´ matching ´M´. + +This definition also determines the [overriding](#overriding) relationships +between matching members of a class ´C´ and its parents. +First, a concrete definition always overrides an abstract definition. +Second, for definitions ´M´ and ´M´' which are both concrete or both abstract, +´M´ overrides ´M'´ if ´M´ appears in a class that precedes (in the +linearization of ´C´) the class in which ´M'´ is defined. + +It is an error if a template directly defines two matching members. It +is also an error if a template contains two members (directly defined +or inherited) with the same name and the same [erased type](03-types.html#type-erasure). +Finally, a template is not allowed to contain two methods (directly +defined or inherited) with the same name which both define default arguments. + +###### Example +Consider the trait definitions: + +```scala +trait A { def f: Int } +trait B extends A { def f: Int = 1 ; def g: Int = 2 ; def h: Int = 3 } +trait C extends A { override def f: Int = 4 ; def g: Int } +trait D extends B with C { def h: Int } +``` + +Then trait `D` has a directly defined abstract member `h`. It +inherits member `f` from trait `C` and member `g` from +trait `B`. + +### Overriding + + + +A member ´M´ of class ´C´ that [matches](#class-members) +a non-private member ´M'´ of a +base class of ´C´ is said to _override_ that member. In this case +the binding of the overriding member ´M´ must [subsume](03-types.html#conformance) +the binding of the overridden member ´M'´. +Furthermore, the following restrictions on modifiers apply to ´M´ and +´M'´: + +- ´M'´ must not be labeled `final`. +- ´M´ must not be [`private`](#modifiers). +- If ´M´ is labeled `private[´C´]` for some enclosing class or package ´C´, + then ´M'´ must be labeled `private[´C'´]` for some class or package ´C'´ where + ´C'´ equals ´C´ or ´C'´ is contained in ´C´. + + +- If ´M´ is labeled `protected`, then ´M'´ must also be + labeled `protected`. +- If ´M'´ is not an abstract member, then ´M´ must be labeled `override`. + Furthermore, one of two possibilities must hold: + - either ´M´ is defined in a subclass of the class where is ´M'´ is defined, + - or both ´M´ and ´M'´ override a third member ´M''´ which is defined + in a base class of both the classes containing ´M´ and ´M'´ +- If ´M'´ is [incomplete](#modifiers) in ´C´ then ´M´ must be + labeled `abstract override`. +- If ´M´ and ´M'´ are both concrete value definitions, then either none + of them is marked `lazy` or both must be marked `lazy`. + +- A stable member can only be overridden by a stable member. + For example, this is not allowed: + +```scala +class X { val stable = 1} +class Y extends X { override var stable = 1 } // error +``` + +Another restriction applies to abstract type members: An abstract type +member with a [volatile type](03-types.html#volatile-types) as its upper +bound may not override an abstract type member which does not have a +volatile upper bound. + +A special rule concerns parameterless methods. If a parameterless +method defined as `def ´f´: ´T´ = ...` or `def ´f´ = ...` overrides a method of +type ´()T'´ which has an empty parameter list, then ´f´ is also +assumed to have an empty parameter list. + +An overriding method inherits all default arguments from the definition +in the superclass. By specifying default arguments in the overriding method +it is possible to add new defaults (if the corresponding parameter in the +superclass does not have a default) or to override the defaults of the +superclass (otherwise). + +###### Example + +Consider the definitions: + +```scala +trait Root { type T <: Root } +trait A extends Root { type T <: A } +trait B extends Root { type T <: B } +trait C extends A with B +``` + +Then the class definition `C` is not well-formed because the +binding of `T` in `C` is +`type T <: B`, +which fails to subsume the binding `type T <: A` of `T` +in type `A`. The problem can be solved by adding an overriding +definition of type `T` in class `C`: + +```scala +class C extends A with B { type T <: C } +``` + +### Inheritance Closure + +Let ´C´ be a class type. The _inheritance closure_ of ´C´ is the +smallest set ´\mathscr{S}´ of types such that + +- ´C´ is in ´\mathscr{S}´. +- If ´T´ is in ´\mathscr{S}´, then every type ´T'´ which forms syntactically + a part of ´T´ is also in ´\mathscr{S}´. +- If ´T´ is a class type in ´\mathscr{S}´, then all [parents](#templates) + of ´T´ are also in ´\mathscr{S}´. + +It is a static error if the inheritance closure of a class type +consists of an infinite number of types. (This restriction is +necessary to make subtyping decidable[^kennedy]). + +[^kennedy]: Kennedy, Pierce. [On Decidability of Nominal Subtyping with Variance.]( https://research.microsoft.com/pubs/64041/fool2007.pdf) in FOOL 2007 + +### Early Definitions + +```ebnf +EarlyDefs ::= ‘{’ [EarlyDef {semi EarlyDef}] ‘}’ ‘with’ +EarlyDef ::= {Annotation} {Modifier} PatVarDef +``` + +A template may start with an _early field definition_ clause, +which serves to define certain field values before the supertype +constructor is called. In a template + +```scala +{ val ´p_1´: ´T_1´ = ´e_1´ + ... + val ´p_n´: ´T_n´ = ´e_n´ +} with ´sc´ with ´mt_1´ with ´mt_n´ { ´\mathit{stats}´ } +``` + +The initial pattern definitions of ´p_1 , \ldots , p_n´ are called +_early definitions_. They define fields +which form part of the template. Every early definition must define +at least one variable. + +An early definition is type-checked and evaluated in the scope which +is in effect just before the template being defined, augmented by any +type parameters of the enclosing class and by any early definitions +preceding the one being defined. In particular, any reference to +`this` in an early definition refers +to the identity of `this` just outside the template. Consequently, it +is impossible for an early definition to refer to the object being +constructed by the template, or to refer to one of its fields and +methods, except for any other preceding early definition in the same +section. Furthermore, references to preceding early definitions +always refer to the value that's defined there and do not take into account +overriding definitions. In other words, a block of early definitions +is evaluated exactly as if it were a local block containing a number of value +definitions. + +Early definitions are evaluated +before the superclass constructor of the template is called, +in the order they are defined. + +###### Example +Early definitions are particularly useful for +traits, which do not have normal constructor parameters. Example: + +```scala +trait Greeting { + val name: String + val msg = "How are you, "+name +} +class C extends { + val name = "Bob" +} with Greeting { + println(msg) +} +``` + +In the code above, the field `name` is initialized before the +constructor of `Greeting` is called. Therefore, field `msg` in +class `Greeting` is properly initialized to `"How are you, Bob"`. + +If `name` had been initialized instead in `C`'s normal class +body, it would be initialized after the constructor of +`Greeting`. In that case, `msg` would be initialized to +`"How are you, "`. + +## Modifiers + +```ebnf +Modifier ::= LocalModifier + | AccessModifier + | ‘override’ +LocalModifier ::= ‘abstract’ + | ‘final’ + | ‘sealed’ + | ‘implicit’ + | ‘lazy’ +AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] +AccessQualifier ::= ‘[’ (id | ‘this’) ‘]’ +``` + +Member definitions may be preceded by modifiers which affect the +accessibility and usage of the identifiers bound by them. If several +modifiers are given, their order does not matter, but the same +modifier may not occur more than once. Modifiers preceding a repeated +definition apply to all constituent definitions. The rules governing +the validity and meaning of a modifier are as follows. + +### `private` +The `private` modifier can be used with any definition or declaration in a +template. Private members of a template can be accessed only from within the +directly enclosing template and its companion module or +[companion class](#object-definitions). + +The `private` modifier is also valid for +[top-level](09-top-level-definitions.html#packagings) templates. + +A `private` modifier can be _qualified_ with an identifier ´C´ (e.g. +`private[´C´]`) that must denote a class or package enclosing the definition. +Members labeled with such a modifier are accessible respectively only from code +inside the package ´C´ or only from code inside the class ´C´ and its +[companion module](#object-definitions). + +A different form of qualification is `private[this]`. A member +´M´ marked with this modifier is called _object-protected_; it can be accessed only from within +the object in which it is defined. That is, a selection ´p.M´ is only +legal if the prefix is `this` or `´O´.this`, for some +class ´O´ enclosing the reference. In addition, the restrictions for +unqualified `private` apply. + +Members marked private without a qualifier are called _class-private_, +whereas members labeled with `private[this]` +are called _object-private_. A member _is private_ if it is +either class-private or object-private, but not if it is marked +`private[´C´]` where ´C´ is an identifier; in the latter +case the member is called _qualified private_. + +Class-private or object-private members may not be abstract, and may +not have `protected` or `override` modifiers. They are not inherited +by subclasses and they may not override definitions in parent classes. + +### `protected` +The `protected` modifier applies to class member definitions. +Protected members of a class can be accessed from within + - the template of the defining class, + - all templates that have the defining class as a base class, + - the companion module of any of those classes. + +A `protected` modifier can be qualified with an identifier ´C´ (e.g. +`protected[´C´]`) that must denote a class or package enclosing the definition. +Members labeled with such a modifier are also accessible respectively from all +code inside the package ´C´ or from all code inside the class ´C´ and its +[companion module](#object-definitions). + +A protected identifier ´x´ may be used as a member name in a selection +`´r´.´x´` only if one of the following applies: + - The access is within the template defining the member, or, if + a qualification ´C´ is given, inside the package ´C´, + or the class ´C´, or its companion module, or + - ´r´ is one of the reserved words `this` and + `super`, or + - ´r´'s type conforms to a type-instance of the + class which contains the access. + +A different form of qualification is `protected[this]`. A member +´M´ marked with this modifier is called _object-protected_; it can be accessed only from within +the object in which it is defined. That is, a selection ´p.M´ is only +legal if the prefix is `this` or `´O´.this`, for some +class ´O´ enclosing the reference. In addition, the restrictions for +unqualified `protected` apply. + +### `override` +The `override` modifier applies to class member definitions or declarations. +It is mandatory for member definitions or declarations that override some +other concrete member definition in a parent class. If an `override` +modifier is given, there must be at least one overridden member +definition or declaration (either concrete or abstract). + +### `abstract override` +The `override` modifier has an additional significance when +combined with the `abstract` modifier. That modifier combination +is only allowed for value members of traits. + +We call a member ´M´ of a template _incomplete_ if it is either +abstract (i.e. defined by a declaration), or it is labeled +`abstract` and `override` and +every member overridden by ´M´ is again incomplete. + +Note that the `abstract override` modifier combination does not +influence the concept whether a member is concrete or abstract. A +member is _abstract_ if only a declaration is given for it; +it is _concrete_ if a full definition is given. + +### `abstract` +The `abstract` modifier is used in class definitions. It is +redundant for traits, and mandatory for all other classes which have +incomplete members. Abstract classes cannot be +[instantiated](06-expressions.html#instance-creation-expressions) with a constructor invocation +unless followed by mixins and/or a refinement which override all +incomplete members of the class. Only abstract classes and traits can have +abstract term members. + +The `abstract` modifier can also be used in conjunction with +`override` for class member definitions. In that case the +previous discussion applies. + +### `final` +The `final` modifier applies to class member definitions and to +class definitions. A `final` class member definition may not be +overridden in subclasses. A `final` class may not be inherited by +a template. `final` is redundant for object definitions. Members +of final classes or objects are implicitly also final, so the +`final` modifier is generally redundant for them, too. Note, however, that +[constant value definitions](04-basic-declarations-and-definitions.html#value-declarations-and-definitions) +do require an explicit `final` modifier, +even if they are defined in a final class or object. +`final` is permitted for abstract classes +but it may not be applied to traits or incomplete members, +and it may not be combined in one modifier list with `sealed`. + +### `sealed` +The `sealed` modifier applies to class definitions. A +`sealed` class may not be directly inherited, except if the inheriting +template is defined in the same source file as the inherited class. +However, subclasses of a sealed class can be inherited anywhere. + +### `lazy` +The `lazy` modifier applies to value definitions. A `lazy` +value is initialized the first time it is accessed (which might never +happen at all). Attempting to access a lazy value during its +initialization might lead to looping behavior. If an exception is +thrown during initialization, the value is considered uninitialized, +and a later access will retry to evaluate its right hand side. + +###### Example +The following code illustrates the use of qualified private: + +```scala +package outerpkg.innerpkg +class Outer { + class Inner { + private[Outer] def f() + private[innerpkg] def g() + private[outerpkg] def h() + } +} +``` + +Here, accesses to the method `f` can appear anywhere within +`Outer`, but not outside it. Accesses to method +`g` can appear anywhere within the package +`outerpkg.innerpkg`, as would be the case for +package-private methods in Java. Finally, accesses to method +`h` can appear anywhere within package `outerpkg`, +including packages contained in it. + +###### Example +A useful idiom to prevent clients of a class from +constructing new instances of that class is to declare the class +`abstract` and `sealed`: + +```scala +object m { + abstract sealed class C (x: Int) { + def nextC = new C(x + 1) {} + } + val empty = new C(0) {} +} +``` + +For instance, in the code above clients can create instances of class +`m.C` only by calling the `nextC` method of an existing `m.C` +object; it is not possible for clients to create objects of class +`m.C` directly. Indeed the following two lines are both in error: + +```scala +new m.C(0) // **** error: C is abstract, so it cannot be instantiated. +new m.C(0) {} // **** error: illegal inheritance from sealed class. +``` + +A similar access restriction can be achieved by marking the primary +constructor `private` ([example](#example-private-constructor)). + +## Class Definitions + +```ebnf +TmplDef ::= ‘class’ ClassDef +ClassDef ::= id [TypeParamClause] {Annotation} + [AccessModifier] ClassParamClauses ClassTemplateOpt +ClassParamClauses ::= {ClassParamClause} + [[nl] ‘(’ implicit ClassParams ‘)’] +ClassParamClause ::= [nl] ‘(’ [ClassParams] ‘)’ +ClassParams ::= ClassParam {‘,’ ClassParam} +ClassParam ::= {Annotation} {Modifier} [(‘val’ | ‘var’)] + id [‘:’ ParamType] [‘=’ Expr] +ClassTemplateOpt ::= ‘extends’ ClassTemplate | [[‘extends’] TemplateBody] +``` + +The most general form of class definition is + +```scala +class ´c´[´\mathit{tps}\,´] ´as´ ´m´(´\mathit{ps}_1´)´\ldots´(´\mathit{ps}_n´) extends ´t´ ´\quad(n \geq 0)´. +``` + +Here, + + - ´c´ is the name of the class to be defined. + - ´\mathit{tps}´ is a non-empty list of type parameters of the class + being defined. The scope of a type parameter is the whole class + definition including the type parameter section itself. It is + illegal to define two type parameters with the same name. The type + parameter section `[´\mathit{tps}\,´]` may be omitted. A class with a type + parameter section is called _polymorphic_, otherwise it is called + _monomorphic_. + - ´as´ is a possibly empty sequence of + [annotations](11-annotations.html#user-defined-annotations). + If any annotations are given, they apply to the primary constructor of the + class. + - ´m´ is an [access modifier](#modifiers) such as + `private` or `protected`, possibly with a qualification. + If such an access modifier is given it applies to the primary constructor of the class. + - ´(\mathit{ps}\_1)\ldots(\mathit{ps}\_n)´ are formal value parameter clauses for + the _primary constructor_ of the class. The scope of a formal value parameter includes + all subsequent parameter sections and the template ´t´. However, a formal + value parameter may not form part of the types of any of the parent classes or members of the class template ´t´. + It is illegal to define two formal value parameters with the same name. + + If a class has no formal parameter section that is not implicit, an empty parameter section `()` is assumed. + + If a formal parameter declaration ´x: T´ is preceded by a `val` + or `var` keyword, an accessor (getter) [definition](04-basic-declarations-and-definitions.html#variable-declarations-and-definitions) + for this parameter is implicitly added to the class. + + The getter introduces a value member ´x´ of class ´c´ that is defined as an alias of the parameter. + If the introducing keyword is `var`, a setter accessor [`´x´_=`](04-basic-declarations-and-definitions.html#variable-declarations-and-definitions) is also implicitly added to the class. + In invocation of that setter `´x´_=(´e´)` changes the value of the parameter to the result of evaluating ´e´. + + The formal parameter declaration may contain modifiers, which then carry over to the accessor definition(s). + When access modifiers are given for a parameter, but no `val` or `var` keyword, `val` is assumed. + A formal parameter prefixed by `val` or `var` may not at the same time be a [call-by-name parameter](04-basic-declarations-and-definitions.html#by-name-parameters). + + - ´t´ is a [template](#templates) of the form + + ```scala + ´sc´ with ´mt_1´ with ´\ldots´ with ´mt_m´ { ´\mathit{stats}´ } // ´m \geq 0´ + ``` + + which defines the base classes, behavior and initial state of objects of + the class. The extends clause + `extends ´sc´ with ´mt_1´ with ´\ldots´ with ´mt_m´` + can be omitted, in which case + `extends scala.AnyRef` is assumed. The class body + `{ ´\mathit{stats}´ }` may also be omitted, in which case the empty body + `{}` is assumed. + +This class definition defines a type `´c´[´\mathit{tps}\,´]` and a constructor +which when applied to parameters conforming to types ´\mathit{ps}´ +initializes instances of type `´c´[´\mathit{tps}\,´]` by evaluating the template +´t´. + +###### Example – `val` and `var` parameters +The following example illustrates `val` and `var` parameters of a class `C`: + +```scala +class C(x: Int, val y: String, var z: List[String]) +val c = new C(1, "abc", List()) +c.z = c.y :: c.z +``` + +###### Example – Private Constructor +The following class can be created only from its companion module. + +```scala +object Sensitive { + def makeSensitive(credentials: Certificate): Sensitive = + if (credentials == Admin) new Sensitive() + else throw new SecurityViolationException +} +class Sensitive private () { + ... +} +``` + +### Constructor Definitions + +```ebnf +FunDef ::= ‘this’ ParamClause ParamClauses + (‘=’ ConstrExpr | [nl] ConstrBlock) +ConstrExpr ::= SelfInvocation + | ConstrBlock +ConstrBlock ::= ‘{’ SelfInvocation {semi BlockStat} ‘}’ +SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} +``` + +A class may have additional constructors besides the primary +constructor. These are defined by constructor definitions of the form +`def this(´\mathit{ps}_1´)´\ldots´(´\mathit{ps}_n´) = ´e´`. Such a +definition introduces an additional constructor for the enclosing +class, with parameters as given in the formal parameter lists ´\mathit{ps}_1 +, \ldots , \mathit{ps}_n´, and whose evaluation is defined by the constructor +expression ´e´. The scope of each formal parameter is the subsequent +parameter sections and the constructor +expression ´e´. A constructor expression is either a self constructor +invocation `this(´\mathit{args}_1´)´\ldots´(´\mathit{args}_n´)` or a block +which begins with a self constructor invocation. The self constructor +invocation must construct a generic instance of the class. I.e. if the +class in question has name ´C´ and type parameters +`[´\mathit{tps}\,´]`, then a self constructor invocation must +generate an instance of `´C´[´\mathit{tps}\,´]`; it is not permitted +to instantiate formal type parameters. + +The signature and the self constructor invocation of a constructor +definition are type-checked and evaluated in the scope which is in +effect at the point of the enclosing class definition, augmented by +any type parameters of the enclosing class and by any +[early definitions](#early-definitions) of the enclosing template. +The rest of the +constructor expression is type-checked and evaluated as a function +body in the current class. + +If there are auxiliary constructors of a class ´C´, they form together +with ´C´'s primary [constructor](#class-definitions) +an overloaded constructor +definition. The usual rules for +[overloading resolution](06-expressions.html#overloading-resolution) +apply for constructor invocations of ´C´, +including for the self constructor invocations in the constructor +expressions themselves. However, unlike other methods, constructors +are never inherited. To prevent infinite cycles of constructor +invocations, there is the restriction that every self constructor +invocation must refer to a constructor definition which precedes it +(i.e. it must refer to either a preceding auxiliary constructor or the +primary constructor of the class). + +###### Example +Consider the class definition + +```scala +class LinkedList[A]() { + var head: A = _ + var tail: LinkedList[A] = null + def this(head: A) = { this(); this.head = head } + def this(head: A, tail: LinkedList[A]) = { this(head); this.tail = tail } +} +``` + +This defines a class `LinkedList` with three constructors. The +second constructor constructs a singleton list, while the +third one constructs a list with a given head and tail. + +### Case Classes + +```ebnf +TmplDef ::= ‘case’ ‘class’ ClassDef +``` + +If a class definition is prefixed with `case`, the class is said +to be a _case class_. + +A case class is required to have a parameter section that is not implicit. +The formal parameters in the first parameter section +are called _elements_ and are treated specially. +First, the value of such a parameter can be extracted as a +field of a constructor pattern. Second, a `val` prefix is +implicitly added to such a parameter, unless the parameter already carries +a `val` or `var` modifier. Hence, an accessor +definition for the parameter is [generated](#class-definitions). + +A case class definition of `´c´[´\mathit{tps}\,´](´\mathit{ps}_1\,´)´\ldots´(´\mathit{ps}_n´)` with type +parameters ´\mathit{tps}´ and value parameters ´\mathit{ps}´ implies +the definition of a companion object, which serves as an [extractor object](08-pattern-matching.html#extractor-patterns). It has the following shape: + +```scala +object ´c´ { + def apply[´\mathit{tps}\,´](´\mathit{ps}_1\,´)´\ldots´(´\mathit{ps}_n´): ´c´[´\mathit{tps}\,´] = new ´c´[´\mathit{Ts}\,´](´\mathit{xs}_1\,´)´\ldots´(´\mathit{xs}_n´) + def unapply[´\mathit{tps}\,´](´x´: ´c´[´\mathit{tps}\,´]) = + if (x eq null) scala.None + else scala.Some(´x.\mathit{xs}_{11}, \ldots , x.\mathit{xs}_{1k}´) +} +``` + +Here, ´\mathit{Ts}´ stands for the vector of types defined in the type +parameter section ´\mathit{tps}´, +each ´\mathit{xs}\_i´ denotes the parameter names of the parameter +section ´\mathit{ps}\_i´, and +´\mathit{xs}\_{11}, \ldots , \mathit{xs}\_{1k}´ denote the names of all parameters +in the first parameter section ´\mathit{xs}\_1´. +If a type parameter section is missing in the class, it is also missing in the `apply` and `unapply` methods. + +If the companion object ´c´ is already defined, +the `apply` and `unapply` methods are added to the existing object. +If the object ´c´ already has a [matching](#definition-matching) +`apply` (or `unapply`) member, no new definition is added. +The definition of `apply` is omitted if class ´c´ is `abstract`. + +If the case class definition contains an empty value parameter list, the +`unapply` method returns a `Boolean` instead of an `Option` type and +is defined as follows: + +```scala +def unapply[´\mathit{tps}\,´](´x´: ´c´[´\mathit{tps}\,´]) = x ne null +``` + +The name of the `unapply` method is changed to `unapplySeq` if the first +parameter section ´\mathit{ps}_1´ of ´c´ ends in a +[repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters). + +A method named `copy` is implicitly added to every case class unless the +class already has a member (directly defined or inherited) with that name, or the +class has a repeated parameter. The method is defined as follows: + +```scala +def copy[´\mathit{tps}\,´](´\mathit{ps}'_1\,´)´\ldots´(´\mathit{ps}'_n´): ´c´[´\mathit{tps}\,´] = new ´c´[´\mathit{Ts}\,´](´\mathit{xs}_1\,´)´\ldots´(´\mathit{xs}_n´) +``` + +Again, `´\mathit{Ts}´` stands for the vector of types defined in the type parameter section `´\mathit{tps}´` +and each `´xs_i´` denotes the parameter names of the parameter section `´ps'_i´`. The value +parameters `´ps'_{1,j}´` of first parameter list have the form `´x_{1,j}´:´T_{1,j}´=this.´x_{1,j}´`, +the other parameters `´ps'_{i,j}´` of the `copy` method are defined as `´x_{i,j}´:´T_{i,j}´`. +In all cases `´x_{i,j}´` and `´T_{i,j}´` refer to the name and type of the corresponding class parameter +`´\mathit{ps}_{i,j}´`. + +Every case class implicitly overrides some method definitions of class +[`scala.AnyRef`](12-the-scala-standard-library.html#root-classes) unless a definition of the same +method is already given in the case class itself or a concrete +definition of the same method is given in some base class of the case +class different from `AnyRef`. In particular: + +- Method `equals: (Any)Boolean` is structural equality, where two + instances are equal if they both belong to the case class in question and they + have equal (with respect to `equals`) constructor arguments (restricted to the class's _elements_, i.e., the first parameter section). +- Method `hashCode: Int` computes a hash-code. If the hashCode methods + of the data structure members map equal (with respect to equals) + values to equal hash-codes, then the case class hashCode method does + too. +- Method `toString: String` returns a string representation which + contains the name of the class and its elements. + +###### Example +Here is the definition of abstract syntax for lambda calculus: + +```scala +class Expr +case class Var (x: String) extends Expr +case class Apply (f: Expr, e: Expr) extends Expr +case class Lambda(x: String, e: Expr) extends Expr +``` + +This defines a class `Expr` with case classes +`Var`, `Apply` and `Lambda`. A call-by-value evaluator +for lambda expressions could then be written as follows. + +```scala +type Env = String => Value +case class Value(e: Expr, env: Env) + +def eval(e: Expr, env: Env): Value = e match { + case Var (x) => + env(x) + case Apply(f, g) => + val Value(Lambda (x, e1), env1) = eval(f, env) + val v = eval(g, env) + eval (e1, (y => if (y == x) v else env1(y))) + case Lambda(_, _) => + Value(e, env) +} +``` + +It is possible to define further case classes that extend type +`Expr` in other parts of the program, for instance + +```scala +case class Number(x: Int) extends Expr +``` + +This form of extensibility can be excluded by declaring the base class +`Expr` `sealed`; in this case, all classes that +directly extend `Expr` must be in the same source file as +`Expr`. + +## Traits + +```ebnf +TmplDef ::= ‘trait’ TraitDef +TraitDef ::= id [TypeParamClause] TraitTemplateOpt +TraitTemplateOpt ::= ‘extends’ TraitTemplate | [[‘extends’] TemplateBody] +``` + +A _trait_ is a class that is meant to be added to some other class +as a mixin. Unlike normal classes, traits cannot have +constructor parameters. Furthermore, no constructor arguments are +passed to the superclass of the trait. This is not necessary as traits are +initialized after the superclass is initialized. + +Assume a trait ´D´ defines some aspect of an instance ´x´ of type ´C´ (i.e. ´D´ is a base class of ´C´). +Then the _actual supertype_ of ´D´ in ´x´ is the compound type consisting of all the +base classes in ´\mathcal{L}(C)´ that succeed ´D´. The actual supertype gives +the context for resolving a [`super` reference](06-expressions.html#this-and-super) in a trait. +Note that the actual supertype depends on the type to which the trait is added in a mixin composition; +it is not statically known at the time the trait is defined. + +If ´D´ is not a trait, then its actual supertype is simply its +least proper supertype (which is statically known). + +###### Example +The following trait defines the property +of being comparable to objects of some type. It contains an abstract +method `<` and default implementations of the other +comparison operators `<=`, `>`, and +`>=`. + +```scala +trait Comparable[T <: Comparable[T]] { self: T => + def < (that: T): Boolean + def <=(that: T): Boolean = this < that || this == that + def > (that: T): Boolean = that < this + def >=(that: T): Boolean = that <= this +} +``` + +###### Example +Consider an abstract class `Table` that implements maps +from a type of keys `A` to a type of values `B`. The class +has a method `set` to enter a new key / value pair into the table, +and a method `get` that returns an optional value matching a +given key. Finally, there is a method `apply` which is like +`get`, except that it returns a given default value if the table +is undefined for the given key. This class is implemented as follows. + +```scala +abstract class Table[A, B](defaultValue: B) { + def get(key: A): Option[B] + def set(key: A, value: B): Unit + def apply(key: A) = get(key) match { + case Some(value) => value + case None => defaultValue + } +} +``` + +Here is a concrete implementation of the `Table` class. + +```scala +class ListTable[A, B](defaultValue: B) extends Table[A, B](defaultValue) { + private var elems: List[(A, B)] = Nil + def get(key: A) = elems.find(_._1 == key).map(_._2) + def set(key: A, value: B) = { elems = (key, value) :: elems } +} +``` + +Here is a trait that prevents concurrent access to the +`get` and `set` operations of its parent class: + +```scala +trait SynchronizedTable[A, B] extends Table[A, B] { + abstract override def get(key: A): B = + synchronized { super.get(key) } + abstract override def set(key: A, value: B) = + synchronized { super.set(key, value) } +} +``` + +Note that `SynchronizedTable` does not pass an argument to +its superclass, `Table`, even though `Table` is defined with a +formal parameter. Note also that the `super` calls +in `SynchronizedTable`'s `get` and `set` methods +statically refer to abstract methods in class `Table`. This is +legal, as long as the calling method is labeled +[`abstract override`](#modifiers). + +Finally, the following mixin composition creates a synchronized list +table with strings as keys and integers as values and with a default +value `0`: + +```scala +object MyTable extends ListTable[String, Int](0) with SynchronizedTable[String, Int] +``` + +The object `MyTable` inherits its `get` and `set` +method from `SynchronizedTable`. The `super` calls in these +methods are re-bound to refer to the corresponding implementations in +`ListTable`, which is the actual supertype of `SynchronizedTable` +in `MyTable`. + +## Object Definitions + +```ebnf +ObjectDef ::= id ClassTemplate +``` + +An _object definition_ defines a single object of a new class. Its +most general form is +`object ´m´ extends ´t´`. Here, +´m´ is the name of the object to be defined, and +´t´ is a [template](#templates) of the form + +```scala +´sc´ with ´mt_1´ with ´\ldots´ with ´mt_n´ { ´\mathit{stats}´ } +``` + +which defines the base classes, behavior and initial state of ´m´. +The extends clause `extends ´sc´ with ´mt_1´ with ´\ldots´ with ´mt_n´` +can be omitted, in which case +`extends scala.AnyRef` is assumed. The class body +`{ ´\mathit{stats}´ }` may also be omitted, in which case the empty body +`{}` is assumed. + +The object definition defines a single object (or: _module_) +conforming to the template ´t´. It is roughly equivalent to the +following definition of a lazy value: + +```scala +lazy val ´m´ = new ´sc´ with ´mt_1´ with ´\ldots´ with ´mt_n´ { this: ´m.type´ => ´\mathit{stats}´ } +``` + +Note that the value defined by an object definition is instantiated +lazily. The `new ´m´$cls` constructor is evaluated +not at the point of the object definition, but is instead evaluated +the first time ´m´ is dereferenced during execution of the program +(which might be never at all). An attempt to dereference ´m´ again +during evaluation of the constructor will lead to an infinite loop +or run-time error. +Other threads trying to dereference ´m´ while the +constructor is being evaluated block until evaluation is complete. + +The expansion given above is not accurate for top-level objects. It +cannot be because variable and method definition cannot appear on the +top-level outside of a [package object](09-top-level-definitions.html#package-objects). Instead, +top-level objects are translated to static fields. + +###### Example +Classes in Scala do not have static members; however, an equivalent +effect can be achieved by an accompanying object definition +E.g. + +```scala +abstract class Point { + val x: Double + val y: Double + def isOrigin = (x == 0.0 && y == 0.0) +} +object Point { + val origin = new Point() { val x = 0.0; val y = 0.0 } +} +``` + +This defines a class `Point` and an object `Point` which +contains `origin` as a member. Note that the double use of the +name `Point` is legal, since the class definition defines the +name `Point` in the type name space, whereas the object +definition defines a name in the term namespace. + +This technique is applied by the Scala compiler when interpreting a +Java class with static members. Such a class ´C´ is conceptually seen +as a pair of a Scala class that contains all instance members of ´C´ +and a Scala object that contains all static members of ´C´. + +Generally, a _companion module_ of a class is an object which has +the same name as the class and is defined in the same scope and +compilation unit. Conversely, the class is called the _companion class_ +of the module. + +Very much like a concrete class definition, an object definition may +still contain declarations of abstract type members, but not of +abstract term members. diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md new file mode 100644 index 000000000000..f574d7ad2469 --- /dev/null +++ b/docs/_spec/06-expressions.md @@ -0,0 +1,1828 @@ +--- +title: Expressions +layout: default +chapter: 6 +--- + +# Expressions + +```ebnf +Expr ::= (Bindings | id | ‘_’) ‘=>’ Expr + | Expr1 +Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] + | ‘while’ ‘(’ Expr ‘)’ {nl} Expr + | ‘try’ Expr [‘catch’ Expr] [‘finally’ Expr] + | ‘do’ Expr [semi] ‘while’ ‘(’ Expr ‘)’ + | ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr + | ‘throw’ Expr + | ‘return’ [Expr] + | [SimpleExpr ‘.’] id ‘=’ Expr + | SimpleExpr1 ArgumentExprs ‘=’ Expr + | PostfixExpr + | PostfixExpr Ascription + | PostfixExpr ‘match’ ‘{’ CaseClauses ‘}’ +PostfixExpr ::= InfixExpr [id [nl]] +InfixExpr ::= PrefixExpr + | InfixExpr id [nl] InfixExpr +PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr +SimpleExpr ::= ‘new’ (ClassTemplate | TemplateBody) + | BlockExpr + | SimpleExpr1 [‘_’] +SimpleExpr1 ::= Literal + | Path + | ‘_’ + | ‘(’ [Exprs] ‘)’ + | SimpleExpr ‘.’ id + | SimpleExpr TypeArgs + | SimpleExpr1 ArgumentExprs + | XmlExpr +Exprs ::= Expr {‘,’ Expr} +BlockExpr ::= ‘{’ CaseClauses ‘}’ + | ‘{’ Block ‘}’ +Block ::= BlockStat {semi BlockStat} [ResultExpr] +ResultExpr ::= Expr1 + | (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block +Ascription ::= ‘:’ InfixType + | ‘:’ Annotation {Annotation} + | ‘:’ ‘_’ ‘*’ +``` + +Expressions are composed of operators and operands. Expression forms are +discussed subsequently in decreasing order of precedence. + +## Expression Typing + +The typing of expressions is often relative to some _expected type_ (which might be undefined). When we write "expression ´e´ is expected to conform to type ´T´", we mean: + 1. the expected type of ´e´ is ´T´, and + 2. the type of expression ´e´ must conform to ´T´. + +The following skolemization rule is applied universally for every +expression: If the type of an expression would be an existential type +´T´, then the type of the expression is assumed instead to be a +[skolemization](03-types.html#existential-types) of ´T´. + +Skolemization is reversed by type packing. Assume an expression ´e´ of +type ´T´ and let ´t_1[\mathit{tps}\_1] >: L_1 <: U_1 , \ldots , t_n[\mathit{tps}\_n] >: L_n <: U_n´ be +all the type variables created by skolemization of some part of ´e´ which are free in ´T´. +Then the _packed type_ of ´e´ is + +```scala +´T´ forSome { type ´t_1[\mathit{tps}\_1] >: L_1 <: U_1´; ´\ldots´; type ´t_n[\mathit{tps}\_n] >: L_n <: U_n´ }. +``` + +## Literals + +```ebnf +SimpleExpr ::= Literal +``` + +Typing of literals is described along with their [lexical syntax](01-lexical-syntax.html#literals); +their evaluation is immediate. + +## The _Null_ Value + +The `null` value is of type `scala.Null`, and thus conforms to every reference type. +It denotes a reference value which refers to a special `null` object. +This object implements methods in class `scala.AnyRef` as follows: + +- `eq(´x\,´)` and `==(´x\,´)` return `true` iff the + argument ´x´ is also the "null" object. +- `ne(´x\,´)` and `!=(´x\,´)` return true iff the + argument x is not also the "null" object. +- `isInstanceOf[´T\,´]` always returns `false`. +- `asInstanceOf[´T\,´]` returns the [default value](04-basic-declarations-and-definitions.html#value-declarations-and-definitions) of type ´T´. +- `##` returns ``0``. + +A reference to any other member of the "null" object causes a +`NullPointerException` to be thrown. + +## Designators + +```ebnf +SimpleExpr ::= Path + | SimpleExpr ‘.’ id +``` + +A designator refers to a named term. It can be a _simple name_ or +a _selection_. + +A simple name ´x´ refers to a value as specified +[here](02-identifiers-names-and-scopes.html#identifiers,-names-and-scopes). +If ´x´ is bound by a definition or declaration in an enclosing class +or object ´C´, it is taken to be equivalent to the selection +`´C´.this.´x´` where ´C´ is taken to refer to the class containing ´x´ +even if the type name ´C´ is [shadowed](02-identifiers-names-and-scopes.html#identifiers,-names-and-scopes) at the +occurrence of ´x´. + +If ´r´ is a [stable identifier](03-types.html#paths) of type ´T´, the selection ´r.x´ refers +statically to a term member ´m´ of ´r´ that is identified in ´T´ by +the name ´x´. + + + +For other expressions ´e´, ´e.x´ is typed as +if it was `{ val ´y´ = ´e´; ´y´.´x´ }`, for some fresh name +´y´. + +The expected type of a designator's prefix is always undefined. The +type of a designator is the type ´T´ of the entity it refers to, with +the following exception: The type of a [path](03-types.html#paths) ´p´ +which occurs in a context where a [stable type](03-types.html#singleton-types) +is required is the singleton type `´p´.type`. + +The contexts where a stable type is required are those that satisfy +one of the following conditions: + +1. The path ´p´ occurs as the prefix of a selection and it does not +designate a constant, or +1. The expected type ´\mathit{pt}´ is a stable type, or +1. The expected type ´\mathit{pt}´ is an abstract type with a stable type as lower + bound, and the type ´T´ of the entity referred to by ´p´ does not + conform to ´\mathit{pt}´, or +1. The path ´p´ designates a module. + +The selection ´e.x´ is evaluated by first evaluating the qualifier +expression ´e´, which yields an object ´r´, say. The selection's +result is then the member of ´r´ that is either defined by ´m´ or defined +by a definition overriding ´m´. + +## This and Super + +```ebnf +SimpleExpr ::= [id ‘.’] ‘this’ + | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id +``` + +The expression `this` can appear in the statement part of a +template or compound type. It stands for the object being defined by +the innermost template or compound type enclosing the reference. If +this is a compound type, the type of `this` is that compound type. +If it is a template of a +class or object definition with simple name ´C´, the type of this +is the same as the type of `´C´.this`. + +The expression `´C´.this` is legal in the statement part of an +enclosing class or object definition with simple name ´C´. It +stands for the object being defined by the innermost such definition. +If the expression's expected type is a stable type, or +`´C´.this` occurs as the prefix of a selection, its type is +`´C´.this.type`, otherwise it is the self type of class ´C´. + +A reference `super.´m´` refers statically to a method or type ´m´ +in the least proper supertype of the innermost template containing the +reference. It evaluates to the member ´m'´ in the actual supertype of +that template which is equal to ´m´ or which overrides ´m´. The +statically referenced member ´m´ must be a type or a +method. + + + +If it is +a method, it must be concrete, or the template +containing the reference must have a member ´m'´ which overrides ´m´ +and which is labeled `abstract override`. + +A reference `´C´.super.´m´` refers statically to a method +or type ´m´ in the least proper supertype of the innermost enclosing class or +object definition named ´C´ which encloses the reference. It evaluates +to the member ´m'´ in the actual supertype of that class or object +which is equal to ´m´ or which overrides ´m´. The +statically referenced member ´m´ must be a type or a +method. If the statically +referenced member ´m´ is a method, it must be concrete, or the innermost enclosing +class or object definition named ´C´ must have a member ´m'´ which +overrides ´m´ and which is labeled `abstract override`. + +The `super` prefix may be followed by a trait qualifier +`[´T\,´]`, as in `´C´.super[´T\,´].´x´`. This is +called a _static super reference_. In this case, the reference is +to the type or method of ´x´ in the parent trait of ´C´ whose simple +name is ´T´. That member must be uniquely defined. If it is a method, +it must be concrete. + +###### Example +Consider the following class definitions + +```scala +class Root { def x = "Root" } +class A extends Root { override def x = "A" ; def superA = super.x } +trait B extends Root { override def x = "B" ; def superB = super.x } +class C extends Root with B { + override def x = "C" ; def superC = super.x +} +class D extends A with B { + override def x = "D" ; def superD = super.x +} +``` + +The linearization of class `C` is `{C, B, Root}` and +the linearization of class `D` is `{D, B, A, Root}`. +Then we have: + +```scala +(new A).superA == "Root" + +(new C).superB == "Root" +(new C).superC == "B" + +(new D).superA == "Root" +(new D).superB == "A" +(new D).superD == "B" +``` + +Note that the `superB` method returns different results +depending on whether `B` is mixed in with class `Root` or `A`. + +## Function Applications + +```ebnf +SimpleExpr ::= SimpleExpr1 ArgumentExprs +ArgumentExprs ::= ‘(’ [Exprs] ‘)’ + | ‘(’ ‘using’ Exprs ‘)’ + | ‘(’ [Exprs ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ + | [nl] BlockExpr +Exprs ::= Expr {‘,’ Expr} +``` + +An application `´f(e_1 , \ldots , e_m)´` applies the function `´f´` to the argument expressions `´e_1, \ldots , e_m´`. For this expression to be well-typed, the function must be *applicable* to its arguments, which is defined next by case analysis on ´f´'s type. + +If ´f´ has a method type `(´p_1´:´T_1 , \ldots , p_n´:´T_n´)´U´`, each argument expression ´e_i´ is typed with the corresponding parameter type ´T_i´ as expected type. Let ´S_i´ be the type of argument ´e_i´ ´(i = 1 , \ldots , m)´. The method ´f´ must be _applicable_ to its arguments ´e_1, \ldots , e_n´ of types ´S_1 , \ldots , S_n´. We say that an argument expression ´e_i´ is a _named_ argument if it has the form `´x_i=e'_i´` and `´x_i´` is one of the parameter names `´p_1, \ldots, p_n´`. + +Once the types ´S_i´ have been determined, the method ´f´ of the above method type is said to be applicable if all of the following conditions hold: + - for every named argument ´p_j=e_i'´ the type ´S_i´ is [compatible](03-types.html#compatibility) with the parameter type ´T_j´; + - for every positional argument ´e_i´ the type ´S_i´ is [compatible](03-types.html#compatibility) with ´T_i´; + - if the expected type is defined, the result type ´U´ is [compatible](03-types.html#compatibility) to it. + +If ´f´ is a polymorphic method, [local type inference](#local-type-inference) is used to instantiate ´f´'s type parameters. +The polymorphic method is applicable if type inference can determine type arguments so that the instantiated method is applicable. + +If ´f´ has some value type, the application is taken to be equivalent to `´f´.apply(´e_1 , \ldots , e_m´)`, +i.e. the application of an `apply` method defined by ´f´. The value `´f´` is applicable to the given arguments if `´f´.apply` is applicable. + + +The application `´f´(´e_1 , \ldots , e_n´)` evaluates ´f´ and then each argument +´e_1 , \ldots , e_n´ from left to right, except for arguments that correspond to +a by-name parameter (see below). Each argument expression is converted to the +type of its corresponding formal parameter. After that, the application is +rewritten to the function's right hand side, with actual arguments substituted +for formal parameters. The result of evaluating the rewritten right-hand side +is finally converted to the function's declared result type, if one is given. + +The case of a formal parameter with a parameterless +method type `=> ´T´` is treated specially. In this case, the +corresponding actual argument expression ´e´ is not evaluated before the +application. Instead, every use of the formal parameter on the +right-hand side of the rewrite rule entails a re-evaluation of ´e´. +In other words, the evaluation order for +`=>`-parameters is _call-by-name_ whereas the evaluation +order for normal parameters is _call-by-value_. +Furthermore, it is required that ´e´'s [packed type](#expression-typing) +conforms to the parameter type ´T´. +The behavior of by-name parameters is preserved if the application is +transformed into a block due to named or default arguments. In this case, +the local value for that parameter has the form `val ´y_i´ = () => ´e´` +and the argument passed to the function is `´y_i´()`. + +The last argument in an application may be marked as a sequence +argument, e.g. `´e´: _*`. Such an argument must correspond +to a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters) of type +`´S´*` and it must be the only argument matching this +parameter (i.e. the number of formal parameters and actual arguments +must be the same). Furthermore, the type of ´e´ must conform to +`scala.Seq[´T´]`, for some type ´T´ which conforms to +´S´. In this case, the argument list is transformed by replacing the +sequence ´e´ with its elements. When the application uses named +arguments, the vararg parameter has to be specified exactly once. + +If only a single argument is supplied, it may be supplied as a block expression +and parentheses can be omitted, in the form `´f´ { block }`. This is valid when +`f` has a single formal parameter or when all other formal parameters have +default values. + +A function application usually allocates a new frame on the program's +run-time stack. However, if a local method or a final method calls +itself as its last action, the call is executed using the stack-frame +of the caller. + +###### Example +Assume the following method which computes the sum of a +variable number of arguments: + +```scala +def sum(xs: Int*) = xs.foldLeft(0)((x, y) => x + y) +``` + +Then + +```scala +sum(1, 2, 3, 4) +sum(List(1, 2, 3, 4): _*) +``` + +both yield `10` as result. On the other hand, + +```scala +sum(List(1, 2, 3, 4)) +``` + +would not typecheck. + +An argument list may begin with the soft keyword `using` to facilitate cross-compilation with Scala 3. +The keyword is ignored. + +### Named and Default Arguments + +If an application is to use named arguments ´p = e´ or default +arguments, the following conditions must hold. + +- For every named argument ´p_i = e_i´ which appears left of a positional argument + in the argument list ´e_1 \ldots e_m´, the argument position ´i´ coincides with + the position of parameter ´p_i´ in the parameter list of the applied method. +- The names ´x_i´ of all named arguments are pairwise distinct and no named + argument defines a parameter which is already specified by a + positional argument. +- Every formal parameter ´p_j:T_j´ which is not specified by either a positional + or named argument has a default argument. + +If the application uses named or default +arguments the following transformation is applied to convert it into +an application without named or default arguments. + +If the method ´f´ +has the form `´p.m´[´\mathit{targs}´]` it is transformed into the +block + +```scala +{ val q = ´p´ + q.´m´[´\mathit{targs}´] +} +``` + +If the method ´f´ is itself an application expression the transformation +is applied recursively on ´f´. The result of transforming ´f´ is a block of +the form + +```scala +{ val q = ´p´ + val ´x_1´ = expr´_1´ + ´\ldots´ + val ´x_k´ = expr´_k´ + q.´m´[´\mathit{targs}´](´\mathit{args}_1´)´, \ldots ,´(´\mathit{args}_l´) +} +``` + +where every argument in ´(\mathit{args}\_1) , \ldots , (\mathit{args}\_l)´ is a reference to +one of the values ´x_1 , \ldots , x_k´. To integrate the current application +into the block, first a value definition using a fresh name ´y_i´ is created +for every argument in ´e_1 , \ldots , e_m´, which is initialised to ´e_i´ for +positional arguments and to ´e'_i´ for named arguments of the form +`´x_i=e'_i´`. Then, for every parameter which is not specified +by the argument list, a value definition using a fresh name ´z_i´ is created, +which is initialized using the method computing the +[default argument](04-basic-declarations-and-definitions.html#function-declarations-and-definitions) of +this parameter. + +Let ´\mathit{args}´ be a permutation of the generated names ´y_i´ and ´z_i´ such such +that the position of each name matches the position of its corresponding +parameter in the method type `(´p_1:T_1 , \ldots , p_n:T_n´)´U´`. +The final result of the transformation is a block of the form + +```scala +{ val q = ´p´ + val ´x_1´ = expr´_1´ + ´\ldots´ + val ´x_l´ = expr´_k´ + val ´y_1´ = ´e_1´ + ´\ldots´ + val ´y_m´ = ´e_m´ + val ´z_1´ = ´q.m\$default\$i[\mathit{targs}](\mathit{args}_1), \ldots ,(\mathit{args}_l)´ + ´\ldots´ + val ´z_d´ = ´q.m\$default\$j[\mathit{targs}](\mathit{args}_1), \ldots ,(\mathit{args}_l)´ + q.´m´[´\mathit{targs}´](´\mathit{args}_1´)´, \ldots ,´(´\mathit{args}_l´)(´\mathit{args}´) +} +``` + +### Signature Polymorphic Methods + +For invocations of signature polymorphic methods of the target platform `´f´(´e_1 , \ldots , e_m´)`, +the invoked method has a different method type `(´p_1´:´T_1 , \ldots , p_n´:´T_n´)´U´` at each call +site. The parameter types `´T_ , \ldots , T_n´` are the types of the argument expressions +`´e_1 , \ldots , e_m´`. If the declared return type `´R´` of the signature polymorphic method is +any type other than `scala.AnyRef`, then the return type `´U´` is `´R´`. +Otherwise, `´U´` is the expected type at the call site. If the expected type is undefined then +`´U´` is `scala.AnyRef`. The parameter names `´p_1 , \ldots , p_n´` are fresh. + +###### Note + +On the Java platform version 11 and later, signature polymorphic methods are native, +members of `java.lang.invoke.MethodHandle` or `java.lang.invoke.VarHandle`, and have a single +repeated parameter of type `java.lang.Object*`. + + +## Method Values + +```ebnf +SimpleExpr ::= SimpleExpr1 ‘_’ +``` + +The expression `´e´ _` is well-formed if ´e´ is of method +type or if ´e´ is a call-by-name parameter. If ´e´ is a method with +parameters, `´e´ _` represents ´e´ converted to a function +type by [eta expansion](#eta-expansion-section). If ´e´ is a +parameterless method or call-by-name parameter of type +`=> ´T´`, `´e´ _` represents the function of type +`() => ´T´`, which evaluates ´e´ when it is applied to the empty +parameter list `()`. + +###### Example +The method values in the left column are each equivalent to the [eta-expanded expressions](#eta-expansion-section) on the right. + +| placeholder syntax | eta-expansion | +|------------------------------ | ----------------------------------------------------------------------------| +|`math.sin _` | `x => math.sin(x)` | +|`math.pow _` | `(x1, x2) => math.pow(x1, x2)` | +|`val vs = 1 to 9; vs.fold _` | `(z) => (op) => vs.fold(z)(op)` | +|`(1 to 9).fold(z)_` | `{ val eta1 = 1 to 9; val eta2 = z; op => eta1.fold(eta2)(op) }` | +|`Some(1).fold(??? : Int)_` | `{ val eta1 = Some(1); val eta2 = () => ???; op => eta1.fold(eta2())(op) }` | + +Note that a space is necessary between a method name and the trailing underscore +because otherwise the underscore would be considered part of the name. + +## Type Applications + +```ebnf +SimpleExpr ::= SimpleExpr TypeArgs +``` + +A _type application_ `´e´[´T_1 , \ldots , T_n´]` instantiates +a polymorphic value ´e´ of type +`[´a_1´ >: ´L_1´ <: ´U_1, \ldots , a_n´ >: ´L_n´ <: ´U_n´]´S´` +with argument types +`´T_1 , \ldots , T_n´`. Every argument type ´T_i´ must obey +the corresponding bounds ´L_i´ and ´U_i´. That is, for each ´i = 1 +, \ldots , n´, we must have ´\sigma L_i <: T_i <: \sigma +U_i´, where ´\sigma´ is the substitution ´[a_1 := T_1 , \ldots , a_n +:= T_n]´. The type of the application is ´\sigma S´. + +If the function part ´e´ is of some value type, the type application +is taken to be equivalent to +`´e´.apply[´T_1 , \ldots ,´ T´_n´]`, i.e. the application of an `apply` method defined by +´e´. + +Type applications can be omitted if +[local type inference](#local-type-inference) can infer best type parameters +for a polymorphic method from the types of the actual method arguments +and the expected result type. + +## Tuples + +```ebnf +SimpleExpr ::= ‘(’ [Exprs] ‘)’ +``` + +A _tuple expression_ `(´e_1 , \ldots , e_n´)` is an alias +for the class instance creation +`scala.Tuple´n´(´e_1 , \ldots , e_n´)`, where ´n \geq 2´. +The empty tuple +`()` is the unique value of type `scala.Unit`. + +## Instance Creation Expressions + +```ebnf +SimpleExpr ::= ‘new’ (ClassTemplate | TemplateBody) +``` + +A _simple instance creation expression_ is of the form +`new ´c´` +where ´c´ is a [constructor invocation](05-classes-and-objects.html#constructor-invocations). Let ´T´ be +the type of ´c´. Then ´T´ must +denote a (a type instance of) a non-abstract subclass of +`scala.AnyRef`. Furthermore, the _concrete self type_ of the +expression must conform to the [self type](05-classes-and-objects.html#templates) of the class denoted by +´T´. The concrete self type is normally +´T´, except if the expression `new ´c´` appears as the +right hand side of a value definition + +```scala +val ´x´: ´S´ = new ´c´ +``` + +(where the type annotation `: ´S´` may be missing). +In the latter case, the concrete self type of the expression is the +compound type `´T´ with ´x´.type`. + +The expression is evaluated by creating a fresh +object of type ´T´ which is initialized by evaluating ´c´. The +type of the expression is ´T´. + +A _general instance creation expression_ is of the form +`new ´t´` for some [class template](05-classes-and-objects.html#templates) ´t´. +Such an expression is equivalent to the block + +```scala +{ class ´a´ extends ´t´; new ´a´ } +``` + +where ´a´ is a fresh name of an _anonymous class_ which is +inaccessible to user programs. + +There is also a shorthand form for creating values of structural +types: If `{´D´}` is a class body, then +`new {´D´}` is equivalent to the general instance creation expression +`new AnyRef{´D´}`. + +###### Example +Consider the following structural instance creation expression: + +```scala +new { def getName() = "aaron" } +``` + +This is a shorthand for the general instance creation expression + +```scala +new AnyRef{ def getName() = "aaron" } +``` + +The latter is in turn a shorthand for the block + +```scala +{ class anon$X extends AnyRef{ def getName() = "aaron" }; new anon$X } +``` + +where `anon$X` is some freshly created name. + +## Blocks + +```ebnf +BlockExpr ::= ‘{’ CaseClauses ‘}’ + | ‘{’ Block ‘}’ +Block ::= BlockStat {semi BlockStat} [ResultExpr] +``` + +A _block expression_ `{´s_1´; ´\ldots´; ´s_n´; ´e\,´}` is +constructed from a sequence of block statements ´s_1 , \ldots , s_n´ +and a final expression ´e´. The statement sequence may not contain +two definitions or declarations that bind the same name in the same +namespace. The final expression can be omitted, in which +case the unit value `()` is assumed. + +The expected type of the final expression ´e´ is the expected +type of the block. The expected type of all preceding statements is +undefined. + +The type of a block `´s_1´; ´\ldots´; ´s_n´; ´e´` is +`´T´ forSome {´\,Q\,´}`, where ´T´ is the type of ´e´ and ´Q´ +contains [existential clauses](03-types.html#existential-types) +for every value or type name which is free in ´T´ +and which is defined locally in one of the statements ´s_1 , \ldots , s_n´. +We say the existential clause _binds_ the occurrence of the value or type name. +Specifically, + +- A locally defined type definition `type´\;t = T´` + is bound by the existential clause `type´\;t >: T <: T´`. + It is an error if ´t´ carries type parameters. +- A locally defined value definition `val´\;x: T = e´` is + bound by the existential clause `val´\;x: T´`. +- A locally defined class definition `class´\;c´ extends´\;t´` + is bound by the existential clause `type´\;c <: T´` where + ´T´ is the least class type or refinement type which is a proper + supertype of the type ´c´. It is an error if ´c´ carries type parameters. +- A locally defined object definition `object´\;x\;´extends´\;t´` + is bound by the existential clause `val´\;x: T´` where + ´T´ is the least class type or refinement type which is a proper supertype of the type + `´x´.type`. + +Evaluation of the block entails evaluation of its +statement sequence, followed by an evaluation of the final expression +´e´, which defines the result of the block. + +A block expression `{´c_1´; ´\ldots´; ´c_n´}` where ´s_1 , \ldots , s_n´ are +case clauses forms a [pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions). + +###### Example +Assuming a class `Ref[T](x: T)`, the block + +```scala +{ class C extends B {´\ldots´} ; new Ref(new C) } +``` + +has the type `Ref[_1] forSome { type _1 <: B }`. +The block + +```scala +{ class C extends B {´\ldots´} ; new C } +``` + +simply has type `B`, because with the rules [here](03-types.html#simplification-rules) +the existentially quantified type +`_1 forSome { type _1 <: B }` can be simplified to `B`. + +## Prefix, Infix, and Postfix Operations + +```ebnf +PostfixExpr ::= InfixExpr [id [nl]] +InfixExpr ::= PrefixExpr + | InfixExpr id [nl] InfixExpr +PrefixExpr ::= [‘-’ | ‘+’ | ‘!’ | ‘~’] SimpleExpr +``` + +Expressions can be constructed from operands and operators. + +### Prefix Operations + +A prefix operation ´\mathit{op};e´ consists of a prefix operator ´\mathit{op}´, which +must be one of the identifiers ‘`+`’, ‘`-`’, ‘`!`’ or ‘`~`’, +which must not be enclosed in backquotes. +The expression ´\mathit{op};e´ is +equivalent to the postfix method application +`e.unary_´\mathit{op}´`. + + + +Prefix operators are different from normal method applications in +that their operand expression need not be atomic. For instance, the +input sequence `-sin(x)` is read as `-(sin(x))`, whereas the +method application `negate sin(x)` would be parsed as the +application of the infix operator `sin` to the operands +`negate` and `(x)`. + +### Postfix Operations + +A postfix operator can be an arbitrary identifier. The postfix +operation ´e;\mathit{op}´ is interpreted as ´e.\mathit{op}´. + +### Infix Operations + +An infix operator can be an arbitrary identifier. Infix operators have +precedence and associativity defined as follows: + +The _precedence_ of an infix operator is determined by the operator's first +character. Characters are listed below in increasing order of +precedence, with characters on the same line having the same precedence. + +```scala +(all letters, as defined in [chapter 1](01-lexical-syntax.html), including `_` and `$`) +| +^ +& += ! +< > +: ++ - +* / % +(other operator characters, as defined in [chapter 1](01-lexical-syntax.html), including Unicode categories `Sm` and `So`) +``` + +That is, operators starting with a letter have lowest precedence, +followed by operators starting with ‘`|`’, etc. + +There's one exception to this rule, which concerns +[_assignment operators_](#assignment-operators). +The precedence of an assignment operator is the same as the one +of simple assignment `(=)`. That is, it is lower than the +precedence of any other operator. + +The _associativity_ of an operator is determined by the operator's +last character. Operators ending in a colon ‘`:`’ are +right-associative. All other operators are left-associative. + +Precedence and associativity of operators determine the grouping of +parts of an expression as follows. + +- If there are several infix operations in an + expression, then operators with higher precedence bind more closely + than operators with lower precedence. +- If there are consecutive infix + operations ´e_0; \mathit{op}\_1; e_1; \mathit{op}\_2 \ldots \mathit{op}\_n; e_n´ + with operators ´\mathit{op}\_1 , \ldots , \mathit{op}\_n´ of the same precedence, + then all these operators must + have the same associativity. If all operators are left-associative, + the sequence is interpreted as + ´(\ldots(e_0;\mathit{op}\_1;e_1);\mathit{op}\_2\ldots);\mathit{op}\_n;e_n´. + Otherwise, if all operators are right-associative, the + sequence is interpreted as + ´e_0;\mathit{op}\_1;(e_1;\mathit{op}\_2;(\ldots \mathit{op}\_n;e_n)\ldots)´. +- Postfix operators always have lower precedence than infix + operators. E.g. ´e_1;\mathit{op}\_1;e_2;\mathit{op}\_2´ is always equivalent to + ´(e_1;\mathit{op}\_1;e_2);\mathit{op}\_2´. + +The right-hand operand of a left-associative operator may consist of +several arguments enclosed in parentheses, e.g. ´e;\mathit{op};(e_1,\ldots,e_n)´. +This expression is then interpreted as ´e.\mathit{op}(e_1,\ldots,e_n)´. + +A left-associative binary +operation ´e_1;\mathit{op};e_2´ is interpreted as ´e_1.\mathit{op}(e_2)´. If ´\mathit{op}´ is +right-associative and its parameter is passed by name, the same operation is interpreted as +´e_2.\mathit{op}(e_1)´. If ´\mathit{op}´ is right-associative and its parameter is passed by value, +it is interpreted as `{ val ´x´=´e_1´; ´e_2´.´\mathit{op}´(´x\,´) }`, where ´x´ is a fresh name. + +### Assignment Operators + +An _assignment operator_ is an operator symbol (syntax category +`op` in [Identifiers](01-lexical-syntax.html#identifiers)) that ends in an equals character +“`=`”, with the following exceptions: + +1. the operator also starts with an equals character, or +1. the operator is one of `(<=)`, `(>=)`, `(!=)`. + +Assignment operators are treated specially in that they +can be expanded to assignments if no other interpretation is valid. + +Let's consider an assignment operator such as `+=` in an infix +operation `´l´ += ´r´`, where ´l´, ´r´ are expressions. +This operation can be re-interpreted as an operation which corresponds +to the assignment + +```scala +´l´ = ´l´ + ´r´ +``` + +except that the operation's left-hand-side ´l´ is evaluated only once. + +The re-interpretation occurs if the following two conditions are fulfilled. + +1. The left-hand-side ´l´ does not have a member named + `+=`, and also cannot be converted by an + [implicit conversion](#implicit-conversions) + to a value with a member named `+=`. +1. The assignment `´l´ = ´l´ + ´r´` is type-correct. + In particular this implies that ´l´ refers to a variable or object + that can be assigned to, and that is convertible to a value with a member + named `+`. + +## Typed Expressions + +```ebnf +Expr1 ::= PostfixExpr ‘:’ CompoundType +``` + +The _typed expression_ ´e: T´ has type ´T´. The type of +expression ´e´ is expected to conform to ´T´. The result of +the expression is the value of ´e´ converted to type ´T´. + +###### Example +Here are examples of well-typed and ill-typed expressions. + +```scala +1: Int // legal, of type Int +1: Long // legal, of type Long +// 1: string // ***** illegal +``` + +## Annotated Expressions + +```ebnf +Expr1 ::= PostfixExpr ‘:’ Annotation {Annotation} +``` + +An _annotated expression_ `´e´: @´a_1´ ´\ldots´ @´a_n´` +attaches [annotations](11-annotations.html#user-defined-annotations) ´a_1 , \ldots , a_n´ to the +expression ´e´. + +## Assignments + +```ebnf +Expr1 ::= [SimpleExpr ‘.’] id ‘=’ Expr + | PrefixOperator SimpleExpr ‘=’ Expr + | SimpleExpr1 ArgumentExprs ‘=’ Expr +``` + +The interpretation of an assignment to a simple variable `´x´ = ´e´` +depends on the definition of ´x´. If ´x´ denotes a mutable +variable, then the assignment changes the current value of ´x´ to be +the result of evaluating the expression ´e´. The type of ´e´ is +expected to conform to the type of ´x´. If ´x´ is a parameterless +method defined in some template, and the same template contains a +setter method `´x´_=` as member, then the assignment +`´x´ = ´e´` is interpreted as the invocation +`´x´_=(´e\,´)` of that setter method. Analogously, an +assignment `´f.x´ = ´e´` to a parameterless method ´x´ +is interpreted as the invocation `´f.x´_=(´e\,´)`. +If ´x´ is an application of a unary operator, then the expression +is interpreted as though it were written as the explicit application +`´x´.unary_´\mathit{op}´`, namely, as `´x´.unary_´\mathit{op}´_=(´e\,´)`. + +An assignment `´f´(´\mathit{args}\,´) = ´e´` with a method application to the +left of the ‘`=`’ operator is interpreted as +`´f.´update(´\mathit{args}´, ´e\,´)`, i.e. +the invocation of an `update` method defined by ´f´. + +###### Example +Here are some assignment expressions and their equivalent expansions. + +| assignment | expansion | +|--------------------------|----------------------| +|`x.f = e` | `x.f_=(e)` | +|`x.f() = e` | `x.f.update(e)` | +|`x.f(i) = e` | `x.f.update(i, e)` | +|`x.f(i, j) = e` | `x.f.update(i, j, e)`| + +###### Example Imperative Matrix Multiplication + +Here is the usual imperative code for matrix multiplication. + +```scala +def matmul(xss: Array[Array[Double]], yss: Array[Array[Double]]) = { + val zss: Array[Array[Double]] = new Array(xss.length, yss(0).length) + var i = 0 + while (i < xss.length) { + var j = 0 + while (j < yss(0).length) { + var acc = 0.0 + var k = 0 + while (k < yss.length) { + acc = acc + xss(i)(k) * yss(k)(j) + k += 1 + } + zss(i)(j) = acc + j += 1 + } + i += 1 + } + zss +} +``` + +Desugaring the array accesses and assignments yields the following +expanded version: + +```scala +def matmul(xss: Array[Array[Double]], yss: Array[Array[Double]]) = { + val zss: Array[Array[Double]] = new Array(xss.length, yss.apply(0).length) + var i = 0 + while (i < xss.length) { + var j = 0 + while (j < yss.apply(0).length) { + var acc = 0.0 + var k = 0 + while (k < yss.length) { + acc = acc + xss.apply(i).apply(k) * yss.apply(k).apply(j) + k += 1 + } + zss.apply(i).update(j, acc) + j += 1 + } + i += 1 + } + zss +} +``` + +## Conditional Expressions + +```ebnf +Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] +``` + +The _conditional expression_ `if (´e_1´) ´e_2´ else ´e_3´` chooses +one of the values of ´e_2´ and ´e_3´, depending on the +value of ´e_1´. The condition ´e_1´ is expected to +conform to type `Boolean`. The then-part ´e_2´ and the +else-part ´e_3´ are both expected to conform to the expected +type of the conditional expression. The type of the conditional +expression is the [weak least upper bound](03-types.html#weak-conformance) +of the types of ´e_2´ and +´e_3´. A semicolon preceding the `else` symbol of a +conditional expression is ignored. + +The conditional expression is evaluated by evaluating first +´e_1´. If this evaluates to `true`, the result of +evaluating ´e_2´ is returned, otherwise the result of +evaluating ´e_3´ is returned. + +A short form of the conditional expression eliminates the +else-part. The conditional expression `if (´e_1´) ´e_2´` is +evaluated as if it was `if (´e_1´) ´e_2´ else ()`. + +## While Loop Expressions + +```ebnf +Expr1 ::= ‘while’ ‘(’ Expr ‘)’ {nl} Expr +``` + +The _while loop expression_ `while (´e_1´) ´e_2´` is typed and +evaluated as if it was an application of `whileLoop (´e_1´) (´e_2´)` where +the hypothetical method `whileLoop` is defined as follows. + +```scala +def whileLoop(cond: => Boolean)(body: => Unit): Unit = + if (cond) { body ; whileLoop(cond)(body) } else {} +``` + +## Do Loop Expressions + +```ebnf +Expr1 ::= ‘do’ Expr [semi] ‘while’ ‘(’ Expr ‘)’ +``` + +The _do loop expression_ `do ´e_1´ while (´e_2´)` is typed and +evaluated as if it was the expression `(´e_1´ ; while (´e_2´) ´e_1´)`. +A semicolon preceding the `while` symbol of a do loop expression is ignored. + +## For Comprehensions and For Loops + +```ebnf +Expr1 ::= ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) + {nl} [‘yield’] Expr +Enumerators ::= Generator {semi Generator} +Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} +Guard ::= ‘if’ PostfixExpr +``` + +A _for loop_ `for (´\mathit{enums}\,´) ´e´` executes expression ´e´ +for each binding generated by the enumerators ´\mathit{enums}´. +A _for comprehension_ `for (´\mathit{enums}\,´) yield ´e´` evaluates +expression ´e´ for each binding generated by the enumerators ´\mathit{enums}´ +and collects the results. An enumerator sequence always starts with a +generator; this can be followed by further generators, value +definitions, or guards. + +A _generator_ `´p´ <- ´e´` produces bindings from an expression ´e´ which is +matched in some way against pattern ´p´. Optionally, `case` can appear in front +of a generator pattern, this has no meaning in Scala 2 but will be [required in +Scala 3 if `p` is not +irrefutable](https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html). + +A _value definition_ `´p´ = ´e´` +binds the value name ´p´ (or several names in a pattern ´p´) to +the result of evaluating the expression ´e´. A _guard_ +`if ´e´` contains a boolean expression which restricts +enumerated bindings. The precise meaning of generators and guards is +defined by translation to invocations of four methods: `map`, +`withFilter`, `flatMap`, and `foreach`. These methods can +be implemented in different ways for different carrier types. + +The translation scheme is as follows. In a first step, every +generator `´p´ <- ´e´`, where ´p´ is not [irrefutable](08-pattern-matching.html#patterns) +for the type of ´e´ is replaced by + +```scala +´p´ <- ´e´.withFilter { case ´p´ => true; case _ => false } +``` + +Then, the following rules are applied repeatedly until all +comprehensions have been eliminated. + + - A for comprehension + `for (´p´ <- ´e\,´) yield ´e'´` + is translated to + `´e´.map { case ´p´ => ´e'´ }`. + - A for loop + `for (´p´ <- ´e\,´) ´e'´` + is translated to + `´e´.foreach { case ´p´ => ´e'´ }`. + - A for comprehension + + ```scala + for (´p´ <- ´e´; ´p'´ <- ´e'; \ldots´) yield ´e''´ + ``` + + where `´\ldots´` is a (possibly empty) + sequence of generators, definitions, or guards, + is translated to + + ```scala + ´e´.flatMap { case ´p´ => for (´p'´ <- ´e'; \ldots´) yield ´e''´ } + ``` + + - A for loop + + ```scala + for (´p´ <- ´e´; ´p'´ <- ´e'; \ldots´) ´e''´ + ``` + + where `´\ldots´` is a (possibly empty) + sequence of generators, definitions, or guards, + is translated to + + ```scala + ´e´.foreach { case ´p´ => for (´p'´ <- ´e'; \ldots´) ´e''´ } + ``` + + - A generator `´p´ <- ´e´` followed by a guard + `if ´g´` is translated to a single generator + `´p´ <- ´e´.withFilter((´x_1 , \ldots , x_n´) => ´g\,´)` where + ´x_1 , \ldots , x_n´ are the free variables of ´p´. + + - A generator `´p´ <- ´e´` followed by a value definition + `´p'´ = ´e'´` is translated to the following generator of pairs of values, where + ´x´ and ´x'´ are fresh names: + + ```scala + (´p´, ´p'´) <- for (´x @ p´ <- ´e´) yield { val ´x' @ p'´ = ´e'´; (´x´, ´x'´) } + ``` + +###### Example +The following code produces all pairs of numbers between ´1´ and ´n-1´ +whose sums are prime. + +```scala +for { i <- 1 until n + j <- 1 until i + if isPrime(i+j) +} yield (i, j) +``` + +The for comprehension is translated to: + +```scala +(1 until n) + .flatMap { + case i => (1 until i) + .withFilter { j => isPrime(i+j) } + .map { case j => (i, j) } } +``` + +###### Example +For comprehensions can be used to express vector +and matrix algorithms concisely. +For instance, here is a method to compute the transpose of a given matrix: + + + +```scala +def transpose[A](xss: Array[Array[A]]) = { + for (i <- Array.range(0, xss(0).length)) yield + for (xs <- xss) yield xs(i) +} +``` + +Here is a method to compute the scalar product of two vectors: + +```scala +def scalprod(xs: Array[Double], ys: Array[Double]) = { + var acc = 0.0 + for ((x, y) <- xs zip ys) acc = acc + x * y + acc +} +``` + +Finally, here is a method to compute the product of two matrices. +Compare with the [imperative version](#example-imperative-matrix-multiplication). + +```scala +def matmul(xss: Array[Array[Double]], yss: Array[Array[Double]]) = { + val ysst = transpose(yss) + for (xs <- xss) yield + for (yst <- ysst) yield + scalprod(xs, yst) +} +``` + +The code above makes use of the fact that `map`, `flatMap`, +`withFilter`, and `foreach` are defined for instances of class +`scala.Array`. + +## Return Expressions + +```ebnf +Expr1 ::= ‘return’ [Expr] +``` + +A _return expression_ `return ´e´` must occur inside the body of some +enclosing user defined method. The innermost enclosing method in a +source program, ´m´, must have an explicitly declared result type, and +the type of ´e´ must conform to it. + +The return expression evaluates the expression ´e´ and returns its +value as the result of ´m´. The evaluation of any statements or +expressions following the return expression is omitted. The type of +a return expression is `scala.Nothing`. + +The expression ´e´ may be omitted. The return expression +`return` is type-checked and evaluated as if it were `return ()`. + +Returning from the method from within a nested function may be +implemented by throwing and catching a +`scala.runtime.NonLocalReturnControl`. Any exception catches +between the point of return and the enclosing methods might see +and catch that exception. A key comparison makes sure that this +exception is only caught by the method instance which is terminated +by the return. + +If the return expression is itself part of an anonymous function, it +is possible that the enclosing method ´m´ has already returned +before the return expression is executed. In that case, the thrown +`scala.runtime.NonLocalReturnControl` will not be caught, and will +propagate up the call stack. + +## Throw Expressions + +```ebnf +Expr1 ::= ‘throw’ Expr +``` + +A _throw expression_ `throw ´e´` evaluates the expression +´e´. The type of this expression must conform to +`Throwable`. If ´e´ evaluates to an exception +reference, evaluation is aborted with the thrown exception. If ´e´ +evaluates to `null`, evaluation is instead aborted with a +`NullPointerException`. If there is an active +[`try` expression](#try-expressions) which handles the thrown +exception, evaluation resumes with the handler; otherwise the thread +executing the `throw` is aborted. The type of a throw expression +is `scala.Nothing`. + +## Try Expressions + +```ebnf +Expr1 ::= ‘try’ Expr [‘catch’ Expr] [‘finally’ Expr] +``` + +A _try expression_ is of the form `try { ´b´ } catch ´h´` +where the handler ´h´ is usually a +[pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions) + +```scala +{ case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ } +``` + +This expression is evaluated by evaluating the block +´b´. If evaluation of ´b´ does not cause an exception to be +thrown, the result of ´b´ is returned. Otherwise the +handler ´h´ is applied to the thrown exception. +If the handler contains a case matching the thrown exception, +the first such case is invoked. If the handler contains +no case matching the thrown exception, the exception is +re-thrown. More generally, if the handler is a `PartialFunction`, +it is applied only if it is defined at the given exception. + +Let ´\mathit{pt}´ be the expected type of the try expression. The block +´b´ is expected to conform to ´\mathit{pt}´. The handler ´h´ +is expected conform to type `scala.Function[scala.Throwable, ´\mathit{pt}\,´]`. +The type of the try expression is the [weak least upper bound](03-types.html#weak-conformance) +of the type of ´b´ and the result type of ´h´. + +A try expression `try { ´b´ } finally ´e´` evaluates the block +´b´. If evaluation of ´b´ does not cause an exception to be +thrown, the expression ´e´ is evaluated. If an exception is thrown +during evaluation of ´e´, the evaluation of the try expression is +aborted with the thrown exception. If no exception is thrown during +evaluation of ´e´, the result of ´b´ is returned as the +result of the try expression. + +If an exception is thrown during evaluation of ´b´, the finally block +´e´ is also evaluated. If another exception ´e´ is thrown +during evaluation of ´e´, evaluation of the try expression is +aborted with the thrown exception. If no exception is thrown during +evaluation of ´e´, the original exception thrown in ´b´ is +re-thrown once evaluation of ´e´ has completed. The block +´b´ is expected to conform to the expected type of the try +expression. The finally expression ´e´ is expected to conform to +type `Unit`. + +A try expression `try { ´b´ } catch ´e_1´ finally ´e_2´` +is a shorthand +for `try { try { ´b´ } catch ´e_1´ } finally ´e_2´`. + +## Anonymous Functions + +```ebnf +Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr +ResultExpr ::= (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block +Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’ +Binding ::= (id | ‘_’) [‘:’ Type] +``` + +The anonymous function of arity ´n´, `(´x_1´: ´T_1 , \ldots , x_n´: ´T_n´) => e` maps parameters ´x_i´ of types ´T_i´ to a result given by expression ´e´. The scope of each formal parameter ´x_i´ is ´e´. Formal parameters must have pairwise distinct names. + +In the case of a single untyped formal parameter, `(´x\,´) => ´e´` can be abbreviated to `´x´ => ´e´`. If an anonymous function `(´x´: ´T\,´) => ´e´` with a single typed parameter appears as the result expression of a block, it can be abbreviated to `´x´: ´T´ => e`. + +A formal parameter may also be a wildcard represented by an underscore `_`. In that case, a fresh name for the parameter is chosen arbitrarily. + +A named parameter of an anonymous function may be optionally preceded by an `implicit` modifier. In that case the parameter is labeled [`implicit`](07-implicits.html#implicit-parameters-and-views); however the parameter section itself does not count as an [implicit parameter section](07-implicits.html#implicit-parameters). Hence, arguments to anonymous functions always have to be given explicitly. + +### Translation +If the expected type of the anonymous function is of the shape `scala.Function´n´[´S_1 , \ldots , S_n´, ´R\,´]`, or can be [SAM-converted](#sam-conversion) to such a function type, the type `´T_i´` of a parameter `´x_i´` can be omitted, as far as `´S_i´` is defined in the expected type, and `´T_i´ = ´S_i´` is assumed. Furthermore, the expected type when type checking ´e´ is ´R´. + +If there is no expected type for the function literal, all formal parameter types `´T_i´` must be specified explicitly, and the expected type of ´e´ is undefined. The type of the anonymous function is `scala.Function´n´[´T_1 , \ldots , T_n´, ´R\,´]`, where ´R´ is the [packed type](#expression-typing) of ´e´. ´R´ must be equivalent to a type which does not refer to any of the formal parameters ´x_i´. + +The eventual run-time value of an anonymous function is determined by the expected type: + - a subclass of one of the builtin function types, `scala.Function´n´[´S_1 , \ldots , S_n´, ´R\,´]` (with ´S_i´ and ´R´ fully defined), + - a [single-abstract-method (SAM) type](#sam-conversion); + - `PartialFunction[´T´, ´U´]` + - some other type. + +The standard anonymous function evaluates in the same way as the following instance creation expression: + +```scala +new scala.Function´n´[´T_1 , \ldots , T_n´, ´T´] { + def apply(´x_1´: ´T_1 , \ldots , x_n´: ´T_n´): ´T´ = ´e´ +} +``` + +The same evaluation holds for a SAM type, except that the instantiated type is given by the SAM type, and the implemented method is the single abstract method member of this type. + +The underlying platform may provide more efficient ways of constructing these instances, such as Java 8's `invokedynamic` bytecode and `LambdaMetaFactory` class. + +When a `PartialFunction` is required, an additional member `isDefinedAt` +is synthesized, which simply returns `true`. +However, if the function literal has the shape `x => x match { $\ldots$ }`, +then `isDefinedAt` is derived from the pattern match in the following way: +each case from the match expression evaluates to `true`, +and if there is no default case, +a default case is added that evaluates to `false`. +For more details on how that is implemented see +["Pattern Matching Anonymous Functions"](08-pattern-matching.html#pattern-matching-anonymous-functions). + +###### Example +Examples of anonymous functions: + +```scala +x => x // The identity function + +f => g => x => f(g(x)) // Curried function composition + +(x: Int,y: Int) => x + y // A summation function + +() => { count += 1; count } // The function which takes an + // empty parameter list ´()´, + // increments a non-local variable + // `count' and returns the new value. + +_ => 5 // The function that ignores its argument + // and always returns 5. +``` + +### Placeholder Syntax for Anonymous Functions + +```ebnf +SimpleExpr1 ::= ‘_’ +``` + +An expression (of syntactic category `Expr`) +may contain embedded underscore symbols `_` at places where identifiers +are legal. Such an expression represents an anonymous function where subsequent +occurrences of underscores denote successive parameters. + +Define an _underscore section_ to be an expression of the form +`_:´T´` where ´T´ is a type, or else of the form `_`, +provided the underscore does not appear as the expression part of a +type ascription `_:´T´`. + +An expression ´e´ of syntactic category `Expr` _binds_ an underscore section +´u´, if the following two conditions hold: (1) ´e´ properly contains ´u´, and +(2) there is no other expression of syntactic category `Expr` +which is properly contained in ´e´ and which itself properly contains ´u´. + +If an expression ´e´ binds underscore sections ´u_1 , \ldots , u_n´, in this order, it is equivalent to +the anonymous function `(´u'_1´, ... ´u'_n´) => ´e'´` +where each ´u_i'´ results from ´u_i´ by replacing the underscore with a fresh identifier and +´e'´ results from ´e´ by replacing each underscore section ´u_i´ by ´u_i'´. + +###### Example +The anonymous functions in the left column use placeholder +syntax. Each of these is equivalent to the anonymous function on its right. + +| | | +|---------------------------|----------------------------| +|`_ + 1` | `x => x + 1` | +|`_ * _` | `(x1, x2) => x1 * x2` | +|`(_: Int) * 2` | `(x: Int) => (x: Int) * 2` | +|`if (_) x else y` | `z => if (z) x else y` | +|`_.map(f)` | `x => x.map(f)` | +|`_.map(_ + 1)` | `x => x.map(y => y + 1)` | + +## Constant Expressions + +Constant expressions are expressions that the Scala compiler can evaluate to a constant. +The definition of "constant expression" depends on the platform, but they +include at least the expressions of the following forms: + +- A literal of a value class, such as an integer +- A string literal +- A class constructed with [`Predef.classOf`](12-the-scala-standard-library.html#the-predef-object) +- An element of an enumeration from the underlying platform +- A literal array, of the form `Array´(c_1 , \ldots , c_n)´`, + where all of the ´c_i´'s are themselves constant expressions +- An identifier defined by a [constant value definition](04-basic-declarations-and-definitions.html#value-declarations-and-definitions). + +## Statements + +```ebnf +BlockStat ::= Import + | {Annotation} [‘implicit’] [‘lazy’] Def + | {Annotation} {LocalModifier} TmplDef + | Expr1 + | +TemplateStat ::= Import + | {Annotation} {Modifier} Def + | {Annotation} {Modifier} Dcl + | Expr + | +``` + +Statements occur as parts of blocks and templates. A _statement_ can be +an import, a definition or an expression, or it can be empty. +Statements used in the template of a class definition can also be +declarations. An expression that is used as a statement can have an +arbitrary value type. An expression statement ´e´ is evaluated by +evaluating ´e´ and discarding the result of the evaluation. + + + +Block statements may be definitions which bind local names in the +block. The only modifier allowed in all block-local definitions is +`implicit`. When prefixing a class or object definition, +modifiers `abstract`, `final`, and `sealed` are also +permitted. + +Evaluation of a statement sequence entails evaluation of the +statements in the order they are written. + +## Implicit Conversions + +Implicit conversions can be applied to expressions whose type does not +match their expected type, to qualifiers in selections, and to unapplied methods. The +available implicit conversions are given in the next two sub-sections. + +### Value Conversions + +The following seven implicit conversions can be applied to an +expression ´e´ which has some value type ´T´ and which is type-checked with +some expected type ´\mathit{pt}´. + +###### Static Overloading Resolution +If an expression denotes several possible members of a class, +[overloading resolution](#overloading-resolution) +is applied to pick a unique member. + +###### Type Instantiation +An expression ´e´ of polymorphic type + +```scala +[´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]´T´ +``` + +which does not appear as the function part of +a type application is converted to a type instance of ´T´ +by determining with [local type inference](#local-type-inference) +instance types `´T_1 , \ldots , T_n´` +for the type variables `´a_1 , \ldots , a_n´` and +implicitly embedding ´e´ in the [type application](#type-applications) +`´e´[´T_1 , \ldots , T_n´]`. + +###### Numeric Widening +If ´e´ has a primitive number type which [weakly conforms](03-types.html#weak-conformance) +to the expected type, it is widened to +the expected type using one of the numeric conversion methods +`toShort`, `toChar`, `toInt`, `toLong`, +`toFloat`, `toDouble` defined [in the standard library](12-the-scala-standard-library.html#numeric-value-types). + +Since conversions from `Int` to `Float` and from `Long` to `Float` or `Double` +may incur a loss of precision, those implicit conversions are deprecated. +The conversion is permitted for literals if the original value can be recovered, +that is, if conversion back to the original type produces the original value. + +###### Numeric Literal Narrowing +If the expected type is `Byte`, `Short` or `Char`, and +the expression ´e´ is an integer literal fitting in the range of that +type, it is converted to the same literal in that type. + +###### Value Discarding +If ´e´ has some value type and the expected type is `Unit`, +´e´ is converted to the expected type by embedding it in the +term `{ ´e´; () }`. + +###### SAM conversion +An expression `(p1, ..., pN) => body` of function type `(T1, ..., TN) => T` is sam-convertible to the expected type `S` if the following holds: + - the class `C` of `S` declares an abstract method `m` with signature `(p1: A1, ..., pN: AN): R`; + - besides `m`, `C` must not declare or inherit any other deferred value members; + - the method `m` must have a single argument list; + - there must be a type `U` that is a subtype of `S`, so that the expression + `new U { final def m(p1: A1, ..., pN: AN): R = body }` is well-typed (conforming to the expected type `S`); + - for the purpose of scoping, `m` should be considered a static member (`U`'s members are not in scope in `body`); + - `(A1, ..., AN) => R` is a subtype of `(T1, ..., TN) => T` (satisfying this condition drives type inference of unknown type parameters in `S`); + +Note that a function literal that targets a SAM is not necessarily compiled to the above instance creation expression. This is platform-dependent. + +It follows that: + - if class `C` defines a constructor, it must be accessible and must define exactly one, empty, argument list; + - class `C` cannot be `final` or `sealed` (for simplicity we ignore the possibility of SAM conversion in the same compilation unit as the sealed class); + - `m` cannot be polymorphic; + - it must be possible to derive a fully-defined type `U` from `S` by inferring any unknown type parameters of `C`. + +Finally, we impose some implementation restrictions (these may be lifted in future releases): + - `C` must not be nested or local (it must not capture its environment, as that results in a nonzero-argument constructor) + - `C`'s constructor must not have an implicit argument list (this simplifies type inference); + - `C` must not declare a self type (this simplifies type inference); + - `C` must not be `@specialized`. + +###### View Application +If none of the previous conversions applies, and ´e´'s type +does not conform to the expected type ´\mathit{pt}´, it is attempted to convert +´e´ to the expected type with a [view](07-implicits.html#views). + +###### Selection on `Dynamic` +If none of the previous conversions applies, and ´e´ is a prefix +of a selection ´e.x´, and ´e´'s type conforms to class `scala.Dynamic`, +then the selection is rewritten according to the rules for +[dynamic member selection](#dynamic-member-selection). + +### Method Conversions + +The following four implicit conversions can be applied to methods +which are not applied to some argument list. + +###### Evaluation +A parameterless method ´m´ of type `=> ´T´` is always converted to +type ´T´ by evaluating the expression to which ´m´ is bound. + +###### Implicit Application +If the method takes only implicit parameters, implicit +arguments are passed following the rules [here](07-implicits.html#implicit-parameters). + +###### Eta Expansion +Otherwise, if the method is not a constructor, +and the expected type ´\mathit{pt}´ is a function type, or, +for methods of non-zero arity, a type [sam-convertible](#sam-conversion) to a function type, +´(\mathit{Ts}') \Rightarrow T'´, [eta-expansion](#eta-expansion-section) +is performed on the expression ´e´. + +(The exception for zero-arity methods is to avoid surprises due to unexpected sam conversion.) + +###### Empty Application +Otherwise, if ´e´ has method type ´()T´, it is implicitly applied to the empty +argument list, yielding ´e()´. + +### Overloading Resolution + +If an identifier or selection ´e´ references several members of a +class, the context of the reference is used to identify a unique +member. The way this is done depends on whether or not ´e´ is used as +a function. Let ´\mathscr{A}´ be the set of members referenced by ´e´. + +Assume first that ´e´ appears as a function in an application, as in +`´e´(´e_1 , \ldots , e_m´)`. + +One first determines the set of functions that is potentially [applicable](#function-applications) +based on the _shape_ of the arguments. + +The *shape* of an argument expression ´e´, written ´\mathit{shape}(e)´, is +a type that is defined as follows: + - For a function expression `(´p_1´: ´T_1 , \ldots , p_n´: ´T_n´) => ´b´: (Any ´, \ldots ,´ Any) => ´\mathit{shape}(b)´`, + where `Any` occurs ´n´ times in the argument type. + - For a pattern-matching anonymous function definition `{ case ... }`: `PartialFunction[Any, Nothing]`. + - For a named argument `´n´ = ´e´`: ´\mathit{shape}(e)´. + - For all other expressions: `Nothing`. + +Let ´\mathscr{B}´ be the set of alternatives in ´\mathscr{A}´ that are [_applicable_](#function-applications) +to expressions ´(e_1 , \ldots , e_n)´ of types ´(\mathit{shape}(e_1) , \ldots , \mathit{shape}(e_n))´. +If there is precisely one alternative in ´\mathscr{B}´, that alternative is chosen. + +Otherwise, let ´S_1 , \ldots , S_m´ be the list of types obtained by typing each argument as follows. + +Normally, an argument is typed without an expected type, except when +all alternatives explicitly specify the same parameter type for this argument (a missing parameter type, +due to e.g. arity differences, is taken as `NoType`, thus resorting to no expected type), +or when trying to propagate more type information to aid inference of higher-order function parameter types, as explained next. + +The intuition for higher-order function parameter type inference is that all arguments must be of a function-like type +(`PartialFunction`, `FunctionN` or some equivalent [SAM type](#sam-conversion)), +which in turn must define the same set of higher-order argument types, so that they can safely be used as +the expected type of a given argument of the overloaded method, without unduly ruling out any alternatives. +The intent is not to steer overloading resolution, but to preserve enough type information to steer type +inference of the arguments (a function literal or eta-expanded method) to this overloaded method. + +Note that the expected type drives eta-expansion (not performed unless a function-like type is expected), +as well as inference of omitted parameter types of function literals. + +More precisely, an argument `´e_i´` is typed with an expected type that is derived from the `´i´`th argument +type found in each alternative (call these `´T_{ij}´` for alternative `´j´` and argument position `´i´`) when +all `´T_{ij}´` are function types `´(A_{1j},..., A_{nj}) => ?´` (or the equivalent `PartialFunction`, or SAM) +of some arity `´n´`, and their argument types `´A_{kj}´` are identical across all overloads `´j´` for a +given `´k´`. Then, the expected type for `´e_i´` is derived as follows: + - we use `´PartialFunction[A_{1j},..., A_{nj}, ?]´` if for some overload `´j´`, `´T_{ij}´`'s type symbol is `PartialFunction`; + - else, if for some `´j´`, `´T_{ij}´` is `FunctionN`, the expected type is `´FunctionN[A_{1j},..., A_{nj}, ?]´`; + - else, if for all `´j´`, `´T_{ij}´` is a SAM type of the same class, defining argument types `´A_{1j},..., A_{nj}´` + (and a potentially varying result type), the expected type encodes these argument types and the SAM class. + +For every member ´m´ in ´\mathscr{B}´ one determines whether it is applicable +to expressions (´e_1 , \ldots , e_m´) of types ´S_1, \ldots , S_m´. + +It is an error if none of the members in ´\mathscr{B}´ is applicable. If there is one +single applicable alternative, that alternative is chosen. Otherwise, let ´\mathscr{CC}´ +be the set of applicable alternatives which don't employ any default argument +in the application to ´e_1 , \ldots , e_m´. + +It is again an error if ´\mathscr{CC}´ is empty. +Otherwise, one chooses the _most specific_ alternative among the alternatives +in ´\mathscr{CC}´, according to the following definition of being "as specific as", and +"more specific than": + + + +- A parameterized method ´m´ of type `(´p_1:T_1, \ldots , p_n:T_n´)´U´` is + _as specific as_ some other member ´m'´ of type ´S´ if ´m'´ is [applicable](#function-applications) + to arguments `(´p_1 , \ldots , p_n´)` of types ´T_1 , \ldots , T_n´. +- A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]´T´` is + as specific as some other member of type ´S´ if ´T´ is as specific as ´S´ + under the assumption that for ´i = 1 , \ldots , n´ each ´a_i´ is an abstract type name + bounded from below by ´L_i´ and from above by ´U_i´. +- A member of any other type is always as specific as a parameterized method or a polymorphic method. +- Given two members of types ´T´ and ´U´ which are neither parameterized nor polymorphic method types, + the member of type ´T´ is as specific as the member of type ´U´ if + the existential dual of ´T´ conforms to the existential dual of ´U´. + Here, the existential dual of a polymorphic type + `[´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]´T´` is + `´T´ forSome { type ´a_1´ >: ´L_1´ <: ´U_1´ ´, \ldots ,´ type ´a_n´ >: ´L_n´ <: ´U_n´}`. + The existential dual of every other type is the type itself. + +The _relative weight_ of an alternative ´A´ over an alternative ´B´ is a +number from 0 to 2, defined as the sum of + +- 1 if ´A´ is as specific as ´B´, 0 otherwise, and +- 1 if ´A´ is defined in a class or object which is derived from the class or object defining ´B´, 0 otherwise. + +A class or object ´C´ is _derived_ from a class or object ´D´ if one of +the following holds: + +- ´C´ is a subclass of ´D´, or +- ´C´ is a companion object of a class derived from ´D´, or +- ´D´ is a companion object of a class from which ´C´ is derived. + +An alternative ´A´ is _more specific_ than an alternative ´B´ if +the relative weight of ´A´ over ´B´ is greater than the relative +weight of ´B´ over ´A´. + +It is an error if there is no alternative in ´\mathscr{CC}´ which is more +specific than all other alternatives in ´\mathscr{CC}´. + +Assume next that ´e´ appears as a function in a type application, as +in `´e´[´\mathit{targs}\,´]`. Then all alternatives in +´\mathscr{A}´ which take the same number of type parameters as there are type +arguments in ´\mathit{targs}´ are chosen. It is an error if no such alternative exists. +If there are several such alternatives, overloading resolution is +applied again to the whole expression `´e´[´\mathit{targs}\,´]`. + +Assume finally that ´e´ does not appear as a function in either an application or a type application. +If an expected type is given, let ´\mathscr{B}´ be the set of those alternatives +in ´\mathscr{A}´ which are [compatible](03-types.html#compatibility) to it. +Otherwise, let ´\mathscr{B}´ be the same as ´\mathscr{A}´. +In this last case we choose the most specific alternative among all alternatives in ´\mathscr{B}´. +It is an error if there is no alternative in ´\mathscr{B}´ which is +more specific than all other alternatives in ´\mathscr{B}´. + +###### Example +Consider the following definitions: + +```scala +class A extends B {} +def f(x: B, y: B) = ´\ldots´ +def f(x: A, y: B) = ´\ldots´ +val a: A +val b: B +``` + +Then the application `f(b, b)` refers to the first +definition of ´f´ whereas the application `f(a, a)` +refers to the second. Assume now we add a third overloaded definition + +```scala +def f(x: B, y: A) = ´\ldots´ +``` + +Then the application `f(a, a)` is rejected for being ambiguous, since +no most specific applicable signature exists. + +### Local Type Inference + +Local type inference infers type arguments to be passed to expressions +of polymorphic type. Say ´e´ is of type [´a_1´ >: ´L_1´ <: ´U_1, \ldots , a_n´ >: ´L_n´ <: ´U_n´]´T´ +and no explicit type parameters are given. + +Local type inference converts this expression to a type +application `´e´[´T_1 , \ldots , T_n´]`. The choice of the +type arguments ´T_1 , \ldots , T_n´ depends on the context in which +the expression appears and on the expected type ´\mathit{pt}´. +There are three cases. + +###### Case 1: Selections +If the expression appears as the prefix of a selection with a name +´x´, then type inference is _deferred_ to the whole expression +´e.x´. That is, if ´e.x´ has type ´S´, it is now treated as having +type [´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]´S´, +and local type inference is applied in turn to infer type arguments +for ´a_1 , \ldots , a_n´, using the context in which ´e.x´ appears. + +###### Case 2: Values +If the expression ´e´ appears as a value without being applied to +value arguments, the type arguments are inferred by solving a +constraint system which relates the expression's type ´T´ with the +expected type ´\mathit{pt}´. Without loss of generality we can assume that +´T´ is a value type; if it is a method type we apply +[eta-expansion](#eta-expansion-section) to convert it to a function type. Solving +means finding a substitution ´\sigma´ of types ´T_i´ for the type +parameters ´a_i´ such that + +- None of the inferred types ´T_i´ is a [singleton type](03-types.html#singleton-types) + unless it is a singleton type corresponding to an object or a constant value + definition or the corresponding bound ´U_i´ is a subtype of `scala.Singleton`. +- All type parameter bounds are respected, i.e. + ´\sigma L_i <: \sigma a_i´ and ´\sigma a_i <: \sigma U_i´ for ´i = 1 , \ldots , n´. +- The expression's type conforms to the expected type, i.e. + ´\sigma T <: \sigma \mathit{pt}´. + +It is a compile time error if no such substitution exists. +If several substitutions exist, local-type inference will choose for +each type variable ´a_i´ a minimal or maximal type ´T_i´ of the +solution space. A _maximal_ type ´T_i´ will be chosen if the type +parameter ´a_i´ appears [contravariantly](04-basic-declarations-and-definitions.html#variance-annotations) in the +type ´T´ of the expression. A _minimal_ type ´T_i´ will be chosen +in all other situations, i.e. if the variable appears covariantly, +non-variantly or not at all in the type ´T´. We call such a substitution +an _optimal solution_ of the given constraint system for the type ´T´. + +###### Case 3: Methods +The last case applies if the expression +´e´ appears in an application ´e(d_1 , \ldots , d_m)´. In that case +´T´ is a method type ´(p_1:R_1 , \ldots , p_m:R_m)T'´. Without loss of +generality we can assume that the result type ´T'´ is a value type; if +it is a method type we apply [eta-expansion](#eta-expansion-section) to +convert it to a function type. One computes first the types ´S_j´ of +the argument expressions ´d_j´, using two alternative schemes. Each +argument expression ´d_j´ is typed first with the expected type ´R_j´, +in which the type parameters ´a_1 , \ldots , a_n´ are taken as type +constants. If this fails, the argument ´d_j´ is typed instead with an +expected type ´R_j'´ which results from ´R_j´ by replacing every type +parameter in ´a_1 , \ldots , a_n´ with _undefined_. + +In a second step, type arguments are inferred by solving a constraint +system which relates the method's type with the expected type +´\mathit{pt}´ and the argument types ´S_1 , \ldots , S_m´. Solving the +constraint system means +finding a substitution ´\sigma´ of types ´T_i´ for the type parameters +´a_i´ such that + +- None of the inferred types ´T_i´ is a [singleton type](03-types.html#singleton-types) + unless it is a singleton type corresponding to an object or a constant value + definition or the corresponding bound ´U_i´ is a subtype of `scala.Singleton`. +- All type parameter bounds are respected, i.e. ´\sigma L_i <: \sigma a_i´ and + ´\sigma a_i <: \sigma U_i´ for ´i = 1 , \ldots , n´. +- The method's result type ´T'´ conforms to the expected type, i.e. ´\sigma T' <: \sigma \mathit{pt}´. +- Each argument type [weakly conforms](03-types.html#weak-conformance) + to the corresponding formal parameter + type, i.e. ´\sigma S_j <:_w \sigma R_j´ for ´j = 1 , \ldots , m´. + +It is a compile time error if no such substitution exists. If several +solutions exist, an optimal one for the type ´T'´ is chosen. + +All or parts of an expected type ´\mathit{pt}´ may be undefined. The rules for +[conformance](03-types.html#conformance) are extended to this case by adding +the rule that for any type ´T´ the following two statements are always +true: ´\mathit{undefined} <: T´ and ´T <: \mathit{undefined}´ + +It is possible that no minimal or maximal solution for a type variable +exists, in which case a compile-time error results. Because ´<:´ is a +pre-order, it is also possible that a solution set has several optimal +solutions for a type. In that case, a Scala compiler is free to pick +any one of them. + +###### Example +Consider the two methods: + +```scala +def cons[A](x: A, xs: List[A]): List[A] = x :: xs +def nil[B]: List[B] = Nil +``` + +and the definition + +```scala +val xs = cons(1, nil) +``` + +The application of `cons` is typed with an undefined expected +type. This application is completed by local type inference to +`cons[Int](1, nil)`. +Here, one uses the following +reasoning to infer the type argument `Int` for the type +parameter `a`: + +First, the argument expressions are typed. The first argument `1` +has type `Int` whereas the second argument `nil` is +itself polymorphic. One tries to type-check `nil` with an +expected type `List[a]`. This leads to the constraint system + +```scala +List[b?] <: List[a] +``` + +where we have labeled `b?` with a question mark to indicate +that it is a variable in the constraint system. +Because class `List` is covariant, the optimal +solution of this constraint is + +```scala +b = scala.Nothing +``` + +In a second step, one solves the following constraint system for +the type parameter `a` of `cons`: + +```scala +Int <: a? +List[scala.Nothing] <: List[a?] +List[a?] <: ´\mathit{undefined}´ +``` + +The optimal solution of this constraint system is + +```scala +a = Int +``` + +so `Int` is the type inferred for `a`. + +###### Example + +Consider now the definition + +```scala +val ys = cons("abc", xs) +``` + +where `xs` is defined of type `List[Int]` as before. +In this case local type inference proceeds as follows. + +First, the argument expressions are typed. The first argument +`"abc"` has type `String`. The second argument `xs` is +first tried to be typed with expected type `List[a]`. This fails, +as `List[Int]` is not a subtype of `List[a]`. Therefore, +the second strategy is tried; `xs` is now typed with expected type +`List[´\mathit{undefined}´]`. This succeeds and yields the argument type +`List[Int]`. + +In a second step, one solves the following constraint system for +the type parameter `a` of `cons`: + +```scala +String <: a? +List[Int] <: List[a?] +List[a?] <: ´\mathit{undefined}´ +``` + +The optimal solution of this constraint system is + +```scala +a = scala.Any +``` + +so `scala.Any` is the type inferred for `a`. + +### Eta Expansion + +_Eta-expansion_ converts an expression of method type to an +equivalent expression of function type. It proceeds in two steps. + +First, one identifies the maximal sub-expressions of ´e´; let's +say these are ´e_1 , \ldots , e_m´. For each of these, one creates a +fresh name ´x_i´. Let ´e'´ be the expression resulting from +replacing every maximal subexpression ´e_i´ in ´e´ by the +corresponding fresh name ´x_i´. Second, one creates a fresh name ´y_i´ +for every argument type ´T_i´ of the method (´i = 1 , \ldots , +n´). The result of eta-conversion is then: + +```scala +{ val ´x_1´ = ´e_1´; + ´\ldots´ + val ´x_m´ = ´e_m´; + (´y_1: T_1 , \ldots , y_n: T_n´) => ´e'´(´y_1 , \ldots , y_n´) +} +``` + +The behavior of [call-by-name parameters](#function-applications) +is preserved under eta-expansion: the corresponding actual argument expression, +a sub-expression of parameterless method type, is not evaluated in the expanded block. + +### Dynamic Member Selection + +The standard Scala library defines a marker trait `scala.Dynamic`. Subclasses of this trait are able to intercept selections and applications on their instances by defining methods of the names `applyDynamic`, `applyDynamicNamed`, `selectDynamic`, and `updateDynamic`. + +The following rewrites are performed, assuming ´e´'s type conforms to `scala.Dynamic`, and the original expression does not type check under the normal rules, as specified fully in the relevant subsection of [implicit conversion](#dynamic-member-selection): + + * `e.m[Ti](xi)` becomes `e.applyDynamic[Ti]("m")(xi)` + * `e.m[Ti]` becomes `e.selectDynamic[Ti]("m")` + * `e.m = x` becomes `e.updateDynamic("m")(x)` + +If any arguments are named in the application (one of the `xi` is of the shape `arg = x`), their name is preserved as the first component of the pair passed to `applyDynamicNamed` (for missing names, `""` is used): + + * `e.m[Ti](argi = xi)` becomes `e.applyDynamicNamed[Ti]("m")(("argi", xi))` + +Finally: + + * `e.m(x) = y` becomes `e.selectDynamic("m").update(x, y)` + +None of these methods are actually defined in the `scala.Dynamic`, so that users are free to define them with or without type parameters, or implicit arguments. diff --git a/docs/_spec/07-implicits.md b/docs/_spec/07-implicits.md new file mode 100644 index 000000000000..6f38fdd48074 --- /dev/null +++ b/docs/_spec/07-implicits.md @@ -0,0 +1,525 @@ +--- +title: Implicits +layout: default +chapter: 7 +--- + +# Implicits + +## The Implicit Modifier + +```ebnf +LocalModifier ::= ‘implicit’ +ParamClauses ::= {ParamClause} [nl] ‘(’ ‘implicit’ Params ‘)’ +``` + +Template members and parameters labeled with an `implicit` +modifier can be passed to [implicit parameters](#implicit-parameters) +and can be used as implicit conversions called [views](#views). +The `implicit` modifier is illegal for all +type members, as well as for [top-level objects](09-top-level-definitions.html#packagings). + +###### Example Monoid + +The following code defines an abstract class of monoids and +two concrete implementations, `StringMonoid` and +`IntMonoid`. The two implementations are marked implicit. + +```scala +abstract class Monoid[A] extends SemiGroup[A] { + def unit: A + def add(x: A, y: A): A +} +object Monoids { + implicit object stringMonoid extends Monoid[String] { + def add(x: String, y: String): String = x.concat(y) + def unit: String = "" + } + implicit object intMonoid extends Monoid[Int] { + def add(x: Int, y: Int): Int = x + y + def unit: Int = 0 + } +} +``` + +## Implicit Parameters + +An _implicit parameter list_ +`(implicit ´p_1´,´\ldots´,´p_n´)` of a method marks the parameters ´p_1 , \ldots , p_n´ as +implicit. A method or constructor can have only one implicit parameter +list, and it must be the last parameter list given. + +A method with implicit parameters can be applied to arguments just +like a normal method. In this case the `implicit` label has no +effect. However, if such a method misses arguments for its implicit +parameters, such arguments will be automatically provided. + +The actual arguments that are eligible to be passed to an implicit +parameter of type ´T´ fall into two categories. First, eligible are +all identifiers ´x´ that can be accessed at the point of the method +call without a prefix and that denote an +[implicit definition](#the-implicit-modifier) +or an implicit parameter. To be accessible without a prefix, an identifier +must be a local name, a member of an enclosing template or a name introduced by an +[import clause](04-basic-declarations-and-definitions.html#import-clauses). If there are no eligible +identifiers under this rule, then, second, eligible are also all +`implicit` members of some object that belongs to the implicit +scope of the implicit parameter's type, ´T´. + +The _implicit scope_ of a type ´T´ consists of all [companion modules](05-classes-and-objects.html#object-definitions) of classes that are associated with the implicit parameter's type. +Here, we say a class ´C´ is _associated_ with a type ´T´ if it is a [base class](05-classes-and-objects.html#class-linearization) of some part of ´T´. + +The _parts_ of a type ´T´ are: + +- if ´T´ is a compound type `´T_1´ with ´\ldots´ with ´T_n´`, + the union of the parts of ´T_1 , \ldots , T_n´, as well as ´T´ itself; +- if ´T´ is a parameterized type `´S´[´T_1 , \ldots , T_n´]`, + the union of the parts of ´S´ and ´T_1 , \ldots , T_n´; +- if ´T´ is a singleton type `´p´.type`, + the parts of the type of ´p´; +- if ´T´ is a type projection `´S´#´U´`, + the parts of ´S´ as well as ´T´ itself; +- if ´T´ is a type alias, the parts of its expansion; +- if ´T´ is an abstract type, the parts of its upper bound; +- if ´T´ denotes an implicit conversion to a type with a method with argument types ´T_1 , \ldots , T_n´ and result type ´U´, + the union of the parts of ´T_1 , \ldots , T_n´ and ´U´; +- the parts of quantified (existential or universal) and annotated types are defined as the parts of the underlying types (e.g., the parts of `T forSome { ... }` are the parts of `T`); +- in all other cases, just ´T´ itself. + +Note that packages are internally represented as classes with companion modules to hold the package members. +Thus, implicits defined in a package object are part of the implicit scope of a type prefixed by that package. + +If there are several eligible arguments which match the implicit +parameter's type, a most specific one will be chosen using the rules +of static [overloading resolution](06-expressions.html#overloading-resolution). +If the parameter has a default argument and no implicit argument can +be found the default argument is used. + +###### Example +Assuming the classes from the [`Monoid` example](#example-monoid), here is a +method which computes the sum of a list of elements using the +monoid's `add` and `unit` operations. + +```scala +def sum[A](xs: List[A])(implicit m: Monoid[A]): A = + if (xs.isEmpty) m.unit + else m.add(xs.head, sum(xs.tail)) +``` + +The monoid in question is marked as an implicit parameter, and can therefore +be inferred based on the type of the list. +Consider for instance the call `sum(List(1, 2, 3))` +in a context where `stringMonoid` and `intMonoid` +are visible. We know that the formal type parameter `a` of +`sum` needs to be instantiated to `Int`. The only +eligible object which matches the implicit formal parameter type +`Monoid[Int]` is `intMonoid` so this object will +be passed as implicit parameter. + +This discussion also shows that implicit parameters are inferred after +any type arguments are [inferred](06-expressions.html#local-type-inference). + +Implicit methods can themselves have implicit parameters. An example +is the following method from module `scala.List`, which injects +lists into the `scala.Ordered` class, provided the element +type of the list is also convertible to this type. + +```scala +implicit def list2ordered[A](x: List[A]) + (implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] = + ... +``` + +Assume in addition a method + +```scala +implicit def int2ordered(x: Int): Ordered[Int] +``` + +that injects integers into the `Ordered` class. We can now +define a `sort` method over ordered lists: + +```scala +def sort[A](xs: List[A])(implicit a2ordered: A => Ordered[A]) = ... +``` + +We can apply `sort` to a list of lists of integers +`yss: List[List[Int]]` +as follows: + +```scala +sort(yss) +``` + +The call above will be completed by passing two nested implicit arguments: + +```scala +sort(yss)((xs: List[Int]) => list2ordered[Int](xs)(int2ordered)) +``` + +The possibility of passing implicit arguments to implicit arguments +raises the possibility of an infinite recursion. For instance, one +might try to define the following method, which injects _every_ type into the +`Ordered` class: + +```scala +implicit def magic[A](x: A)(implicit a2ordered: A => Ordered[A]): Ordered[A] = + a2ordered(x) +``` + +Now, if one tried to apply +`sort` to an argument `arg` of a type that did not have +another injection into the `Ordered` class, one would obtain an infinite +expansion: + +```scala +sort(arg)(x => magic(x)(x => magic(x)(x => ... ))) +``` + +Such infinite expansions should be detected and reported as errors, however to support the deliberate +implicit construction of recursive values we allow implicit arguments to be marked as by-name. At call +sites recursive uses of implicit values are permitted if they occur in an implicit by-name argument. + +Consider the following example, + +```scala +trait Foo { + def next: Foo +} + +object Foo { + implicit def foo(implicit rec: Foo): Foo = + new Foo { def next = rec } +} + +val foo = implicitly[Foo] +assert(foo eq foo.next) +``` + +As with the `magic` case above this diverges due to the recursive implicit argument `rec` of method +`foo`. If we mark the implicit argument as by-name, + +```scala +trait Foo { + def next: Foo +} + +object Foo { + implicit def foo(implicit rec: => Foo): Foo = + new Foo { def next = rec } +} + +val foo = implicitly[Foo] +assert(foo eq foo.next) +``` + +the example compiles with the assertion successful. + +When compiled, recursive by-name implicit arguments of this sort are extracted out as val members of a +local synthetic object at call sites as follows, + +```scala +val foo: Foo = scala.Predef.implicitly[Foo]( + { + object LazyDefns$1 { + val rec$1: Foo = Foo.foo(rec$1) + // ^^^^^ + // recursive knot tied here + } + LazyDefns$1.rec$1 + } +) +assert(foo eq foo.next) +``` + +Note that the recursive use of `rec$1` occurs within the by-name argument of `foo` and is consequently +deferred. The desugaring matches what a programmer would do to construct such a recursive value +explicitly. + +To prevent infinite expansions, such as the `magic` example above, the compiler keeps track of a stack +of “open implicit types” for which implicit arguments are currently being searched. Whenever an +implicit argument for type ´T´ is searched, ´T´ is added to the stack paired with the implicit +definition which produces it, and whether it was required to satisfy a by-name implicit argument or +not. The type is removed from the stack once the search for the implicit argument either definitely +fails or succeeds. Everytime a type is about to be added to the stack, it is checked against +existing entries which were produced by the same implicit definition and then, + ++ if it is equivalent to some type which is already on the stack and there is a by-name argument between + that entry and the top of the stack. In this case the search for that type succeeds immediately and + the implicit argument is compiled as a recursive reference to the found argument. That argument is + added as an entry in the synthesized implicit dictionary if it has not already been added. ++ otherwise if the _core_ of the type _dominates_ the core of a type already on the stack, then the + implicit expansion is said to _diverge_ and the search for that type fails immediately. ++ otherwise it is added to the stack paired with the implicit definition which produces it. + Implicit resolution continues with the implicit arguments of that definition (if any). + +Here, the _core type_ of ´T´ is ´T´ with aliases expanded, +top-level type [annotations](11-annotations.html#user-defined-annotations) and +[refinements](03-types.html#compound-types) removed, and occurrences of top-level existentially bound +variables replaced by their upper bounds. + +A core type ´T´ _dominates_ a type ´U´ if ´T´ is [equivalent](03-types.html#equivalence) to ´U´, +or if the top-level type constructors of ´T´ and ´U´ have a common element and ´T´ is more complex +than ´U´ and the _covering sets_ of ´T´ and ´U´ are equal. + +The set of _top-level type constructors_ ´\mathit{ttcs}(T)´ of a type ´T´ depends on the form of +the type: + +- For a type designator, ´\mathit{ttcs}(p.c) ~=~ \{c\}´; +- For a parameterized type, ´\mathit{ttcs}(p.c[\mathit{targs}]) ~=~ \{c\}´; +- For a singleton type, ´\mathit{ttcs}(p.type) ~=~ \mathit{ttcs}(T)´, provided ´p´ has type ´T´; +- For a compound type, `´\mathit{ttcs}(T_1´ with ´\ldots´ with ´T_n)´` ´~=~ \mathit{ttcs}(T_1) \cup \ldots \cup \mathit{ttcs}(T_n)´. + +The _complexity_ ´\operatorname{complexity}(T)´ of a core type is an integer which also depends on the form of +the type: + +- For a type designator, ´\operatorname{complexity}(p.c) ~=~ 1 + \operatorname{complexity}(p)´ +- For a parameterized type, ´\operatorname{complexity}(p.c[\mathit{targs}]) ~=~ 1 + \Sigma \operatorname{complexity}(\mathit{targs})´ +- For a singleton type denoting a package ´p´, ´\operatorname{complexity}(p.type) ~=~ 0´ +- For any other singleton type, ´\operatorname{complexity}(p.type) ~=~ 1 + \operatorname{complexity}(T)´, provided ´p´ has type ´T´; +- For a compound type, `´\operatorname{complexity}(T_1´ with ´\ldots´ with ´T_n)´` ´= \Sigma\operatorname{complexity}(T_i)´ + +The _covering set_ ´\mathit{cs}(T)´ of a type ´T´ is the set of type designators mentioned in a type. +For example, given the following, + +```scala +type A = List[(Int, Int)] +type B = List[(Int, (Int, Int))] +type C = List[(Int, String)] +``` + +the corresponding covering sets are: + +- ´\mathit{cs}(A)´: List, Tuple2, Int +- ´\mathit{cs}(B)´: List, Tuple2, Int +- ´\mathit{cs}(C)´: List, Tuple2, Int, String + +###### Example +When typing `sort(xs)` for some list `xs` of type `List[List[List[Int]]]`, +the sequence of types for +which implicit arguments are searched is + +```scala +List[List[Int]] => Ordered[List[List[Int]]], +List[Int] => Ordered[List[Int]], +Int => Ordered[Int] +``` + +All types share the common type constructor `scala.Function1`, +but the complexity of each new type is lower than the complexity of the previous types. +Hence, the code typechecks. + +###### Example +Let `ys` be a list of some type which cannot be converted +to `Ordered`. For instance: + +```scala +val ys = List(new IllegalArgumentException, new ClassCastException, new Error) +``` + +Assume that the definition of `magic` above is in scope. Then the sequence +of types for which implicit arguments are searched is + +```scala +Throwable => Ordered[Throwable], +Throwable => Ordered[Throwable], +... +``` + +Since the second type in the sequence is equal to the first, the compiler +will issue an error signalling a divergent implicit expansion. + +## Views + +Implicit parameters and methods can also define implicit conversions +called views. A _view_ from type ´S´ to type ´T´ is +defined by an implicit value which has function type +`´S´ => ´T´` or `(=> ´S´) => ´T´` or by a method convertible to a value of that +type. + +Views are applied in three situations: + +1. If an expression ´e´ is of type ´T´, and ´T´ does not conform to the + expression's expected type ´\mathit{pt}´. In this case an implicit ´v´ is + searched which is applicable to ´e´ and whose result type conforms to + ´\mathit{pt}´. The search proceeds as in the case of implicit parameters, + where the implicit scope is the one of `´T´ => ´\mathit{pt}´`. If + such a view is found, the expression ´e´ is converted to + `´v´(´e´)`. +1. In a selection ´e.m´ with ´e´ of type ´T´, if the selector ´m´ does + not denote an accessible member of ´T´. In this case, a view ´v´ is searched + which is applicable to ´e´ and whose result contains a member named + ´m´. The search proceeds as in the case of implicit parameters, where + the implicit scope is the one of ´T´. If such a view is found, the + selection ´e.m´ is converted to `´v´(´e´).´m´`. +1. In a selection ´e.m(\mathit{args})´ with ´e´ of type ´T´, if the selector + ´m´ denotes some member(s) of ´T´, but none of these members is applicable to the arguments + ´\mathit{args}´. In this case a view ´v´ is searched which is applicable to ´e´ + and whose result contains a method ´m´ which is applicable to ´\mathit{args}´. + The search proceeds as in the case of implicit parameters, where + the implicit scope is the one of ´T´. If such a view is found, the + selection ´e.m´ is converted to `´v´(´e´).´m(\mathit{args})´`. + +The implicit view, if it is found, can accept its argument ´e´ as a +call-by-value or as a call-by-name parameter. However, call-by-value +implicits take precedence over call-by-name implicits. + +As for implicit parameters, overloading resolution is applied +if there are several possible candidates (of either the call-by-value +or the call-by-name category). + +###### Example Ordered + +Class `scala.Ordered[A]` contains a method + +```scala + def <= [B >: A](that: B)(implicit b2ordered: B => Ordered[B]): Boolean +``` + +Assume two lists `xs` and `ys` of type `List[Int]` +and assume that the `list2ordered` and `int2ordered` +methods defined [here](#implicit-parameters) are in scope. +Then the operation + +```scala + xs <= ys +``` + +is legal, and is expanded to: + +```scala + list2ordered(xs)(int2ordered).<= + (ys) + (xs => list2ordered(xs)(int2ordered)) +``` + +The first application of `list2ordered` converts the list +`xs` to an instance of class `Ordered`, whereas the second +occurrence is part of an implicit parameter passed to the `<=` +method. + +## Context Bounds and View Bounds + +```ebnf + TypeParam ::= (id | ‘_’) [TypeParamClause] [‘>:’ Type] [‘<:’ Type] + {‘<%’ Type} {‘:’ Type} +``` + +A type parameter ´A´ of a method or non-trait class may have one or more view +bounds `´A´ <% ´T´`. In this case the type parameter may be +instantiated to any type ´S´ which is convertible by application of a +view to the bound ´T´. + +A type parameter ´A´ of a method or non-trait class may also have one +or more context bounds `´A´ : ´T´`. In this case the type parameter may be +instantiated to any type ´S´ for which _evidence_ exists at the +instantiation point that ´S´ satisfies the bound ´T´. Such evidence +consists of an implicit value with type ´T[S]´. + +A method or class containing type parameters with view or context bounds is treated as being +equivalent to a method with implicit parameters. Consider first the case of a +single parameter with view and/or context bounds such as: + +```scala +def ´f´[´A´ <% ´T_1´ ... <% ´T_m´ : ´U_1´ : ´U_n´](´\mathit{ps}´): ´R´ = ... +``` + +Then the method definition above is expanded to + +```scala +def ´f´[´A´](´\mathit{ps}´)(implicit ´v_1´: ´A´ => ´T_1´, ..., ´v_m´: ´A´ => ´T_m´, + ´w_1´: ´U_1´[´A´], ..., ´w_n´: ´U_n´[´A´]): ´R´ = ... +``` + +where the ´v_i´ and ´w_j´ are fresh names for the newly introduced implicit parameters. These +parameters are called _evidence parameters_. + +If a class or method has several view- or context-bounded type parameters, each +such type parameter is expanded into evidence parameters in the order +they appear and all the resulting evidence parameters are concatenated +in one implicit parameter section. Since traits do not take +constructor parameters, this translation does not work for them. +Consequently, type-parameters in traits may not be view- or context-bounded. + +Evidence parameters are prepended to the existing implicit parameter section, if one exists. + +For example: + +```scala +def foo[A: M](implicit b: B): C +// expands to: +// def foo[A](implicit evidence´1: M[A], b: B): C +``` + +###### Example +The `<=` method from the [`Ordered` example](#example-ordered) can be declared +more concisely as follows: + +```scala +def <= [B >: A <% Ordered[B]](that: B): Boolean +``` + +## Manifests + +Manifests are type descriptors that can be automatically generated by +the Scala compiler as arguments to implicit parameters. The Scala +standard library contains a hierarchy of four manifest classes, +with `OptManifest` +at the top. Their signatures follow the outline below. + +```scala +trait OptManifest[+T] +object NoManifest extends OptManifest[Nothing] +trait ClassManifest[T] extends OptManifest[T] +trait Manifest[T] extends ClassManifest[T] +``` + +If an implicit parameter of a method or constructor is of a subtype ´M[T]´ of +class `OptManifest[T]`, _a manifest is determined for ´M[S]´_, +according to the following rules. + +First if there is already an implicit argument that matches ´M[T]´, this +argument is selected. + +Otherwise, let ´\mathit{Mobj}´ be the companion object `scala.reflect.Manifest` +if ´M´ is trait `Manifest`, or be +the companion object `scala.reflect.ClassManifest` otherwise. Let ´M'´ be the trait +`Manifest` if ´M´ is trait `Manifest`, or be the trait `OptManifest` otherwise. +Then the following rules apply. + +1. If ´T´ is a value class or one of the classes `Any`, `AnyVal`, `Object`, + `Null`, or `Nothing`, + a manifest for it is generated by selecting + the corresponding manifest value `Manifest.´T´`, which exists in the + `Manifest` module. +1. If ´T´ is an instance of `Array[´S´]`, a manifest is generated + with the invocation `´\mathit{Mobj}´.arrayType[S](m)`, where ´m´ is the manifest + determined for ´M[S]´. +1. If ´T´ is some other class type ´S´#´C[U_1, \ldots, U_n]´ where the prefix + type ´S´ cannot be statically determined from the class ´C´, + a manifest is generated with the invocation `´\mathit{Mobj}´.classType[T](´m_0´, classOf[T], ´ms´)` + where ´m_0´ is the manifest determined for ´M'[S]´ and ´ms´ are the + manifests determined for ´M'[U_1], \ldots, M'[U_n]´. +1. If ´T´ is some other class type with type arguments ´U_1 , \ldots , U_n´, + a manifest is generated + with the invocation `´\mathit{Mobj}´.classType[T](classOf[T], ´ms´)` + where ´ms´ are the + manifests determined for ´M'[U_1] , \ldots , M'[U_n]´. +1. If ´T´ is a singleton type `´p´.type`, a manifest is generated with + the invocation `´\mathit{Mobj}´.singleType[T](´p´)` +1. If ´T´ is a refined type ´T' \{ R \}´, a manifest is generated for ´T'´. + (That is, refinements are never reflected in manifests). +1. If ´T´ is an intersection type + `´T_1´ with ´, \ldots ,´ with ´T_n´` + where ´n > 1´, the result depends on whether a full manifest is + to be determined or not. + If ´M´ is trait `Manifest`, then + a manifest is generated with the invocation + `Manifest.intersectionType[T](´ms´)` where ´ms´ are the manifests + determined for ´M[T_1] , \ldots , M[T_n]´. + Otherwise, if ´M´ is trait `ClassManifest`, + then a manifest is generated for the [intersection dominator](03-types.html#type-erasure) + of the types ´T_1 , \ldots , T_n´. +1. If ´T´ is some other type, then if ´M´ is trait `OptManifest`, + a manifest is generated from the designator `scala.reflect.NoManifest`. + If ´M´ is a type different from `OptManifest`, a static error results. diff --git a/docs/_spec/08-pattern-matching.md b/docs/_spec/08-pattern-matching.md new file mode 100644 index 000000000000..7607b0db85e0 --- /dev/null +++ b/docs/_spec/08-pattern-matching.md @@ -0,0 +1,817 @@ +--- +title: Pattern Matching +layout: default +chapter: 8 +--- + +# Pattern Matching + +## Patterns + +```ebnf + Pattern ::= Pattern1 { ‘|’ Pattern1 } + Pattern1 ::= boundvarid ‘:’ TypePat + | ‘_’ ‘:’ TypePat + | Pattern2 + Pattern2 ::= id [‘@’ Pattern3] + | Pattern3 + Pattern3 ::= SimplePattern + | SimplePattern {id [nl] SimplePattern} + SimplePattern ::= ‘_’ + | varid + | Literal + | StableId + | StableId ‘(’ [Patterns] ‘)’ + | StableId ‘(’ [Patterns ‘,’] [id ‘@’] ‘_’ ‘*’ ‘)’ + | ‘(’ [Patterns] ‘)’ + | XmlPattern + Patterns ::= Pattern {‘,’ Patterns} +``` + +A pattern is built from constants, constructors, variables and type +tests. Pattern matching tests whether a given value (or sequence of values) +has the shape defined by a pattern, and, if it does, binds the +variables in the pattern to the corresponding components of the value +(or sequence of values). The same variable name may not be bound more +than once in a pattern. + +###### Example +Some examples of patterns are: + 1. The pattern `ex: IOException` matches all instances of class + `IOException`, binding variable `ex` to the instance. + 1. The pattern `Some(x)` matches values of the form `Some(´v´)`, + binding `x` to the argument value ´v´ of the `Some` constructor. + 1. The pattern `(x, _)` matches pairs of values, binding `x` to + the first component of the pair. The second component is matched + with a wildcard pattern. + 1. The pattern `x :: y :: xs` matches lists of length ´\geq 2´, + binding `x` to the list's first element, `y` to the list's + second element, and `xs` to the remainder. + 1. The pattern `1 | 2 | 3` matches the integers between 1 and 3. + +Pattern matching is always done in a context which supplies an +expected type of the pattern. We distinguish the following kinds of +patterns. + +### Variable Patterns + +```ebnf + SimplePattern ::= ‘_’ + | varid +``` + +A _variable pattern_ ´x´ is a simple identifier which starts with a +lower case letter. It matches any value, and binds the variable name +to that value. The type of ´x´ is the expected type of the pattern as +given from outside. A special case is the wild-card pattern `_` +which is treated as if it was a fresh variable on each occurrence. + +### Typed Patterns + +```ebnf + Pattern1 ::= varid ‘:’ TypePat + | ‘_’ ‘:’ TypePat +``` + +A _typed pattern_ ´x: T´ consists of a pattern variable ´x´ and a +type pattern ´T´. The type of ´x´ is the type pattern ´T´, where +each type variable and wildcard is replaced by a fresh, unknown type. +This pattern matches any value matched by the [type pattern](#type-patterns) +´T´; it binds the variable name to +that value. + +### Pattern Binders + +```ebnf + Pattern2 ::= varid ‘@’ Pattern3 +``` + +A _pattern binder_ `´x´@´p´` consists of a pattern variable ´x´ and a +pattern ´p´. The type of the variable ´x´ is the static type ´T´ implied +by the pattern ´p´. +This pattern matches any value ´v´ matched by the pattern ´p´, +and it binds the variable name to that value. + +A pattern ´p´ _implies_ a type ´T´ if the pattern matches only values of the type ´T´. + +### Literal Patterns + +```ebnf + SimplePattern ::= Literal +``` + +A _literal pattern_ ´L´ matches any value that is equal (in terms of +`==`) to the literal ´L´. The type of ´L´ must conform to the +expected type of the pattern. + +### Interpolated string patterns + +```ebnf + Literal ::= interpolatedString +``` + +The expansion of interpolated string literals in patterns is the same as +in expressions. If it occurs in a pattern, a interpolated string literal +of either of the forms +``` +id"text0{ pat1 }text1 … { patn }textn" +id"""text0{ pat1 }text1 … { patn }textn""" +``` +is equivalent to: +``` +StringContext("""text0""", …, """textn""").id(pat1, …, patn) +``` +You could define your own `StringContext` to shadow the default one that's +in the `scala` package. + +This expansion is well-typed if the member `id` evaluates to an extractor +object. If the extractor object has `apply` as well as `unapply` or +`unapplySeq` methods, processed strings can be used as either expressions +or patterns. + +Taking XML as an example +```scala +implicit class XMLinterpolation(s: StringContext) = { + object xml { + def apply(exprs: Any*) = + // parse ‘s’ and build an XML tree with ‘exprs’ + //in the holes + def unapplySeq(xml: Node): Option[Seq[Node]] = + // match `s’ against `xml’ tree and produce + //subtrees in holes + } +} +``` +Then, XML pattern matching could be expressed like this: +```scala +case xml""" + + $linktext + + """ => ... +``` +where linktext is a variable bound by the pattern. + +### Stable Identifier Patterns + +```ebnf + SimplePattern ::= StableId +``` + +A _stable identifier pattern_ is a [stable identifier](03-types.html#paths) ´r´. +The type of ´r´ must conform to the expected +type of the pattern. The pattern matches any value ´v´ such that +`´r´ == ´v´` (see [here](12-the-scala-standard-library.html#root-classes)). + +To resolve the syntactic overlap with a variable pattern, a +stable identifier pattern may not be a simple name starting with a lower-case +letter. However, it is possible to enclose such a variable name in +backquotes; then it is treated as a stable identifier pattern. + +###### Example +Consider the following class definition: + +```scala +class C { c => + val x = 42 + val y = 27 + val Z = 8 + def f(x: Int) = x match { + case c.x => 1 // matches 42 + case `y` => 2 // matches 27 + case Z => 3 // matches 8 + case x => 4 // matches any value + } +} +``` + +Here, the first three patterns are stable identifier patterns, while the last +one is a variable pattern. + +### Constructor Patterns + +```ebnf +SimplePattern ::= StableId ‘(’ [Patterns] ‘)’ +``` + +A _constructor pattern_ is of the form ´c(p_1 , \ldots , p_n)´ where ´n +\geq 0´. It consists of a stable identifier ´c´, followed by element +patterns ´p_1 , \ldots , p_n´. The constructor ´c´ is a simple or +qualified name which denotes a [case class](05-classes-and-objects.html#case-classes). +If the case class is monomorphic, then it +must conform to the expected type of the pattern, and the formal +parameter types of ´x´'s [primary constructor](05-classes-and-objects.html#class-definitions) +are taken as the expected types of the element patterns ´p_1, \ldots , +p_n´. If the case class is polymorphic, then its type parameters are +instantiated so that the instantiation of ´c´ conforms to the expected +type of the pattern. The instantiated formal parameter types of ´c´'s +primary constructor are then taken as the expected types of the +component patterns ´p_1, \ldots , p_n´. The pattern matches all +objects created from constructor invocations ´c(v_1 , \ldots , v_n)´ +where each element pattern ´p_i´ matches the corresponding value +´v_i´. + +A special case arises when ´c´'s formal parameter types end in a +repeated parameter. This is further discussed [here](#pattern-sequences). + +### Tuple Patterns + +```ebnf + SimplePattern ::= ‘(’ [Patterns] ‘)’ +``` + +A _tuple pattern_ `(´p_1 , \ldots , p_n´)` is an alias +for the constructor pattern `scala.Tuple´n´(´p_1 , \ldots , p_n´)`, +where ´n \geq 2´. The empty tuple +`()` is the unique value of type `scala.Unit`. + +### Extractor Patterns + +```ebnf + SimplePattern ::= StableId ‘(’ [Patterns] ‘)’ +``` + +An _extractor pattern_ ´x(p_1 , \ldots , p_n)´ where ´n \geq 0´ is of +the same syntactic form as a constructor pattern. However, instead of +a case class, the stable identifier ´x´ denotes an object which has a +member method named `unapply` or `unapplySeq` that matches +the pattern. + +An extractor pattern cannot match the value `null`. The implementation +ensures that the `unapply`/`unapplySeq` method is not applied to `null`. + +A type is said to be an _extractor type_ for some type `T` if it has a +method `get` with return type `T`, and a method `isEmpty` with a return type +that conforms to `Boolean`. `Option[T]` is an extractor type for type `T`. + +An `unapply` method in an object ´x´ _matches_ the pattern +´x(p_1 , \ldots , p_n)´ if it has a single parameter (and, optionally, an +implicit parameter list) and one of the following applies: + +* ´n=0´ and `unapply`'s result type conforms to `Boolean`. In this case + the extractor pattern matches all values ´v´ for which + `´x´.unapply(´v´)` yields `true`. +* ´n=1´ and `unapply`'s result type is an extractor type for some + type ´T´. In this case, the (only) argument pattern ´p_1´ is typed in + turn with expected type ´T´. The extractor pattern matches then all + values ´v´ for which `´x´.unapply(´v´)` yields a value ´u´ for which `´u´.isEmpty` yields + `false`, `´u´.get` yields a value ´v_1´, and ´p_1´ matches ´v_1´. +* ´n>1´ and `unapply`'s result type is + an extractor type for some type ´T´ with members ´\_1 , \ldots , \_n´ returning + types ´T_1 , \ldots , T_n´. In this case, the argument patterns ´p_1 + , \ldots , p_n´ are typed in turn with expected types ´T_1 , \ldots , + T_n´. The extractor pattern matches then all values ´v´ for which + `´x´.unapply(´v´)` yields a value ´u´ for which + `´u´.isEmpty` yields `false`, `´u´.get` yields some value ´t´, and each pattern + ´p_i´ matches the corresponding value ´t._1´ from + ´t._1 , \ldots , t._n´. + +An `unapplySeq` method in an object ´x´ matches the pattern +´x(q_1 , \ldots , q_m, p_1 , \ldots , p_n)´ if it takes exactly one argument +and its result type is of the form `Option[(´T_1 , \ldots , T_m´, Seq[S])]` (if `m = 0`, the type `Option[Seq[S]]` is also accepted). +This case is further discussed [below](#pattern-sequences). + +###### Example 1 + +If we define an extractor object `Pair`: + +```scala +object Pair { + def apply[A, B](x: A, y: B) = Tuple2(x, y) + def unapply[A, B](x: Tuple2[A, B]): Option[Tuple2[A, B]] = Some(x) +} +``` + +This means that the name `Pair` can be used in place of `Tuple2` for tuple +formation as well as for deconstruction of tuples in patterns. +Hence, the following is possible: + +```scala +val x = (1, 2) +val y = x match { + case Pair(i, s) => Pair(s + i, i * i) +} +``` + +###### Example 2 + +If we define a class `NameBased` + +```scala +class NameBased[A, B](a: A, b: B) { + def isEmpty = false + def get = this + def _1 = a + def _2 = b +} +``` + +Then `NameBased` is an extractor type for `NameBased` itself, since it has a +member `isEmpty` returning a value of type Boolean, and it has a member `get` +returning a value of type `NameBased`. + +Since it also has members `_1` and `_2`, it can be used in an extractor pattern +with n = 2 as follows: + +```scala +object Extractor { + def unapply(x: Any) = new NameBased(1, "two") +} + +"anything" match { + case Extractor(a, b) => println(s"\$a, \$b") //prints "1, two" +} +``` + + +### Pattern Sequences + +```ebnf +SimplePattern ::= StableId ‘(’ [Patterns ‘,’] [varid ‘@’] ‘_’ ‘*’ ‘)’ +``` + +A _pattern sequence_ ´p_1 , \ldots , p_n´ appears in two contexts. +First, in a constructor pattern ´c(q_1 , \ldots , q_m, p_1 , \ldots , p_n)´, where ´c´ is a case class which has ´m+1´ primary constructor parameters, ending in a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters) of type `S*`. +Second, in an extractor pattern ´x(q_1 , \ldots , q_m, p_1 , \ldots , p_n)´ if the extractor object ´x´ does not have an `unapply` method, +but it does define an `unapplySeq` method with a result type that is an extractor type for type `(T_1, ... , T_m, Seq[S])` (if `m = 0`, an extractor type for the type `Seq[S]` is also accepted). The expected type for the patterns ´p_i´ is ´S´. + +The last pattern in a pattern sequence may be a _sequence wildcard_ `_*`. +Each element pattern ´p_i´ is type-checked with +´S´ as expected type, unless it is a sequence wildcard. If a final +sequence wildcard is present, the pattern matches all values ´v´ that +are sequences which start with elements matching patterns +´p_1 , \ldots , p_{n-1}´. If no final sequence wildcard is given, the +pattern matches all values ´v´ that are sequences of +length ´n´ which consist of elements matching patterns ´p_1 , \ldots , +p_n´. + +### Infix Operation Patterns + +```ebnf + Pattern3 ::= SimplePattern {id [nl] SimplePattern} +``` + +An _infix operation pattern_ ´p;\mathit{op};q´ is a shorthand for the +constructor or extractor pattern ´\mathit{op}(p, q)´. The precedence and +associativity of operators in patterns is the same as in +[expressions](06-expressions.html#prefix,-infix,-and-postfix-operations). + +An infix operation pattern ´p;\mathit{op};(q_1 , \ldots , q_n)´ is a +shorthand for the constructor or extractor pattern ´\mathit{op}(p, q_1 +, \ldots , q_n)´. + +### Pattern Alternatives + +```ebnf + Pattern ::= Pattern1 { ‘|’ Pattern1 } +``` + +A _pattern alternative_ `´p_1´ | ´\ldots´ | ´p_n´` +consists of a number of alternative patterns ´p_i´. All alternative +patterns are type checked with the expected type of the pattern. They +may not bind variables other than wildcards. The alternative pattern +matches a value ´v´ if at least one its alternatives matches ´v´. + +### XML Patterns + +XML patterns are treated [here](10-xml-expressions-and-patterns.html#xml-patterns). + +### Regular Expression Patterns + +Regular expression patterns have been discontinued in Scala from version 2.0. + +Later version of Scala provide a much simplified version of regular +expression patterns that cover most scenarios of non-text sequence +processing. A _sequence pattern_ is a pattern that stands in a +position where either (1) a pattern of a type `T` which is +conforming to +`Seq[A]` for some `A` is expected, or (2) a case +class constructor that has an iterated formal parameter +`A*`. A wildcard star pattern `_*` in the +rightmost position stands for arbitrary long sequences. It can be +bound to variables using `@`, as usual, in which case the variable will have the +type `Seq[A]`. + +### Irrefutable Patterns + +A pattern ´p´ is _irrefutable_ for a type ´T´, if one of the following applies: + +1. ´p´ is a variable pattern, +1. ´p´ is a typed pattern ´x: T'´, and ´T <: T'´, +1. ´p´ is a constructor pattern ´c(p_1 , \ldots , p_n)´, the type ´T´ + is an instance of class ´c´, the [primary constructor](05-classes-and-objects.html#class-definitions) + of type ´T´ has argument types ´T_1 , \ldots , T_n´, and each ´p_i´ is + irrefutable for ´T_i´. +1. ´p´ is an extractor pattern for which the extractor type is `Some[´T´]` for + some type ´T´ +1. ´p´ is an extractor pattern for which the extractor types `isEmpty` method + is the singleton type `false` +1. ´p´ is an extractor pattern for which the return type is the singleton type + `true` + +## Type Patterns + +```ebnf + TypePat ::= Type +``` + +Type patterns consist of types, type variables, and wildcards. +A type pattern ´T´ is of one of the following forms: + +* A reference to a class ´C´, ´p.C´, or `´T´#´C´`. This + type pattern matches any non-null instance of the given class. + Note that the prefix of the class, if it exists, is relevant for determining + class instances. For instance, the pattern ´p.C´ matches only + instances of classes ´C´ which were created with the path ´p´ as + prefix. This also applies to prefixes which are not given syntactically. + For example, if ´C´ refers to a class defined in the nearest enclosing + class and is thus equivalent to ´this.C´, it is considered to have a prefix. + + The bottom types `scala.Nothing` and `scala.Null` cannot + be used as type patterns, because they would match nothing in any case. + +* A singleton type `´p´.type`. This type pattern matches only the value + denoted by the path ´p´ (the `eq` method is used to compare the matched value + to ´p´). + +* A literal type `´lit´`. This type pattern matches only the value + denoted by the literal ´lit´ (the `==` method is used to compare the matched + value to ´lit´). + +* A compound type pattern `´T_1´ with ´\ldots´ with ´T_n´` where each ´T_i´ is a + type pattern. This type pattern matches all values that are matched by each of + the type patterns ´T_i´. + +* A parameterized type pattern ´T[a_1 , \ldots , a_n]´, where the ´a_i´ + are type variable patterns or wildcards `_`. + This type pattern matches all values which match ´T´ for + some arbitrary instantiation of the type variables and wildcards. The + bounds or alias type of these type variable are determined as + described [here](#type-parameter-inference-in-patterns). + +* A parameterized type pattern `scala.Array´[T_1]´`, where + ´T_1´ is a type pattern. This type pattern matches any non-null instance + of type `scala.Array´[U_1]´`, where ´U_1´ is a type matched by ´T_1´. + +Types which are not of one of the forms described above are also +accepted as type patterns. However, such type patterns will be translated to their +[erasure](03-types.html#type-erasure). The Scala +compiler will issue an "unchecked" warning for these patterns to +flag the possible loss of type-safety. + +A _type variable pattern_ is a simple identifier which starts with +a lower case letter. + +## Type Parameter Inference in Patterns + +Type parameter inference is the process of finding bounds for the +bound type variables in a typed pattern or constructor +pattern. Inference takes into account the expected type of the +pattern. + +### Type parameter inference for typed patterns + +Assume a typed pattern ´p: T'´. Let ´T´ result from ´T'´ where all wildcards in +´T'´ are renamed to fresh variable names. Let ´a_1 , \ldots , a_n´ be +the type variables in ´T´. These type variables are considered bound +in the pattern. Let the expected type of the pattern be ´\mathit{pt}´. + +Type parameter inference constructs first a set of subtype constraints over +the type variables ´a_i´. The initial constraints set ´\mathcal{C}\_0´ reflects +just the bounds of these type variables. That is, assuming ´T´ has +bound type variables ´a_1 , \ldots , a_n´ which correspond to class +type parameters ´a_1' , \ldots , a_n'´ with lower bounds ´L_1, \ldots , L_n´ +and upper bounds ´U_1 , \ldots , U_n´, ´\mathcal{C}_0´ contains the constraints + +$$ +\begin{cases} +a_i &<: \sigma U_i & \quad (i = 1, \ldots , n) \\\\ +\sigma L_i &<: a_i & \quad (i = 1, \ldots , n) +\end{cases} +$$ + +where ´\sigma´ is the substitution ´[a_1' := a_1 , \ldots , a_n' :=a_n]´. + +The set ´\mathcal{C}_0´ is then augmented by further subtype constraints. There are two +cases. + +###### Case 1 +If there exists a substitution ´\sigma´ over the type variables ´a_i , \ldots , a_n´ such that ´\sigma T´ conforms to ´\mathit{pt}´, one determines the weakest subtype constraints +´\mathcal{C}\_1´ over the type variables ´a_1, \ldots , a_n´ such that ´\mathcal{C}\_0 \wedge \mathcal{C}_1´ implies that ´T´ conforms to ´\mathit{pt}´. + +###### Case 2 +Otherwise, if ´T´ can not be made to conform to ´\mathit{pt}´ by +instantiating its type variables, one determines all type variables in +´\mathit{pt}´ which are defined as type parameters of a method enclosing +the pattern. Let the set of such type parameters be ´b_1 , \ldots , +b_m´. Let ´\mathcal{C}\_0'´ be the subtype constraints reflecting the bounds of the +type variables ´b_i´. If ´T´ denotes an instance type of a final +class, let ´\mathcal{C}\_2´ be the weakest set of subtype constraints over the type +variables ´a_1 , \ldots , a_n´ and ´b_1 , \ldots , b_m´ such that +´\mathcal{C}\_0 \wedge \mathcal{C}\_0' \wedge \mathcal{C}\_2´ implies that ´T´ conforms to +´\mathit{pt}´. If ´T´ does not denote an instance type of a final class, +let ´\mathcal{C}\_2´ be the weakest set of subtype constraints over the type variables +´a_1 , \ldots , a_n´ and ´b_1 , \ldots , b_m´ such that ´\mathcal{C}\_0 \wedge +\mathcal{C}\_0' \wedge \mathcal{C}\_2´ implies that it is possible to construct a type +´T'´ which conforms to both ´T´ and ´\mathit{pt}´. It is a static error if +there is no satisfiable set of constraints ´\mathcal{C}\_2´ with this property. + +The final step consists in choosing type bounds for the type +variables which imply the established constraint system. The process +is different for the two cases above. + +###### Case 1 +We take ´a_i >: L_i <: U_i´ where each ´L_i´ is minimal and each ´U_i´ is maximal wrt ´<:´ such that ´a_i >: L_i <: U_i´ for ´i = 1, \ldots, n´ implies ´\mathcal{C}\_0 \wedge \mathcal{C}\_1´. + +###### Case 2 +We take ´a_i >: L_i <: U_i´ and ´b\_i >: L_i' <: U_i' ´ where each ´L_i´ +and ´L_j'´ is minimal and each ´U_i´ and ´U_j'´ is maximal such that +´a_i >: L_i <: U_i´ for ´i = 1 , \ldots , n´ and +´b_j >: L_j' <: U_j'´ for ´j = 1 , \ldots , m´ +implies ´\mathcal{C}\_0 \wedge \mathcal{C}\_0' \wedge \mathcal{C}_2´. + +In both cases, local type inference is permitted to limit the +complexity of inferred bounds. Minimality and maximality of types have +to be understood relative to the set of types of acceptable +complexity. + +### Type parameter inference for constructor patterns +Assume a constructor pattern ´C(p_1 , \ldots , p_n)´ where class ´C´ +has type parameters ´a_1 , \ldots , a_n´. These type parameters +are inferred in the same way as for the typed pattern +`(_: ´C[a_1 , \ldots , a_n]´)`. + +###### Example +Consider the program fragment: + +```scala +val x: Any +x match { + case y: List[a] => ... +} +``` + +Here, the type pattern `List[a]` is matched against the +expected type `Any`. The pattern binds the type variable +`a`. Since `List[a]` conforms to `Any` +for every type argument, there are no constraints on `a`. +Hence, `a` is introduced as an abstract type with no +bounds. The scope of `a` is right-hand side of its case clause. + +On the other hand, if `x` is declared as + +```scala +val x: List[List[String]], +``` + +this generates the constraint +`List[a] <: List[List[String]]`, which simplifies to +`a <: List[String]`, because `List` is covariant. Hence, +`a` is introduced with upper bound +`List[String]`. + +###### Example +Consider the program fragment: + +```scala +val x: Any +x match { + case y: List[String] => ... +} +``` + +Scala does not maintain information about type arguments at run-time, +so there is no way to check that `x` is a list of strings. +Instead, the Scala compiler will [erase](03-types.html#type-erasure) the +pattern to `List[_]`; that is, it will only test whether the +top-level runtime-class of the value `x` conforms to +`List`, and the pattern match will succeed if it does. This +might lead to a class cast exception later on, in the case where the +list `x` contains elements other than strings. The Scala +compiler will flag this potential loss of type-safety with an +"unchecked" warning message. + +###### Example +Consider the program fragment + +```scala +class Term[A] +class Number(val n: Int) extends Term[Int] +def f[B](t: Term[B]): B = t match { + case y: Number => y.n +} +``` + +The expected type of the pattern `y: Number` is +`Term[B]`. The type `Number` does not conform to +`Term[B]`; hence Case 2 of the rules above +applies. This means that `B` is treated as another type +variable for which subtype constraints are inferred. In our case the +applicable constraint is `Number <: Term[B]`, which +entails `B = Int`. Hence, `B` is treated in +the case clause as an abstract type with lower and upper bound +`Int`. Therefore, the right hand side of the case clause, +`y.n`, of type `Int`, is found to conform to the +function's declared result type, `Number`. + +## Pattern Matching Expressions + +```ebnf + Expr ::= PostfixExpr ‘match’ ‘{’ CaseClauses ‘}’ + CaseClauses ::= CaseClause {CaseClause} + CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block +``` + +A _pattern matching expression_ + +```scala +e match { case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ } +``` + +consists of a selector expression ´e´ and a number ´n > 0´ of +cases. Each case consists of a (possibly guarded) pattern ´p_i´ and a +block ´b_i´. Each ´p_i´ might be complemented by a guard +`if ´e´` where ´e´ is a boolean expression. +The scope of the pattern +variables in ´p_i´ comprises the pattern's guard and the corresponding block ´b_i´. + +Let ´T´ be the type of the selector expression ´e´ and let ´a_1 +, \ldots , a_m´ be the type parameters of all methods enclosing +the pattern matching expression. For every ´a_i´, let ´L_i´ be its +lower bound and ´U_i´ be its higher bound. Every pattern ´p \in \{p_1, , \ldots , p_n\}´ +can be typed in two ways. First, it is attempted +to type ´p´ with ´T´ as its expected type. If this fails, ´p´ is +instead typed with a modified expected type ´T'´ which results from +´T´ by replacing every occurrence of a type parameter ´a_i´ by +*undefined*. If this second step fails also, a compile-time +error results. If the second step succeeds, let ´T_p´ be the type of +pattern ´p´ seen as an expression. One then determines minimal bounds +´L_11 , \ldots , L_m'´ and maximal bounds ´U_1' , \ldots , U_m'´ such +that for all ´i´, ´L_i <: L_i'´ and ´U_i' <: U_i´ and the following +constraint system is satisfied: + +$$ +L_1 <: a_1 <: U_1\;\wedge\;\ldots\;\wedge\;L_m <: a_m <: U_m \ \Rightarrow\ T_p <: T +$$ + +If no such bounds can be found, a compile time error results. If such +bounds are found, the pattern matching clause starting with ´p´ is +then typed under the assumption that each ´a_i´ has lower bound ´L_i'´ +instead of ´L_i´ and has upper bound ´U_i'´ instead of ´U_i´. + +The expected type of every block ´b_i´ is the expected type of the +whole pattern matching expression. The type of the pattern matching +expression is then the [weak least upper bound](03-types.html#weak-conformance) +of the types of all blocks +´b_i´. + +When applying a pattern matching expression to a selector value, +patterns are tried in sequence until one is found which matches the +[selector value](#patterns). Say this case is `case ´p_i \Rightarrow b_i´`. +The result of the whole expression is the result of evaluating ´b_i´, +where all pattern variables of ´p_i´ are bound to +the corresponding parts of the selector value. If no matching pattern +is found, a `scala.MatchError` exception is thrown. + +The pattern in a case may also be followed by a guard suffix +`if e` with a boolean expression ´e´. The guard expression is +evaluated if the preceding pattern in the case matches. If the guard +expression evaluates to `true`, the pattern match succeeds as +normal. If the guard expression evaluates to `false`, the pattern +in the case is considered not to match and the search for a matching +pattern continues. + +In the interest of efficiency the evaluation of a pattern matching +expression may try patterns in some other order than textual +sequence. This might affect evaluation through +side effects in guards. However, it is guaranteed that a guard +expression is evaluated only if the pattern it guards matches. + +If the selector of a pattern match is an instance of a +[`sealed` class](05-classes-and-objects.html#modifiers), +the compilation of pattern matching can emit warnings which diagnose +that a given set of patterns is not exhaustive, i.e. that there is a +possibility of a `MatchError` being raised at run-time. + +###### Example + +Consider the following definitions of arithmetic terms: + +```scala +abstract class Term[T] +case class Lit(x: Int) extends Term[Int] +case class Succ(t: Term[Int]) extends Term[Int] +case class IsZero(t: Term[Int]) extends Term[Boolean] +case class If[T](c: Term[Boolean], + t1: Term[T], + t2: Term[T]) extends Term[T] +``` + +There are terms to represent numeric literals, incrementation, a zero +test, and a conditional. Every term carries as a type parameter the +type of the expression it represents (either `Int` or `Boolean`). + +A type-safe evaluator for such terms can be written as follows. + +```scala +def eval[T](t: Term[T]): T = t match { + case Lit(n) => n + case Succ(u) => eval(u) + 1 + case IsZero(u) => eval(u) == 0 + case If(c, u1, u2) => eval(if (eval(c)) u1 else u2) +} +``` + +Note that the evaluator makes crucial use of the fact that type +parameters of enclosing methods can acquire new bounds through pattern +matching. + +For instance, the type of the pattern in the second case, +`Succ(u)`, is `Int`. It conforms to the selector type +`T` only if we assume an upper and lower bound of `Int` for `T`. +Under the assumption `Int <: T <: Int` we can also +verify that the type right hand side of the second case, `Int` +conforms to its expected type, `T`. + +## Pattern Matching Anonymous Functions + +```ebnf + BlockExpr ::= ‘{’ CaseClauses ‘}’ +``` + +An anonymous function can be defined by a sequence of cases + +```scala +{ case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ } +``` + +which appear as an expression without a prior `match`. The +expected type of such an expression must in part be defined. It must +be either `scala.Function´k´[´S_1 , \ldots , S_k´, ´R´]` for some ´k > 0´, +or `scala.PartialFunction[´S_1´, ´R´]`, where the +argument type(s) ´S_1 , \ldots , S_k´ must be fully determined, but the result type +´R´ may be undetermined. + +If the expected type is [SAM-convertible](06-expressions.html#sam-conversion) +to `scala.Function´k´[´S_1 , \ldots , S_k´, ´R´]`, +the expression is taken to be equivalent to the anonymous function: + +```scala +(´x_1: S_1 , \ldots , x_k: S_k´) => (´x_1 , \ldots , x_k´) match { + case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ +} +``` + +Here, each ´x_i´ is a fresh name. +As was shown [here](06-expressions.html#anonymous-functions), this anonymous function is in turn +equivalent to the following instance creation expression, where + ´T´ is the weak least upper bound of the types of all ´b_i´. + +```scala +new scala.Function´k´[´S_1 , \ldots , S_k´, ´T´] { + def apply(´x_1: S_1 , \ldots , x_k: S_k´): ´T´ = (´x_1 , \ldots , x_k´) match { + case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ + } +} +``` + +If the expected type is `scala.PartialFunction[´S´, ´R´]`, +the expression is taken to be equivalent to the following instance creation expression: + +```scala +new scala.PartialFunction[´S´, ´T´] { + def apply(´x´: ´S´): ´T´ = x match { + case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ + } + def isDefinedAt(´x´: ´S´): Boolean = { + case ´p_1´ => true ´\ldots´ case ´p_n´ => true + case _ => false + } +} +``` + +Here, ´x´ is a fresh name and ´T´ is the weak least upper bound of the +types of all ´b_i´. The final default case in the `isDefinedAt` +method is omitted if one of the patterns ´p_1 , \ldots , p_n´ is +already a variable or wildcard pattern. + +###### Example +Here's an example which uses +`foldLeft` to compute the scalar product of +two vectors: + +```scala +def scalarProduct(xs: Array[Double], ys: Array[Double]) = + (xs zip ys).foldLeft(0.0) { + case (a, (b, c)) => a + b * c + } +``` + +The case clauses in this code are equivalent to the following +anonymous function: + +```scala +(x, y) => (x, y) match { + case (a, (b, c)) => a + b * c +} +``` diff --git a/docs/_spec/09-top-level-definitions.md b/docs/_spec/09-top-level-definitions.md new file mode 100644 index 000000000000..33c436e8d77b --- /dev/null +++ b/docs/_spec/09-top-level-definitions.md @@ -0,0 +1,210 @@ +--- +title: Top-Level Definitions +layout: default +chapter: 9 +--- + +# Top-Level Definitions + +## Compilation Units + +```ebnf +CompilationUnit ::= {‘package’ QualId semi} TopStatSeq +TopStatSeq ::= TopStat {semi TopStat} +TopStat ::= {Annotation} {Modifier} TmplDef + | Import + | Packaging + | PackageObject + | +QualId ::= id {‘.’ id} +``` + +A compilation unit consists of a sequence of packagings, import +clauses, and class and object definitions, which may be preceded by a +package clause. + +A _compilation unit_ + +```scala +package ´p_1´; +´\ldots´ +package ´p_n´; +´\mathit{stats}´ +``` + +starting with one or more package +clauses is equivalent to a compilation unit consisting of the +packaging + +```scala +package ´p_1´ { ´\ldots´ + package ´p_n´ { + ´\mathit{stats}´ + } ´\ldots´ +} +``` + +Every compilation unit implicitly imports the following packages, in the given order: + 1. the package `java.lang`, + 2. the package `scala`, and + 3. the object [`scala.Predef`](12-the-scala-standard-library.html#the-predef-object), unless there is an explicit top-level import that references `scala.Predef`. + +Members of a later import in that order hide members of an earlier import. + +The exception to the implicit import of `scala.Predef` can be useful to hide, e.g., predefined implicit conversions. + +## Packagings + +```ebnf +Packaging ::= ‘package’ QualId [nl] ‘{’ TopStatSeq ‘}’ +``` + +A _package_ is a special object which defines a set of member classes, +objects and packages. Unlike other objects, packages are not introduced +by a definition. Instead, the set of members of a package is determined by +packagings. + +A packaging `package ´p´ { ´\mathit{ds}´ }` injects all +definitions in ´\mathit{ds}´ as members into the package whose qualified name +is ´p´. Members of a package are called _top-level_ definitions. +If a definition in ´\mathit{ds}´ is labeled `private`, it is +visible only for other members in the package. + +Inside the packaging, all members of package ´p´ are visible under their +simple names. However this rule does not extend to members of enclosing +packages of ´p´ that are designated by a prefix of the path ´p´. + +```scala +package org.net.prj { + ... +} +``` + +all members of package `org.net.prj` are visible under their +simple names, but members of packages `org` or `org.net` require +explicit qualification or imports. + +Selections ´p´.´m´ from ´p´ as well as imports from ´p´ +work as for objects. However, unlike other objects, packages may not +be used as values. It is illegal to have a package with the same fully +qualified name as a module or a class. + +Top-level definitions outside a packaging are assumed to be injected +into a special empty package. That package cannot be named and +therefore cannot be imported. However, members of the empty package +are visible to each other without qualification. + +## Package Objects + +```ebnf +PackageObject ::= ‘package’ ‘object’ ObjectDef +``` + +A _package object_ `package object ´p´ extends ´t´` adds the +members of template ´t´ to the package ´p´. There can be only one +package object per package. The standard naming convention is to place +the definition above in a file named `package.scala` that's +located in the directory corresponding to package ´p´. + +The package object should not define a member with the same name as +one of the top-level objects or classes defined in package ´p´. If +there is a name conflict, the behavior of the program is currently +undefined. It is expected that this restriction will be lifted in a +future version of Scala. + +## Package References + +```ebnf +QualId ::= id {‘.’ id} +``` + +A reference to a package takes the form of a qualified identifier. +Like all other references, package references are relative. That is, +a package reference starting in a name ´p´ will be looked up in the +closest enclosing scope that defines a member named ´p´. + +If a package name is shadowed, it's possible to refer to its +fully-qualified name by prefixing it with +the special predefined name `_root_`, which refers to the +outermost root package that contains all top-level packages. + +The name `_root_` has this special denotation only when +used as the first element of a qualifier; it is an ordinary +identifier otherwise. + +###### Example +Consider the following program: + +```scala +package b { + class B +} + +package a { + package b { + class A { + val x = new _root_.b.B + } + class C { + import _root_.b._ + def y = new B + } + } +} + +``` + +Here, the reference `_root_.b.B` refers to class `B` in the +toplevel package `b`. If the `_root_` prefix had been +omitted, the name `b` would instead resolve to the package +`a.b`, and, provided that package does not also +contain a class `B`, a compiler-time error would result. + +## Programs + +A _program_ is a top-level object that has a member method +_main_ of type `(Array[String])Unit`. Programs can be +executed from a command shell. The program's command arguments are +passed to the `main` method as a parameter of type +`Array[String]`. + +The `main` method of a program can be directly defined in the +object, or it can be inherited. The scala library defines a special class +`scala.App` whose body acts as a `main` method. +An objects ´m´ inheriting from this class is thus a program, +which executes the initialization code of the object ´m´. + +###### Example +The following example will create a hello world program by defining +a method `main` in module `test.HelloWorld`. + +```scala +package test +object HelloWorld { + def main(args: Array[String]) { println("Hello World") } +} +``` + +This program can be started by the command + +```scala +scala test.HelloWorld +``` + +In a Java environment, the command + +```scala +java test.HelloWorld +``` + +would work as well. + +`HelloWorld` can also be defined without a `main` method +by inheriting from `App` instead: + +```scala +package test +object HelloWorld extends App { + println("Hello World") +} +``` diff --git a/docs/_spec/10-xml-expressions-and-patterns.md b/docs/_spec/10-xml-expressions-and-patterns.md new file mode 100644 index 000000000000..a9b7edb14fff --- /dev/null +++ b/docs/_spec/10-xml-expressions-and-patterns.md @@ -0,0 +1,145 @@ +--- +title: XML +layout: default +chapter: 10 +--- + +# XML Expressions and Patterns + +__By Burak Emir__ + +This chapter describes the syntactic structure of XML expressions and patterns. +It follows as closely as possible the XML 1.0 specification, +changes being mandated by the possibility of embedding Scala code fragments. + +## XML expressions + +XML expressions are expressions generated by the following production, where the +opening bracket `<` of the first element must be in a position to start the lexical +[XML mode](01-lexical-syntax.html#xml-mode). + +```ebnf +XmlExpr ::= XmlContent {Element} +``` + +Well-formedness constraints of the XML specification apply, which +means for instance that start tags and end tags must match, and +attributes may only be defined once, except for constraints +related to entity resolution. + +The following productions describe Scala's extensible markup language, +designed as close as possible to the W3C extensible markup language +standard. Only the productions for attribute values and character data are changed. +Scala does not support declarations. Entity references are not resolved at runtime. + +```ebnf +Element ::= EmptyElemTag + | STag Content ETag + +EmptyElemTag ::= ‘<’ Name {S Attribute} [S] ‘/>’ + +STag ::= ‘<’ Name {S Attribute} [S] ‘>’ +ETag ::= ‘’ +Content ::= [CharData] {Content1 [CharData]} +Content1 ::= XmlContent + | Reference + | ScalaExpr +XmlContent ::= Element + | CDSect + | PI + | Comment +``` + +If an XML expression is a single element, its value is a runtime +representation of an XML node (an instance of a subclass of +`scala.xml.Node`). If the XML expression consists of more +than one element, then its value is a runtime representation of a +sequence of XML nodes (an instance of a subclass of +`scala.Seq[scala.xml.Node]`). + +If an XML expression is an entity reference, CDATA section, processing +instruction, or a comment, it is represented by an instance of the +corresponding Scala runtime class. + +By default, beginning and trailing whitespace in element content is removed, +and consecutive occurrences of whitespace are replaced by a single space +character `\u0020`. This behavior can be changed to preserve all whitespace +with a compiler option. + +```ebnf +Attribute ::= Name Eq AttValue + +AttValue ::= ‘"’ {CharQ | CharRef} ‘"’ + | ‘'’ {CharA | CharRef} ‘'’ + | ScalaExpr + +ScalaExpr ::= Block + +CharData ::= { CharNoRef } ´\textit{ without}´ {CharNoRef}‘{’CharB {CharNoRef} + ´\textit{ and without}´ {CharNoRef}‘]]>’{CharNoRef} +``` + + +XML expressions may contain Scala expressions as attribute values or +within nodes. In the latter case, these are embedded using a single opening +brace `{` and ended by a closing brace `}`. To express a single opening braces +within XML text as generated by CharData, it must be doubled. +Thus, `{{` represents the XML text `{` and does not introduce an embedded Scala expression. + + +```ebnf +BaseChar, CDSect, Char, Comment, CombiningChar, Ideographic, NameChar, PI, S, Reference + ::= ´\textit{“as in W3C XML”}´ + +Char1 ::= Char ´\textit{ without}´ ‘<’ | ‘&’ +CharQ ::= Char1 ´\textit{ without}´ ‘"’ +CharA ::= Char1 ´\textit{ without}´ ‘'’ +CharB ::= Char1 ´\textit{ without}´ ‘{’ + +Name ::= XNameStart {NameChar} + +XNameStart ::= ‘_’ | BaseChar | Ideographic + ´\textit{ (as in W3C XML, but without }´ ‘:’´)´ +``` + +## XML patterns + +XML patterns are patterns generated by the following production, where +the opening bracket `<` of the element patterns must be in a position +to start the lexical [XML mode](01-lexical-syntax.html#xml-mode). + +```ebnf +XmlPattern ::= ElementPattern +``` + +Well-formedness constraints of the XML specification apply. + +An XML pattern has to be a single element pattern. It +matches exactly those runtime +representations of an XML tree +that have the same structure as described by the pattern. +XML patterns may contain [Scala patterns](08-pattern-matching.html#pattern-matching-expressions). + +Whitespace is treated the same way as in XML expressions. + +By default, beginning and trailing whitespace in element content is removed, +and consecutive occurrences of whitespace are replaced by a single space +character `\u0020`. This behavior can be changed to preserve all whitespace +with a compiler option. + +```ebnf +ElemPattern ::= EmptyElemTagP + | STagP ContentP ETagP + +EmptyElemTagP ::= ‘<’ Name [S] ‘/>’ +STagP ::= ‘<’ Name [S] ‘>’ +ETagP ::= ‘’ +ContentP ::= [CharData] {(ElemPattern|ScalaPatterns) [CharData]} +ContentP1 ::= ElemPattern + | Reference + | CDSect + | PI + | Comment + | ScalaPatterns +ScalaPatterns ::= ‘{’ Patterns ‘}’ +``` diff --git a/docs/_spec/11-annotations.md b/docs/_spec/11-annotations.md new file mode 100644 index 000000000000..388d99b60e56 --- /dev/null +++ b/docs/_spec/11-annotations.md @@ -0,0 +1,176 @@ +--- +title: Annotations +layout: default +chapter: 11 +--- + +# Annotations + +```ebnf + Annotation ::= ‘@’ SimpleType {ArgumentExprs} + ConstrAnnotation ::= ‘@’ SimpleType ArgumentExprs +``` + +## Definition + +Annotations associate meta-information with definitions. +A simple annotation has the form `@´c´` or `@´c(a_1 , \ldots , a_n)´`. +Here, ´c´ is a constructor of a class ´C´, which must conform +to the class `scala.Annotation`. + +Annotations may apply to definitions or declarations, types, or +expressions. An annotation of a definition or declaration appears in +front of that definition. An annotation of a type appears after +that type. An annotation of an expression ´e´ appears after the +expression ´e´, separated by a colon. More than one annotation clause +may apply to an entity. The order in which these annotations are given +does not matter. + +Examples: + +```scala +@deprecated("Use D", "1.0") class C { ... } // Class annotation +@transient @volatile var m: Int // Variable annotation +String @local // Type annotation +(e: @unchecked) match { ... } // Expression annotation +``` + +## Predefined Annotations + +### Java Platform Annotations + +The meaning of annotation clauses is implementation-dependent. On the +Java platform, the following annotations have a standard meaning. + + * `@transient` Marks a field to be non-persistent; this is + equivalent to the `transient` + modifier in Java. + + * `@volatile` Marks a field which can change its value + outside the control of the program; this + is equivalent to the `volatile` + modifier in Java. + + * `@SerialVersionUID()` Attaches a serial version identifier (a + `long` constant) to a class. + This is equivalent to the following field + definition in Java: + + ```java + private final static SerialVersionUID = + ``` + + * `@throws()` A Java compiler checks that a program contains handlers for checked exceptions + by analyzing which checked exceptions can result from the execution of a method or + constructor. For each checked exception which is a possible result, the + `throws` + clause for the method or constructor must mention the class of that exception + or one of the superclasses of the class of that exception. + +### Java Beans Annotations + + * `@scala.beans.BeanProperty` When prefixed to a definition of some variable `X`, this + annotation causes getter and setter methods `getX`, `setX` + in the Java bean style to be added in the class containing the + variable. The first letter of the variable appears capitalized after + the `get` or `set`. When the annotation is added to the + definition of an immutable value definition `X`, only a getter is + generated. The construction of these methods is part of + code-generation; therefore, these methods become visible only once a + classfile for the containing class is generated. + + * `@scala.beans.BooleanBeanProperty` This annotation is equivalent to `scala.reflect.BeanProperty`, but + the generated getter method is named `isX` instead of `getX`. + +### Deprecation Annotations + + * `@deprecated(message: , since: )`
+ Marks a definition as deprecated. Accesses to the + defined entity will then cause a deprecated warning mentioning the + _message_ `` to be issued from the compiler. + The argument _since_ documents since when the definition should be considered deprecated.
+ Deprecated warnings are suppressed in code that belongs itself to a definition + that is labeled deprecated. + + * `@deprecatedName(name: , since: )`
+ Marks a formal parameter name as deprecated. Invocations of this entity + using named parameter syntax referring to the deprecated parameter name cause a deprecation warning. + +### Scala Compiler Annotations + + * `@unchecked` When applied to the selector of a `match` expression, + this attribute suppresses any warnings about non-exhaustive pattern + matches that would otherwise be emitted. For instance, no warnings + would be produced for the method definition below. + + ```scala + def f(x: Option[Int]) = (x: @unchecked) match { + case Some(y) => y + } + ``` + + Without the `@unchecked` annotation, a Scala compiler could + infer that the pattern match is non-exhaustive, and could produce a + warning because `Option` is a `sealed` class. + + * `@uncheckedStable` When applied a value declaration or definition, it allows the defined + value to appear in a path, even if its type is [volatile](03-types.html#volatile-types). + For instance, the following member definitions are legal: + + ```scala + type A { type T } + type B + @uncheckedStable val x: A with B // volatile type + val y: x.T // OK since `x' is still a path + ``` + + Without the `@uncheckedStable` annotation, the designator `x` + would not be a path since its type `A with B` is volatile. Hence, + the reference `x.T` would be malformed. + + When applied to value declarations or definitions that have non-volatile + types, the annotation has no effect. + + * `@specialized` When applied to the definition of a type parameter, this annotation causes + the compiler + to generate specialized definitions for primitive types. An optional list of + primitive + types may be given, in which case specialization takes into account only + those types. + For instance, the following code would generate specialized traits for + `Unit`, `Int` and `Double` + + ```scala + trait Function0[@specialized(Unit, Int, Double) T] { + def apply: T + } + ``` + + Whenever the static type of an expression matches a specialized variant of + a definition, the compiler will instead use the specialized version. + See the [specialization sid](https://docs.scala-lang.org/sips/scala-specialization.html) for more details of the implementation. + + +## User-defined Annotations + +Other annotations may be interpreted by platform- or application-dependent +tools. The class `scala.annotation.Annotation` is the base class for +user-defined annotations. It has two sub-traits: +- `scala.annotation.StaticAnnotation`: Instances of a subclass of this trait + will be stored in the generated class files, and therefore accessible to + runtime reflection and later compilation runs. +- `scala.annotation.ConstantAnnotation`: Instances of a subclass of this trait + may only have arguments which are + [constant expressions](06-expressions.html#constant-expressions), and are + also stored in the generated class files. +- If an annotation class inherits from neither `scala.ConstantAnnotation` nor + `scala.StaticAnnotation`, its instances are visible only locally during the + compilation run that analyzes them. + +## Host-platform Annotations + +The host platform may define its own annotation format. These annotations do not +extend any of the classes in the `scala.annotation` package, but can generally +be used in the same way as Scala annotations. The host platform may impose +additional restrictions on the expressions which are valid as annotation +arguments. diff --git a/docs/_spec/12-the-scala-standard-library.md b/docs/_spec/12-the-scala-standard-library.md new file mode 100644 index 000000000000..80d66c0c4166 --- /dev/null +++ b/docs/_spec/12-the-scala-standard-library.md @@ -0,0 +1,816 @@ +--- +title: Standard Library +layout: default +chapter: 12 +--- + +# The Scala Standard Library + +The Scala standard library consists of the package `scala` with a +number of classes and modules. Some of these classes are described in +the following. + +![Class hierarchy of Scala](public/images/classhierarchy.png) + +## Root Classes + +The root of this hierarchy is formed by class `Any`. +Every class in a Scala execution environment inherits directly or +indirectly from this class. Class `Any` has two direct +subclasses: `AnyRef` and `AnyVal`. + +The subclass `AnyRef` represents all values which are represented +as objects in the underlying host system. Classes written in other languages +inherit from `scala.AnyRef`. + +The predefined subclasses of class `AnyVal` describe +values which are not implemented as objects in the underlying host +system. + +User-defined Scala classes which do not explicitly inherit from +`AnyVal` inherit directly or indirectly from `AnyRef`. They cannot +inherit from both `AnyRef` and `AnyVal`. + +Classes `AnyRef` and `AnyVal` are required to provide only +the members declared in class `Any`, but implementations may add +host-specific methods to these classes (for instance, an +implementation may identify class `AnyRef` with its own root +class for objects). + +The signatures of these root classes are described by the following +definitions. + +```scala +package scala +/** The universal root class */ +abstract class Any { + + /** Defined equality; abstract here */ + def equals(that: Any): Boolean + + /** Semantic equality between values */ + final def == (that: Any): Boolean = + if (null eq this) null eq that else this equals that + + /** Semantic inequality between values */ + final def != (that: Any): Boolean = !(this == that) + + /** Hash code; abstract here */ + def hashCode: Int = ´\ldots´ + + /** Textual representation; abstract here */ + def toString: String = ´\ldots´ + + /** Type test; needs to be inlined to work as given */ + def isInstanceOf[a]: Boolean + + /** Type cast; needs to be inlined to work as given */ */ + def asInstanceOf[A]: A = this match { + case x: A => x + case _ => if (this eq null) this + else throw new ClassCastException() + } +} + +/** The root class of all value types */ +final class AnyVal extends Any + +/** The root class of all reference types */ +class AnyRef extends Any { + def equals(that: Any): Boolean = this eq that + final def eq(that: AnyRef): Boolean = ´\ldots´ // reference equality + final def ne(that: AnyRef): Boolean = !(this eq that) + + def hashCode: Int = ´\ldots´ // hashCode computed from allocation address + def toString: String = ´\ldots´ // toString computed from hashCode and class name + + def synchronized[T](body: => T): T // execute `body` in while locking `this`. +} +``` + +The type test `´x´.isInstanceOf[´T´]` is equivalent to a typed +pattern match + +```scala +´x´ match { + case _: ´T'´ => true + case _ => false +} +``` + +where the type ´T'´ is the same as ´T´ except if ´T´ is +of the form ´D´ or ´D[\mathit{tps}]´ where ´D´ is a type member of some outer class ´C´. +In this case ´T'´ is `´C´#´D´` (or `´C´#´D[tps]´`, respectively), whereas ´T´ itself would expand to `´C´.this.´D[tps]´`. +In other words, an `isInstanceOf` test does not check that types have the same enclosing instance. + +The test `´x´.asInstanceOf[´T´]` is treated specially if ´T´ is a +[numeric value type](#value-classes). In this case the cast will +be translated to an application of a [conversion method](#numeric-value-types) +`x.to´T´`. For non-numeric values ´x´ the operation will raise a +`ClassCastException`. + +## Value Classes + +Value classes are classes whose instances are not represented as +objects by the underlying host system. All value classes inherit from +class `AnyVal`. Scala implementations need to provide the +value classes `Unit`, `Boolean`, `Double`, `Float`, +`Long`, `Int`, `Char`, `Short`, and `Byte` +(but are free to provide others as well). +The signatures of these classes are defined in the following. + +### Numeric Value Types + +Classes `Double`, `Float`, +`Long`, `Int`, `Char`, `Short`, and `Byte` +are together called _numeric value types_. Classes `Byte`, +`Short`, or `Char` are called _subrange types_. +Subrange types, as well as `Int` and `Long` are called _integer types_, whereas `Float` and `Double` are called _floating point types_. + +Numeric value types are ranked in the following partial order: + +```scala +Byte - Short + \ + Int - Long - Float - Double + / + Char +``` + +`Byte` and `Short` are the lowest-ranked types in this order, +whereas `Double` is the highest-ranked. Ranking does _not_ +imply a [conformance relationship](03-types.html#conformance); for +instance `Int` is not a subtype of `Long`. However, object +[`Predef`](#the-predef-object) defines [views](07-implicits.html#views) +from every numeric value type to all higher-ranked numeric value types. +Therefore, lower-ranked types are implicitly converted to higher-ranked types +when required by the [context](06-expressions.html#implicit-conversions). + +Given two numeric value types ´S´ and ´T´, the _operation type_ of +´S´ and ´T´ is defined as follows: If both ´S´ and ´T´ are subrange +types then the operation type of ´S´ and ´T´ is `Int`. Otherwise +the operation type of ´S´ and ´T´ is the larger of the two types wrt +ranking. Given two numeric values ´v´ and ´w´ the operation type of +´v´ and ´w´ is the operation type of their run-time types. + +Any numeric value type ´T´ supports the following methods. + + * Comparison methods for equals (`==`), not-equals (`!=`), + less-than (`<`), greater-than (`>`), less-than-or-equals + (`<=`), greater-than-or-equals (`>=`), which each exist in 7 + overloaded alternatives. Each alternative takes a parameter of some + numeric value type. Its result type is type `Boolean`. The + operation is evaluated by converting the receiver and its argument to + their operation type and performing the given comparison operation of + that type. + * Arithmetic methods addition (`+`), subtraction (`-`), + multiplication (`*`), division (`/`), and remainder + (`%`), which each exist in 7 overloaded alternatives. Each + alternative takes a parameter of some numeric value type ´U´. Its + result type is the operation type of ´T´ and ´U´. The operation is + evaluated by converting the receiver and its argument to their + operation type and performing the given arithmetic operation of that + type. + * Parameterless arithmetic methods identity (`+`) and negation + (`-`), with result type ´T´. The first of these returns the + receiver unchanged, whereas the second returns its negation. + * Conversion methods `toByte`, `toShort`, `toChar`, + `toInt`, `toLong`, `toFloat`, `toDouble` which + convert the receiver object to the target type, using the rules of + Java's numeric type cast operation. The conversion might truncate the + numeric value (as when going from `Long` to `Int` or from + `Int` to `Byte`) or it might lose precision (as when going + from `Double` to `Float` or when converting between + `Long` and `Float`). + +Integer numeric value types support in addition the following operations: + + * Bit manipulation methods bitwise-and (`&`), bitwise-or + {`|`}, and bitwise-exclusive-or (`^`), which each exist in 5 + overloaded alternatives. Each alternative takes a parameter of some + integer numeric value type. Its result type is the operation type of + ´T´ and ´U´. The operation is evaluated by converting the receiver and + its argument to their operation type and performing the given bitwise + operation of that type. + + * A parameterless bit-negation method (`~`). Its result type is + the receiver type ´T´ or `Int`, whichever is larger. + The operation is evaluated by converting the receiver to the result + type and negating every bit in its value. + * Bit-shift methods left-shift (`<<`), arithmetic right-shift + (`>>`), and unsigned right-shift (`>>>`). Each of these + methods has two overloaded alternatives, which take a parameter ´n´ + of type `Int`, respectively `Long`. The result type of the + operation is the receiver type ´T´, or `Int`, whichever is larger. + The operation is evaluated by converting the receiver to the result + type and performing the specified shift by ´n´ bits. + +Numeric value types also implement operations `equals`, +`hashCode`, and `toString` from class `Any`. + +The `equals` method tests whether the argument is a numeric value +type. If this is true, it will perform the `==` operation which +is appropriate for that type. That is, the `equals` method of a +numeric value type can be thought of being defined as follows: + +```scala +def equals(other: Any): Boolean = other match { + case that: Byte => this == that + case that: Short => this == that + case that: Char => this == that + case that: Int => this == that + case that: Long => this == that + case that: Float => this == that + case that: Double => this == that + case _ => false +} +``` + +The `hashCode` method returns an integer hashcode that maps equal +numeric values to equal results. It is guaranteed to be the identity +for type `Int` and for all subrange types. + +The `toString` method displays its receiver as an integer or +floating point number. + +###### Example + +This is the signature of the numeric value type `Int`: + +```scala +package scala +abstract sealed class Int extends AnyVal { + def == (that: Double): Boolean // double equality + def == (that: Float): Boolean // float equality + def == (that: Long): Boolean // long equality + def == (that: Int): Boolean // int equality + def == (that: Short): Boolean // int equality + def == (that: Byte): Boolean // int equality + def == (that: Char): Boolean // int equality + /* analogous for !=, <, >, <=, >= */ + + def + (that: Double): Double // double addition + def + (that: Float): Double // float addition + def + (that: Long): Long // long addition + def + (that: Int): Int // int addition + def + (that: Short): Int // int addition + def + (that: Byte): Int // int addition + def + (that: Char): Int // int addition + /* analogous for -, *, /, % */ + + def & (that: Long): Long // long bitwise and + def & (that: Int): Int // int bitwise and + def & (that: Short): Int // int bitwise and + def & (that: Byte): Int // int bitwise and + def & (that: Char): Int // int bitwise and + /* analogous for |, ^ */ + + def << (cnt: Int): Int // int left shift + def << (cnt: Long): Int // long left shift + /* analogous for >>, >>> */ + + def unary_+ : Int // int identity + def unary_- : Int // int negation + def unary_~ : Int // int bitwise negation + + def toByte: Byte // convert to Byte + def toShort: Short // convert to Short + def toChar: Char // convert to Char + def toInt: Int // convert to Int + def toLong: Long // convert to Long + def toFloat: Float // convert to Float + def toDouble: Double // convert to Double +} +``` + +### Class `Boolean` + +Class `Boolean` has only two values: `true` and +`false`. It implements operations as given in the following +class definition. + +```scala +package scala +abstract sealed class Boolean extends AnyVal { + def && (p: => Boolean): Boolean = // boolean and + if (this) p else false + def || (p: => Boolean): Boolean = // boolean or + if (this) true else p + def & (x: Boolean): Boolean = // boolean strict and + if (this) x else false + def | (x: Boolean): Boolean = // boolean strict or + if (this) true else x + def == (x: Boolean): Boolean = // boolean equality + if (this) x else x.unary_! + def != (x: Boolean): Boolean = // boolean inequality + if (this) x.unary_! else x + def unary_!: Boolean = // boolean negation + if (this) false else true +} +``` + +The class also implements operations `equals`, `hashCode`, +and `toString` from class `Any`. + +The `equals` method returns `true` if the argument is the +same boolean value as the receiver, `false` otherwise. The +`hashCode` method returns a fixed, implementation-specific hash-code when invoked on `true`, +and a different, fixed, implementation-specific hash-code when invoked on `false`. The `toString` method +returns the receiver converted to a string, i.e. either `"true"` or `"false"`. + +### Class `Unit` + +Class `Unit` has only one value: `()`. It implements only +the three methods `equals`, `hashCode`, and `toString` +from class `Any`. + +The `equals` method returns `true` if the argument is the +unit value `()`, `false` otherwise. The +`hashCode` method returns a fixed, implementation-specific hash-code, +The `toString` method returns `"()"`. + +## Standard Reference Classes + +This section presents some standard Scala reference classes which are +treated in a special way by the Scala compiler – either Scala provides +syntactic sugar for them, or the Scala compiler generates special code +for their operations. Other classes in the standard Scala library are +documented in the Scala library documentation by HTML pages. + +### Class `String` + +Scala's `String` class is usually derived from the standard String +class of the underlying host system (and may be identified with +it). For Scala clients the class is taken to support in each case a +method + +```scala +def + (that: Any): String +``` + +which concatenates its left operand with the textual representation of its +right operand. + +### The `Tuple` classes + +Scala defines tuple classes `Tuple´n´` for ´n = 2 , \ldots , 22´. +These are defined as follows. + +```scala +package scala +case class Tuple´n´[+T_1, ..., +T_n](_1: T_1, ..., _´n´: T_´n´) { + def toString = "(" ++ _1 ++ "," ++ ´\ldots´ ++ "," ++ _´n´ ++ ")" +} +``` + +### The `Function` Classes + +Scala defines function classes `Function´n´` for ´n = 1 , \ldots , 22´. +These are defined as follows. + +```scala +package scala +trait Function´n´[-T_1, ..., -T_´n´, +R] { + def apply(x_1: T_1, ..., x_´n´: T_´n´): R + def toString = "" +} +``` + +The `PartialFunction` subclass of `Function1` represents functions that (indirectly) specify their domain. +Use the `isDefined` method to query whether the partial function is defined for a given input (i.e., whether the input is part of the function's domain). + +```scala +class PartialFunction[-A, +B] extends Function1[A, B] { + def isDefinedAt(x: A): Boolean +} +``` + +The implicitly imported [`Predef`](#the-predef-object) object defines the name +`Function` as an alias of `Function1`. + +### Class `Array` + +All operations on arrays desugar to the corresponding operations of the +underlying platform. Therefore, the following class definition is given for +informational purposes only: + +```scala +final class Array[T](_length: Int) +extends java.io.Serializable with java.lang.Cloneable { + def length: Int = ´\ldots´ + def apply(i: Int): T = ´\ldots´ + def update(i: Int, x: T): Unit = ´\ldots´ + override def clone(): Array[T] = ´\ldots´ +} +``` + +If ´T´ is not a type parameter or abstract type, the type `Array[T]` +is represented as the array type `|T|[]` in the +underlying host system, where `|T|` is the erasure of `T`. +If ´T´ is a type parameter or abstract type, a different representation might be +used (it is `Object` on the Java platform). + +#### Operations + +`length` returns the length of the array, `apply` means subscripting, +and `update` means element update. + +Because of the syntactic sugar for `apply` and `update` operations, +we have the following correspondences between Scala and Java code for +operations on an array `xs`: + +|_Scala_ |_Java_ | +|------------------|------------| +|`xs.length` |`xs.length` | +|`xs(i)` |`xs[i]` | +|`xs(i) = e` |`xs[i] = e` | + +Two implicit conversions exist in `Predef` that are frequently applied to arrays: +a conversion to `scala.collection.mutable.ArrayOps` and a conversion to +`scala.collection.mutable.ArraySeq` (a subtype of `scala.collection.Seq`). + +Both types make many of the standard operations found in the Scala +collections API available. The conversion to `ArrayOps` is temporary, as all operations +defined on `ArrayOps` return a value of type `Array`, while the conversion to `ArraySeq` +is permanent as all operations return a value of type `ArraySeq`. +The conversion to `ArrayOps` takes priority over the conversion to `ArraySeq`. + +Because of the tension between parametrized types in Scala and the ad-hoc +implementation of arrays in the host-languages, some subtle points +need to be taken into account when dealing with arrays. These are +explained in the following. + +#### Variance + +Unlike arrays in Java, arrays in Scala are _not_ +co-variant; That is, ´S <: T´ does not imply +`Array[´S´] ´<:´ Array[´T´]` in Scala. +However, it is possible to cast an array +of ´S´ to an array of ´T´ if such a cast is permitted in the host +environment. + +For instance `Array[String]` does not conform to +`Array[Object]`, even though `String` conforms to `Object`. +However, it is possible to cast an expression of type +`Array[String]` to `Array[Object]`, and this +cast will succeed without raising a `ClassCastException`. Example: + +```scala +val xs = new Array[String](2) +// val ys: Array[Object] = xs // **** error: incompatible types +val ys: Array[Object] = xs.asInstanceOf[Array[Object]] // OK +``` + +The instantiation of an array with a polymorphic element type ´T´ requires +information about type ´T´ at runtime. +This information is synthesized by adding a [context bound](07-implicits.html#context-bounds-and-view-bounds) +of `scala.reflect.ClassTag` to type ´T´. +An example is the +following implementation of method `mkArray`, which creates +an array of an arbitrary type ´T´, given a sequence of ´T´`s which +defines its elements: + +```scala +import reflect.ClassTag +def mkArray[T : ClassTag](elems: Seq[T]): Array[T] = { + val result = new Array[T](elems.length) + var i = 0 + for (elem <- elems) { + result(i) = elem + i += 1 + } + result +} +``` + +If type ´T´ is a type for which the host platform offers a specialized array +representation, this representation is used. + +###### Example +On the Java Virtual Machine, an invocation of `mkArray(List(1,2,3))` +will return a primitive array of `int`s, written as `int[]` in Java. + +#### Companion object + +`Array`'s companion object provides various factory methods for the +instantiation of single- and multi-dimensional arrays, an extractor method +[`unapplySeq`](08-pattern-matching.html#extractor-patterns) which enables pattern matching +over arrays and additional utility methods: + +```scala +package scala +object Array { + /** copies array elements from `src` to `dest`. */ + def copy(src: AnyRef, srcPos: Int, + dest: AnyRef, destPos: Int, length: Int): Unit = ´\ldots´ + + /** Returns an array of length 0 */ + def empty[T: ClassTag]: Array[T] = + + /** Create an array with given elements. */ + def apply[T: ClassTag](xs: T*): Array[T] = ´\ldots´ + + /** Creates array with given dimensions */ + def ofDim[T: ClassTag](n1: Int): Array[T] = ´\ldots´ + /** Creates a 2-dimensional array */ + def ofDim[T: ClassTag](n1: Int, n2: Int): Array[Array[T]] = ´\ldots´ + ´\ldots´ + + /** Concatenate all argument arrays into a single array. */ + def concat[T: ClassTag](xss: Array[T]*): Array[T] = ´\ldots´ + + /** Returns an array that contains the results of some element computation a number + * of times. */ + def fill[T: ClassTag](n: Int)(elem: => T): Array[T] = ´\ldots´ + /** Returns a two-dimensional array that contains the results of some element + * computation a number of times. */ + def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): Array[Array[T]] = ´\ldots´ + ´\ldots´ + + /** Returns an array containing values of a given function over a range of integer + * values starting from 0. */ + def tabulate[T: ClassTag](n: Int)(f: Int => T): Array[T] = ´\ldots´ + /** Returns a two-dimensional array containing values of a given function + * over ranges of integer values starting from `0`. */ + def tabulate[T: ClassTag](n1: Int, n2: Int)(f: (Int, Int) => T): Array[Array[T]] = ´\ldots´ + ´\ldots´ + + /** Returns an array containing a sequence of increasing integers in a range. */ + def range(start: Int, end: Int): Array[Int] = ´\ldots´ + /** Returns an array containing equally spaced values in some integer interval. */ + def range(start: Int, end: Int, step: Int): Array[Int] = ´\ldots´ + + /** Returns an array containing repeated applications of a function to a start value. */ + def iterate[T: ClassTag](start: T, len: Int)(f: T => T): Array[T] = ´\ldots´ + + /** Enables pattern matching over arrays */ + def unapplySeq[A](x: Array[A]): Option[IndexedSeq[A]] = Some(x) +} +``` + +## Class Node + +```scala +package scala.xml + +trait Node { + + /** the label of this node */ + def label: String + + /** attribute axis */ + def attribute: Map[String, String] + + /** child axis (all children of this node) */ + def child: Seq[Node] + + /** descendant axis (all descendants of this node) */ + def descendant: Seq[Node] = child.toList.flatMap { + x => x::x.descendant.asInstanceOf[List[Node]] + } + + /** descendant axis (all descendants of this node) */ + def descendant_or_self: Seq[Node] = this::child.toList.flatMap { + x => x::x.descendant.asInstanceOf[List[Node]] + } + + override def equals(x: Any): Boolean = x match { + case that:Node => + that.label == this.label && + that.attribute.sameElements(this.attribute) && + that.child.sameElements(this.child) + case _ => false + } + + /** XPath style projection function. Returns all children of this node + * that are labeled with 'that'. The document order is preserved. + */ + def \(that: Symbol): NodeSeq = { + new NodeSeq({ + that.name match { + case "_" => child.toList + case _ => + var res:List[Node] = Nil + for (x <- child.elements if x.label == that.name) { + res = x::res + } + res.reverse + } + }) + } + + /** XPath style projection function. Returns all nodes labeled with the + * name 'that' from the 'descendant_or_self' axis. Document order is preserved. + */ + def \\(that: Symbol): NodeSeq = { + new NodeSeq( + that.name match { + case "_" => this.descendant_or_self + case _ => this.descendant_or_self.asInstanceOf[List[Node]]. + filter(x => x.label == that.name) + }) + } + + /** hashcode for this XML node */ + override def hashCode = + Utility.hashCode(label, attribute.toList.hashCode, child) + + /** string representation of this node */ + override def toString = Utility.toXML(this) + +} +``` + +## The `Predef` Object + +The `Predef` object defines standard functions and type aliases +for Scala programs. It is implicitly imported, as described in +[the chapter on name binding](02-identifiers-names-and-scopes.html), +so that all its defined members are available without qualification. +Its definition for the JVM environment conforms to the following signature: + +```scala +package scala +object Predef { + + // classOf --------------------------------------------------------- + + /** Returns the runtime representation of a class type. */ + def classOf[T]: Class[T] = null + // this is a dummy, classOf is handled by compiler. + + // valueOf ----------------------------------------------------------- + + /** Retrieve the single value of a type with a unique inhabitant. */ + @inline def valueOf[T](implicit vt: ValueOf[T]): T {} = vt.value + // instances of the ValueOf type class are provided by the compiler. + + // Standard type aliases --------------------------------------------- + + type String = java.lang.String + type Class[T] = java.lang.Class[T] + + // Miscellaneous ----------------------------------------------------- + + type Function[-A, +B] = Function1[A, B] + + type Map[A, +B] = collection.immutable.Map[A, B] + type Set[A] = collection.immutable.Set[A] + + val Map = collection.immutable.Map + val Set = collection.immutable.Set + + // Manifest types, companions, and incantations for summoning --------- + + type ClassManifest[T] = scala.reflect.ClassManifest[T] + type Manifest[T] = scala.reflect.Manifest[T] + type OptManifest[T] = scala.reflect.OptManifest[T] + val ClassManifest = scala.reflect.ClassManifest + val Manifest = scala.reflect.Manifest + val NoManifest = scala.reflect.NoManifest + + def manifest[T](implicit m: Manifest[T]) = m + def classManifest[T](implicit m: ClassManifest[T]) = m + def optManifest[T](implicit m: OptManifest[T]) = m + + // Minor variations on identity functions ----------------------------- + def identity[A](x: A): A = x + def implicitly[T](implicit e: T) = e // for summoning implicit values from the nether world + @inline def locally[T](x: T): T = x // to communicate intent and avoid unmoored statements + + // Asserts, Preconditions, Postconditions ----------------------------- + + def assert(assertion: Boolean) { + if (!assertion) + throw new java.lang.AssertionError("assertion failed") + } + + def assert(assertion: Boolean, message: => Any) { + if (!assertion) + throw new java.lang.AssertionError("assertion failed: " + message) + } + + def assume(assumption: Boolean) { + if (!assumption) + throw new IllegalArgumentException("assumption failed") + } + + def assume(assumption: Boolean, message: => Any) { + if (!assumption) + throw new IllegalArgumentException(message.toString) + } + + def require(requirement: Boolean) { + if (!requirement) + throw new IllegalArgumentException("requirement failed") + } + + def require(requirement: Boolean, message: => Any) { + if (!requirement) + throw new IllegalArgumentException("requirement failed: "+ message) + } +``` + +```scala + // Printing and reading ----------------------------------------------- + + def print(x: Any) = Console.print(x) + def println() = Console.println() + def println(x: Any) = Console.println(x) + def printf(text: String, xs: Any*) = Console.printf(text.format(xs: _*)) + + // Implicit conversions ------------------------------------------------ + + ... +} +``` + +### Predefined Implicit Definitions + +The `Predef` object also contains a number of implicit definitions, which are available by default (because `Predef` is implicitly imported). +Implicit definitions come in two priorities. High-priority implicits are defined in the `Predef` class itself whereas low priority implicits are defined in a class inherited by `Predef`. The rules of +static [overloading resolution](06-expressions.html#overloading-resolution) +stipulate that, all other things being equal, implicit resolution +prefers high-priority implicits over low-priority ones. + +The available low-priority implicits include definitions falling into the following categories. + +1. For every primitive type, a wrapper that takes values of that type + to instances of a `runtime.Rich*` class. For instance, values of type `Int` + can be implicitly converted to instances of class `runtime.RichInt`. + +1. For every array type with elements of primitive type, a wrapper that + takes the arrays of that type to instances of a `ArraySeq` class. For instance, values of type `Array[Float]` can be implicitly converted to instances of class `ArraySeq[Float]`. + There are also generic array wrappers that take elements + of type `Array[T]` for arbitrary `T` to `ArraySeq`s. + +1. An implicit conversion from `String` to `WrappedString`. + +The available high-priority implicits include definitions falling into the following categories. + + * An implicit wrapper that adds `ensuring` methods + with the following overloaded variants to type `Any`. + + ```scala + def ensuring(cond: Boolean): A = { assert(cond); x } + def ensuring(cond: Boolean, msg: Any): A = { assert(cond, msg); x } + def ensuring(cond: A => Boolean): A = { assert(cond(x)); x } + def ensuring(cond: A => Boolean, msg: Any): A = { assert(cond(x), msg); x } + ``` + + * An implicit wrapper that adds a `->` method with the following implementation + to type `Any`. + + ```scala + def -> [B](y: B): (A, B) = (x, y) + ``` + + * For every array type with elements of primitive type, a wrapper that + takes the arrays of that type to instances of a `runtime.ArrayOps` + class. For instance, values of type `Array[Float]` can be implicitly + converted to instances of class `runtime.ArrayOps[Float]`. There are + also generic array wrappers that take elements of type `Array[T]` for + arbitrary `T` to `ArrayOps`s. + + * An implicit wrapper that adds `+` and `formatted` method with the following + implementations to type `Any`. + + ```scala + def +(other: String) = String.valueOf(self) + other + def formatted(fmtstr: String): String = fmtstr format self + ``` + + * Numeric primitive conversions that implement the transitive closure of the + following mappings: + + ``` + Byte -> Short + Short -> Int + Char -> Int + Int -> Long + Long -> Float + Float -> Double + ``` + + * Boxing and unboxing conversions between primitive types and their boxed + versions: + + ``` + Byte <-> java.lang.Byte + Short <-> java.lang.Short + Char <-> java.lang.Character + Int <-> java.lang.Integer + Long <-> java.lang.Long + Float <-> java.lang.Float + Double <-> java.lang.Double + Boolean <-> java.lang.Boolean + ``` + + * An implicit definition that generates instances of type `T <:< T`, for + any type `T`. Here, `<:<` is a class defined as follows. + + ```scala + sealed abstract class <:<[-From, +To] extends (From => To) + ``` + + Implicit parameters of `<:<` types are typically used to implement type constraints. diff --git a/docs/_spec/13-syntax-summary.md b/docs/_spec/13-syntax-summary.md new file mode 100644 index 000000000000..6ece538e2ff1 --- /dev/null +++ b/docs/_spec/13-syntax-summary.md @@ -0,0 +1,330 @@ +--- +title: Syntax Summary +layout: default +chapter: 13 +--- + +# Syntax Summary + +The following descriptions of Scala tokens uses literal characters `‘c’` when referring to the ASCII fragment `\u0000` – `\u007F`. + +## Lexical Syntax + +The lexical syntax of Scala is given by the following grammar in EBNF form: + +```ebnf +whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ +upper ::= ‘A’ | … | ‘Z’ | ‘$’ and any character in Unicode categories Lu, Lt or Nl, + and any character in Unicode categories Lo and Lm that doesn't have + contributory property Other_Lowercase +lower ::= ‘a’ | … | ‘z’ | ‘_’ and any character in Unicode category Ll, + and any character in Unicode categories Lo or Lm that has contributory + property Other_Lowercase +letter ::= upper | lower +digit ::= ‘0’ | … | ‘9’ +paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ +delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ +opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | + ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ + and any character in Unicode categories Sm or So +printableChar ::= all characters in [\u0020, \u007E] inclusive +UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit +hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ +charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) +escapeSeq ::= UnicodeEscape | charEscapeSeq +op ::= opchar {opchar} +varid ::= lower idrest +boundvarid ::= varid + | ‘`’ varid ‘`’ +plainid ::= upper idrest + | varid + | op +id ::= plainid + | ‘`’ { charNoBackQuoteOrNewline | escapeSeq } ‘`’ +idrest ::= {letter | digit} [‘_’ op] + +integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] +decimalNumeral ::= digit {digit} +hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit {hexDigit} + +floatingPointLiteral + ::= digit {digit} ‘.’ digit {digit} [exponentPart] [floatType] + | ‘.’ digit {digit} [exponentPart] [floatType] + | digit {digit} exponentPart [floatType] + | digit {digit} [exponentPart] floatType +exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit {digit} +floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ + +booleanLiteral ::= ‘true’ | ‘false’ + +characterLiteral ::= ‘'’ (charNoQuoteOrNewline | escapeSeq) ‘'’ + +stringLiteral ::= ‘"’ {stringElement} ‘"’ + | ‘"""’ multiLineChars ‘"""’ +stringElement ::= charNoDoubleQuoteOrNewline + | escapeSeq +multiLineChars ::= {[‘"’] [‘"’] charNoDoubleQuote} {‘"’} + +interpolatedString + ::= alphaid ‘"’ {[‘\’] interpolatedStringPart | ‘\\’ | ‘\"’} ‘"’ + | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘\$’) | escape} {‘"’} ‘"""’ +interpolatedStringPart + ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape +escape ::= ‘\$\$’ + | ‘\$"’ + | ‘\$’ alphaid + | ‘\$’ BlockExpr +alphaid ::= upper idrest + | varid + +symbolLiteral ::= ‘'’ plainid + +comment ::= ‘/*’ “any sequence of characters; nested comments are allowed” ‘*/’ + | ‘//’ “any sequence of characters up to end of line” + +nl ::= ´\mathit{“new line character”}´ +semi ::= ‘;’ | nl {nl} +``` + +## Context-free Syntax + +The context-free syntax of Scala is given by the following EBNF +grammar: + +```ebnf + Literal ::= [‘-’] integerLiteral + | [‘-’] floatingPointLiteral + | booleanLiteral + | characterLiteral + | stringLiteral + | interpolatedString + | symbolLiteral + | ‘null’ + + QualId ::= id {‘.’ id} + ids ::= id {‘,’ id} + + Path ::= StableId + | [id ‘.’] ‘this’ + StableId ::= id + | Path ‘.’ id + | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id + ClassQualifier ::= ‘[’ id ‘]’ + + Type ::= FunctionArgTypes ‘=>’ Type + | InfixType [ExistentialClause] + FunctionArgTypes ::= InfixType + | ‘(’ [ ParamType {‘,’ ParamType } ] ‘)’ + ExistentialClause ::= ‘forSome’ ‘{’ ExistentialDcl {semi ExistentialDcl} ‘}’ + ExistentialDcl ::= ‘type’ TypeDcl + | ‘val’ ValDcl + InfixType ::= CompoundType {id [nl] CompoundType} + CompoundType ::= AnnotType {‘with’ AnnotType} [Refinement] + | Refinement + AnnotType ::= SimpleType {Annotation} + SimpleType ::= SimpleType TypeArgs + | SimpleType ‘#’ id + | StableId + | Path ‘.’ ‘type’ + | ‘(’ Types ‘)’ + TypeArgs ::= ‘[’ Types ‘]’ + Types ::= Type {‘,’ Type} + Refinement ::= [nl] ‘{’ RefineStat {semi RefineStat} ‘}’ + RefineStat ::= Dcl + | ‘type’ TypeDef + | + TypePat ::= Type + + Ascription ::= ‘:’ InfixType + | ‘:’ Annotation {Annotation} + | ‘:’ ‘_’ ‘*’ + + Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr + | Expr1 + Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] + | ‘while’ ‘(’ Expr ‘)’ {nl} Expr + | ‘try’ Expr [‘catch’ Expr] [‘finally’ Expr] + | ‘do’ Expr [semi] ‘while’ ‘(’ Expr ‘)’ + | ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr + | ‘throw’ Expr + | ‘return’ [Expr] + | [SimpleExpr ‘.’] id ‘=’ Expr + | PrefixOperator SimpleExpr ‘=’ Expr + | SimpleExpr1 ArgumentExprs ‘=’ Expr + | PostfixExpr + | PostfixExpr Ascription + | PostfixExpr ‘match’ ‘{’ CaseClauses ‘}’ + PostfixExpr ::= InfixExpr [id [nl]] + InfixExpr ::= PrefixExpr + | InfixExpr id [nl] InfixExpr + PrefixExpr ::= [PrefixOperator] SimpleExpr + PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ + SimpleExpr ::= ‘new’ (ClassTemplate | TemplateBody) + | BlockExpr + | SimpleExpr1 [‘_’] + SimpleExpr1 ::= Literal + | Path + | ‘_’ + | ‘(’ [Exprs] ‘)’ + | SimpleExpr ‘.’ id + | SimpleExpr TypeArgs + | SimpleExpr1 ArgumentExprs + | XmlExpr + Exprs ::= Expr {‘,’ Expr} + ArgumentExprs ::= ‘(’ [Exprs] ‘)’ + | ‘(’ [Exprs ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ + | [nl] BlockExpr + BlockExpr ::= ‘{’ CaseClauses ‘}’ + | ‘{’ Block ‘}’ + Block ::= BlockStat {semi BlockStat} [ResultExpr] + BlockStat ::= Import + | {Annotation} [‘implicit’] [‘lazy’] Def + | {Annotation} {LocalModifier} TmplDef + | Expr1 + | + ResultExpr ::= Expr1 + | (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block + + Enumerators ::= Generator {semi Generator} + Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} + + CaseClauses ::= CaseClause { CaseClause } + CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block + Guard ::= ‘if’ PostfixExpr + + Pattern ::= Pattern1 { ‘|’ Pattern1 } + Pattern1 ::= boundvarid ‘:’ TypePat + | ‘_’ ‘:’ TypePat + | Pattern2 + Pattern2 ::= id [‘@’ Pattern3] + | Pattern3 + Pattern3 ::= SimplePattern + | SimplePattern { id [nl] SimplePattern } + SimplePattern ::= ‘_’ + | varid + | Literal + | StableId + | StableId ‘(’ [Patterns] ‘)’ + | StableId ‘(’ [Patterns ‘,’] [id ‘@’] ‘_’ ‘*’ ‘)’ + | ‘(’ [Patterns] ‘)’ + | XmlPattern + Patterns ::= Pattern [‘,’ Patterns] + | ‘_’ ‘*’ + + TypeParamClause ::= ‘[’ VariantTypeParam {‘,’ VariantTypeParam} ‘]’ + FunTypeParamClause::= ‘[’ TypeParam {‘,’ TypeParam} ‘]’ + VariantTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeParam + TypeParam ::= (id | ‘_’) [TypeParamClause] [‘>:’ Type] [‘<:’ Type] + {‘<%’ Type} {‘:’ Type} + ParamClauses ::= {ParamClause} [[nl] ‘(’ ‘implicit’ Params ‘)’] + ParamClause ::= [nl] ‘(’ [Params] ‘)’ + Params ::= Param {‘,’ Param} + Param ::= {Annotation} id [‘:’ ParamType] [‘=’ Expr] + ParamType ::= Type + | ‘=>’ Type + | Type ‘*’ + ClassParamClauses ::= {ClassParamClause} + [[nl] ‘(’ ‘implicit’ ClassParams ‘)’] + ClassParamClause ::= [nl] ‘(’ [ClassParams] ‘)’ + ClassParams ::= ClassParam {‘,’ ClassParam} + ClassParam ::= {Annotation} {Modifier} [(‘val’ | ‘var’)] + id ‘:’ ParamType [‘=’ Expr] + Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’ + Binding ::= (id | ‘_’) [‘:’ Type] + + Modifier ::= LocalModifier + | AccessModifier + | ‘override’ + LocalModifier ::= ‘abstract’ + | ‘final’ + | ‘sealed’ + | ‘implicit’ + | ‘lazy’ + AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] + AccessQualifier ::= ‘[’ (id | ‘this’) ‘]’ + + Annotation ::= ‘@’ SimpleType {ArgumentExprs} + ConstrAnnotation ::= ‘@’ SimpleType ArgumentExprs + + TemplateBody ::= [nl] ‘{’ [SelfType] TemplateStat {semi TemplateStat} ‘}’ + TemplateStat ::= Import + | {Annotation [nl]} {Modifier} Def + | {Annotation [nl]} {Modifier} Dcl + | Expr + | + SelfType ::= id [‘:’ Type] ‘=>’ + | ‘this’ ‘:’ Type ‘=>’ + + Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} + ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors) + ImportSelectors ::= ‘{’ {ImportSelector ‘,’} (ImportSelector | ‘_’) ‘}’ + ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’] + + Dcl ::= ‘val’ ValDcl + | ‘var’ VarDcl + | ‘def’ FunDcl + | ‘type’ {nl} TypeDcl + + ValDcl ::= ids ‘:’ Type + VarDcl ::= ids ‘:’ Type + FunDcl ::= FunSig [‘:’ Type] + FunSig ::= id [FunTypeParamClause] ParamClauses + TypeDcl ::= id [TypeParamClause] [‘>:’ Type] [‘<:’ Type] + + PatVarDef ::= ‘val’ PatDef + | ‘var’ VarDef + Def ::= PatVarDef + | ‘def’ FunDef + | ‘type’ {nl} TypeDef + | TmplDef + PatDef ::= Pattern2 {‘,’ Pattern2} [‘:’ Type] ‘=’ Expr + VarDef ::= PatDef + | ids ‘:’ Type ‘=’ ‘_’ + FunDef ::= FunSig [‘:’ Type] ‘=’ Expr + | FunSig [nl] ‘{’ Block ‘}’ + | ‘this’ ParamClause ParamClauses + (‘=’ ConstrExpr | [nl] ConstrBlock) + TypeDef ::= id [TypeParamClause] ‘=’ Type + + TmplDef ::= [‘case’] ‘class’ ClassDef + | [‘case’] ‘object’ ObjectDef + | ‘trait’ TraitDef + ClassDef ::= id [TypeParamClause] {ConstrAnnotation} [AccessModifier] + ClassParamClauses ClassTemplateOpt + TraitDef ::= id [TypeParamClause] TraitTemplateOpt + ObjectDef ::= id ClassTemplateOpt + ClassTemplateOpt ::= ‘extends’ ClassTemplate | [[‘extends’] TemplateBody] + TraitTemplateOpt ::= ‘extends’ TraitTemplate | [[‘extends’] TemplateBody] + ClassTemplate ::= [EarlyDefs] ClassParents [TemplateBody] + TraitTemplate ::= [EarlyDefs] TraitParents [TemplateBody] + ClassParents ::= Constr {‘with’ AnnotType} + TraitParents ::= AnnotType {‘with’ AnnotType} + Constr ::= AnnotType {ArgumentExprs} + EarlyDefs ::= ‘{’ [EarlyDef {semi EarlyDef}] ‘}’ ‘with’ + EarlyDef ::= {Annotation [nl]} {Modifier} PatVarDef + + ConstrExpr ::= SelfInvocation + | ConstrBlock + ConstrBlock ::= ‘{’ SelfInvocation {semi BlockStat} ‘}’ + SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} + + TopStatSeq ::= TopStat {semi TopStat} + TopStat ::= {Annotation [nl]} {Modifier} TmplDef + | Import + | Packaging + | PackageObject + | + Packaging ::= ‘package’ QualId [nl] ‘{’ TopStatSeq ‘}’ + PackageObject ::= ‘package’ ‘object’ ObjectDef + + CompilationUnit ::= {‘package’ QualId semi} TopStatSeq +``` + + diff --git a/docs/_spec/14-references.md b/docs/_spec/14-references.md new file mode 100644 index 000000000000..cc088dfcae11 --- /dev/null +++ b/docs/_spec/14-references.md @@ -0,0 +1,207 @@ +--- +title: References +layout: default +chapter: 14 +--- + +# References + +TODO (see comments in markdown source) + + diff --git a/docs/_spec/15-changelog.md b/docs/_spec/15-changelog.md new file mode 100644 index 000000000000..5d24511ff1cb --- /dev/null +++ b/docs/_spec/15-changelog.md @@ -0,0 +1,847 @@ +--- +title: Changelog +layout: default +chapter: 15 +--- + +# Changelog + +This changelog was no longer maintained after version 2.8.0. + +A pull request updating this chapter to list the most significant +changes made in more recent Scala versions would be highly welcome. + +Many language changes, especially larger ones, are documented in SIP +(Scala Improvement Process) proposals. Most proposals that were +accepted and implemented have not merged into the main spec. Pull +requests that merge SIPs into the main spec are also highly welcome. + +To find out what has changed in Scala 2 since 2.8.0, you can consult +the following sources: + +* Scala release notes (recent versions): https://github.com/scala/scala/releases +* Scala release notes (older versions): https://scala-lang.org/blog/announcements/ +* Scala release notes (even older versions): presumably findable via search engine +* Spec changelog in version control: https://github.com/scala/scala/commits/2.13.x/spec +* SIPs: https://docs.scala-lang.org/sips/all.html + +## Changes in Version 2.8.0 + +Trailing commas in expression, argument, type or pattern sequences are +no longer supported. + +Changed visibility rules for nested packages (where done?) + +Changed [visibility rules](02-identifiers-names-and-scopes.html) +so that packages are no longer treated specially. + +Added section on [weak conformance](03-types.html#weak-conformance). +Relaxed type rules for conditionals, +match expressions, try expressions to compute their result type using +least upper bound wrt weak conformance. Relaxed type rule for local type +inference so that argument types need only weekly conform to inferred +formal parameter types. Added section on +[numeric widening](06-expressions.html#numeric-widening) to support +weak conformance. + +Tightened rules to avoid accidental [overrides](05-classes-and-objects.html#overriding). + +Removed class literals. + +Added section on [context bounds](07-implicits.html#context-bounds-and-view-bounds). + +Clarified differences between [`isInstanceOf` and pattern matches](12-the-scala-standard-library.html#root-classes). + +Allowed [`implicit` modifier on function literals](06-expressions.html#anonymous-functions) with a single parameter. + +## Changes in Version 2.7.2 + +_(10-Nov-2008)_ + +#### Precedence of Assignment Operators + +The [precedence of assignment operators](06-expressions.html#prefix,-infix,-and-postfix-operations) +has been brought in line with. From now on `+=`, has the same precedence as `=`. + +#### Wildcards as function parameters + +A formal parameter to an anonymous function may now be a +[wildcard represented by an underscore](06-expressions.html#placeholder-syntax-for-anonymous-functions). + +> _ => 7 // The function that ignores its argument +> // and always returns 7. + +#### Unicode alternative for left arrow + +The Unicode glyph ‘´\leftarrow´’ ´`\u2190`´ is now treated as a reserved +identifier, equivalent to the ASCII symbol ‘`<-`’. + +## Changes in Version 2.7.1 + +_(09-April-2008)_ + +#### Change in Scoping Rules for Wildcard Placeholders in Types + +A wildcard in a type now binds to the closest enclosing type +application. For example `List[List[_]]` is now equivalent to this +existential type: + + List[List[t] forSome { type t }] + +In version 2.7.0, the type expanded instead to: + + List[List[t]] forSome { type t } + +The new convention corresponds exactly to the way wildcards in Java are +interpreted. + +#### No Contractiveness Requirement for Implicits + +The contractiveness requirement for +[implicit method definitions](07-implicits.html#implicit-parameters) +has been dropped. Instead, it is checked for each implicit expansion individually +that the expansion does not result in a cycle or a tree of infinitely +growing types. + +## Changes in Version 2.7.0 + +_(07-Feb-2008)_ + +#### Java Generics + +Scala now supports Java generic types by default: + +- A generic type in Java such as `ArrayList` is translated to + a generic type in Scala: `ArrayList[String]`. + +- A wildcard type such as `ArrayList` is translated + to `ArrayList[_ <: Number]`. This is itself a shorthand for the + existential type `ArrayList[T] forSome { type T <: Number }`. + +- A raw type in Java such as `ArrayList` is translated to + `ArrayList[_]`, which is a shorthand for + `ArrayList[T] forSome { type T }`. + +This translation works if `-target:jvm-1.5` is specified, which is the +new default. For any other target, Java generics are not recognized. To +ensure upgradability of Scala codebases, extraneous type parameters for +Java classes under `-target:jvm-1.4` are simply ignored. For instance, +when compiling with `-target:jvm-1.4`, a Scala type such as +`ArrayList[String]` is simply treated as the unparameterized type +`ArrayList`. + +#### Changes to Case Classes + +The Scala compiler generates a [companion extractor object for every case class] +(05-classes-and-objects.html#case-classes) now. For instance, given the case class: + + case class X(elem: String) + +the following companion object is generated: + + object X { + def unapply(x: X): Some[String] = Some(x.elem) + def apply(s: String): X = new X(s) + } + +If the object exists already, only the `apply` and `unapply` methods are +added to it. + +Three restrictions on case classes have been removed. + +1. Case classes can now inherit from other case classes. + +2. Case classes may now be `abstract`. + +3. Case classes may now come with companion objects. + +## Changes in Version 2.6.1 + +_(30-Nov-2007)_ + +#### Mutable variables introduced by pattern binding + +[Mutable variables can now be introduced by a pattern matching definition] +(04-basic-declarations-and-definitions.html#variable-declarations-and-definitions), +just like values can. Examples: + + var (x, y) = if (positive) (1, 2) else (-1, -3) + var hd :: tl = mylist + +#### Self-types + +Self types can now be introduced without defining an alias name for +[`this`](05-classes-and-objects.html#templates). Example: + + class C { + type T <: Trait + trait Trait { this: T => ... } + } + +## Changes in Version 2.6 + +_(27-July-2007)_ + +#### Existential types + +It is now possible to define [existential types](03-types.html#existential-types). +An existential type has the form `T forSome {Q}` where `Q` is a sequence of value and/or +type declarations. Given the class definitions + + class Ref[T] + abstract class Outer { type T } + +one may for example write the following existential types + + Ref[T] forSome { type T <: java.lang.Number } + Ref[x.T] forSome { val x: Outer } + +#### Lazy values + +It is now possible to define lazy value declarations using the new modifier +[`lazy`](04-basic-declarations-and-definitions.html#value-declarations-and-definitions). +A `lazy` value definition evaluates its right hand +side ´e´ the first time the value is accessed. Example: + + import compat.Platform._ + val t0 = currentTime + lazy val t1 = currentTime + val t2 = currentTime + + println("t0 <= t2: " + (t0 <= t2)) //true + println("t1 <= t2: " + (t1 <= t2)) //false (lazy evaluation of t1) + +#### Structural types + +It is now possible to declare structural types using [type refinements] +(03-types.html#compound-types). For example: + + class File(name: String) { + def getName(): String = name + def open() { /*..*/ } + def close() { println("close file") } + } + def test(f: { def getName(): String }) { println(f.getName) } + + test(new File("test.txt")) + test(new java.io.File("test.txt")) + +There’s also a shorthand form for creating values of structural types. +For instance, + + new { def getName() = "aaron" } + +is a shorthand for + + new AnyRef{ def getName() = "aaron" } + +## Changes in Version 2.5 + +_(02-May-2007)_ + +#### Type constructor polymorphism + +_Implemented by Adriaan Moors_ + +[Type parameters](04-basic-declarations-and-definitions.html#type-parameters) +and abstract +[type members](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases) can now also abstract over [type constructors](03-types.html#type-constructors). + +This allows a more precise `Iterable` interface: + + trait Iterable[+T] { + type MyType[+T] <: Iterable[T] // MyType is a type constructor + + def filter(p: T => Boolean): MyType[T] = ... + def map[S](f: T => S): MyType[S] = ... + } + + abstract class List[+T] extends Iterable[T] { + type MyType[+T] = List[T] + } + +This definition of `Iterable` makes explicit that mapping a function +over a certain structure (e.g., a `List`) will yield the same structure +(containing different elements). + +#### Early object initialization + +[Early object initialization](05-classes-and-objects.html#early-definitions) +makes it possible to initialize some fields of an object before any +parent constructors are called. This is particularly useful for +traits, which do not have normal constructor parameters. Example: + + trait Greeting { + val name: String + val msg = "How are you, "+name + } + class C extends { + val name = "Bob" + } with Greeting { + println(msg) + } + +In the code above, the field is initialized before the constructor of is +called. Therefore, field `msg` in class is properly initialized to . + +#### For-comprehensions, revised + +The syntax of [for-comprehensions](06-expressions.html#for-comprehensions-and-for-loops) +has changed. +In the new syntax, generators do not start with a `val` anymore, but filters +start with an `if` (and are called guards). +A semicolon in front of a guard is optional. For example: + + for (val x <- List(1, 2, 3); x % 2 == 0) println(x) + +is now written + + for (x <- List(1, 2, 3) if x % 2 == 0) println(x) + +The old syntax is still available but will be deprecated in the future. + +#### Implicit anonymous functions + +It is now possible to define [anonymous functions using underscores] +(06-expressions.html#placeholder-syntax-for-anonymous-functions) in +parameter position. For instance, the expressions in the left column +are each function values which expand to the anonymous functions on +their right. + + _ + 1 x => x + 1 + _ * _ (x1, x2) => x1 * x2 + (_: int) * 2 (x: int) => (x: int) * 2 + if (_) x else y z => if (z) x else y + _.map(f) x => x.map(f) + _.map(_ + 1) x => x.map(y => y + 1) + +As a special case, a [partially unapplied method](06-expressions.html#method-values) +is now designated `m _`   instead of the previous notation  `&m`. + +The new notation will displace the special syntax forms `.m()` for +abstracting over method receivers and `&m` for treating an unapplied +method as a function value. For the time being, the old syntax forms are +still available, but they will be deprecated in the future. + +#### Pattern matching anonymous functions, refined + +It is now possible to use [case clauses to define a function value] +(08-pattern-matching.html#pattern-matching-anonymous-functions) +directly for functions of arities greater than one. Previously, only +unary functions could be defined that way. Example: + + def scalarProduct(xs: Array[Double], ys: Array[Double]) = + (xs zip ys).foldLeft(0.0) { + case (a, (b, c)) => a + b * c + } + +## Changes in Version 2.4 + +_(09-Mar-2007)_ + +#### Object-local private and protected + +The `private` and `protected` modifiers now accept a +[`[this]` qualifier](05-classes-and-objects.html#modifiers). +A definition ´M´ which is labelled `private[this]` is private, +and in addition can be accessed only from within the current object. +That is, the only legal prefixes for ´M´ are `this` or `´C´.this`. +Analogously, a definition ´M´ which is labelled `protected[this]` is +protected, and in addition can be accessed only from within the current +object. + +#### Tuples, revised + +The syntax for [tuples](06-expressions.html#tuples) has been changed from ´\\{…\\}´ to +´(…)´. For any sequence of types ´T_1 , … , T_n´, + +´(T_1 , … , T_n)´ is a shorthand for `Tuple´n´[´T_1 , … , T_n´]`. + +Analogously, for any sequence of expressions or patterns ´x_1 +, … , x_n´, + +´(x_1 , … , x_n)´ is a shorthand for `Tuple´n´(´x_1 , … , x_n´)`. + +#### Access modifiers for primary constructors + +The primary constructor of a class can now be marked [`private` or `protected`] +(05-classes-and-objects.html#class-definitions). +If such an access modifier is given, it comes between the name of the class and its +value parameters. Example: + + class C[T] private (x: T) { ... } + +#### Annotations + +The support for attributes has been extended and its syntax changed. +Attributes are now called [*annotations*](11-annotations.html). The syntax has +been changed to follow Java’s conventions, e.g. `@attribute` instead of +`[attribute]`. The old syntax is still available but will be deprecated +in the future. + +Annotations are now serialized so that they can be read by compile-time +or run-time tools. Class has two sub-traits which are used to indicate +how annotations are retained. Instances of an annotation class +inheriting from trait will be stored in the generated class files. +Instances of an annotation class inheriting from trait will be visible +to the Scala type-checker in every compilation unit where the annotated +symbol is accessed. + +#### Decidable subtyping + +The implementation of subtyping has been changed to prevent infinite +recursions. +[Termination of subtyping](05-classes-and-objects.html#inheritance-closure) +is now ensured by a new restriction of class graphs to be finitary. + +#### Case classes cannot be abstract + +It is now explicitly ruled out that case classes can be abstract. The +specification was silent on this point before, but did not explain how +abstract case classes were treated. The Scala compiler allowed the +idiom. + +#### New syntax for self aliases and self types + +It is now possible to give an explicit alias name and/or type for the +[self reference](05-classes-and-objects.html#templates) `this`. For instance, in + + class C { self: D => + ... + } + +the name `self` is introduced as an alias for `this` within `C` and the +[self type](05-classes-and-objects.html#class-definitions) of `C` is +assumed to be `D`. This construct is introduced now in order to replace +eventually both the qualified this construct and the clause in Scala. + +#### Assignment Operators + +It is now possible to [combine operators with assignments] +(06-expressions.html#assignment-operators). Example: + + var x: int = 0 + x += 1 + +## Changes in Version 2.3.2 + +_(23-Jan-2007)_ + +#### Extractors + +It is now possible to define patterns independently of case classes, using +`unapply` methods in [extractor objects](08-pattern-matching.html#extractor-patterns). +Here is an example: + + object Twice { + def apply(x:Int): int = x*2 + def unapply(z:Int): Option[int] = if (z%2==0) Some(z/2) else None + } + val x = Twice(21) + x match { case Twice(n) => Console.println(n) } // prints 21 + +In the example, `Twice` is an extractor object with two methods: + +- The `apply` method is used to build even numbers. + +- The `unapply` method is used to decompose an even number; it is in a sense + the reverse of `apply`. `unapply` methods return option types: + `Some(...)` for a match that succeeds, `None` for a match that fails. + Pattern variables are returned as the elements of `Some`. + If there are several variables, they are grouped in a tuple. + +In the second-to-last line, `Twice`’s method is used to construct a number `x`. +In the last line, `x` is tested against the pattern `Twice(n)`. +This pattern succeeds for even numbers and assigns to the variable `n` one half +of the number that was tested. +The pattern match makes use of the `unapply` method of object `Twice`. +More details on extractors can be found in the paper “Matching Objects with +Patterns” by Emir, Odersky and Williams. + +#### Tuples + +A new [lightweight syntax for tuples](06-expressions.html#tuples) has been introduced. +For any sequence of types ´T_1 , … , T_n´, + +´\{T_1 , … , T_n \}´ is a shorthand for `Tuple´n´[´T_1 , … , T_n´]`. + +Analogously, for any sequence of expressions or patterns ´x_1, … , x_n´, + +´\{x_1 , … , x_n \}´ is a shorthand for `Tuple´n´(´x_1 , … , x_n´)`. + +#### Infix operators of greater arities + +It is now possible to use methods which have more than one parameter as +[infix operators](06-expressions.html#infix-operations). In this case, all +method arguments are written as a normal parameter list in parentheses. Example: + + class C { + def +(x: int, y: String) = ... + } + val c = new C + c + (1, "abc") + +#### Deprecated attribute + +A new standard attribute [`deprecated`](11-annotations.html#deprecation-annotations) +is available. If a member definition is marked with this attribute, any +reference to the member will cause a “deprecated” warning message to be emitted. + +## Changes in Version 2.3 + +_(23-Nov-2006)_ + +#### Procedures + +A simplified syntax for [methods returning `unit`] +(04-basic-declarations-and-definitions.html#procedures) has been introduced. +Scala now allows the following shorthands: + +`def f(params)` **for** `def f(params): unit` +`def f(params) { ... }` **for** `def f(params): unit = { ... }` + +#### Type Patterns + +The [syntax of types in patterns](08-pattern-matching.html#type-patterns) has +been refined. +Scala now distinguishes between type variables (starting with a lower case +letter) and types as type arguments in patterns. +Type variables are bound in the pattern. +Other type arguments are, as in previous versions, erased. +The Scala compiler will now issue an “unchecked” warning at places where type +erasure might compromise type-safety. + +#### Standard Types + +The recommended names for the two bottom classes in Scala’s type +hierarchy have changed as follows: + + All ==> Nothing + AllRef ==> Null + +The old names are still available as type aliases. + +## Changes in Version 2.1.8 + +_(23-Aug-2006)_ + +#### Visibility Qualifier for protected + +Protected members can now have a visibility qualifier, e.g. +[`protected[]`](05-classes-and-objects.html#protected). +In particular, one can now simulate package protected access as in Java writing + + protected[P] def X ... + +where would name the package containing `X`. + +#### Relaxation of Private Access + +[Private members of a class](05-classes-and-objects.html#private) can now be +referenced from the companion module of the class and vice versa. + +#### Implicit Lookup + +The lookup method for [implicit definitions](07-implicits.html#implicit-parameters) +has been generalized. +When searching for an implicit definition matching a type ´T´, now are considered + +1. all identifiers accessible without prefix, and + +2. all members of companion modules of classes associated with ´T´. + +(The second clause is more general than before). Here, a class is _associated_ +with a type ´T´ if it is referenced by some part of ´T´, or if it is a +base class of some part of ´T´. +For instance, to find implicit members corresponding to the type + + HashSet[List[Int], String] + +one would now look in the companion modules (aka static parts) of `HashSet`, +`List`, `Int`, and `String`. Before, it was just the static part of . + +#### Tightened Pattern Match + +A typed [pattern match with a singleton type `p.type`](08-pattern-matching.html#type-patterns) +now tests whether the selector value is reference-equal to `p`. Example: + + val p = List(1, 2, 3) + val q = List(1, 2) + val r = q + r match { + case _: p.type => Console.println("p") + case _: q.type => Console.println("q") + } + +This will match the second case and hence will print “q”. Before, the +singleton types were erased to `List`, and therefore the first case would have +matched, which is non-sensical. + +## Changes in Version 2.1.7 + +_(19-Jul-2006)_ + +#### Multi-Line string literals + +It is now possible to write [multi-line string-literals] +(01-lexical-syntax.html#string-literals) enclosed in triple quotes. Example: + + """this is a + multi-line + string literal""" + +No escape substitutions except for unicode escapes are performed in such +string literals. + +#### Closure Syntax + +The syntax of [closures](06-expressions.html#anonymous-functions) +has been slightly restricted. The form + + x: T => E + +is valid only when enclosed in braces, i.e.  `{ x: T => E }`. The +following is illegal, because it might be read as the value x typed with +the type `T => E`: + + val f = x: T => E + +Legal alternatives are: + + val f = { x: T => E } + val f = (x: T) => E + +## Changes in Version 2.1.5 + +_(24-May-2006)_ + +#### Class Literals + +There is a new syntax for [class literals](06-expressions.html#literals): +For any class type ´C´, `classOf[´C´]` designates the run-time +representation of ´C´. + +## Changes in Version 2.0 + +_(12-Mar-2006)_ + +Scala in its second version is different in some details from the first +version of the language. There have been several additions and some old +idioms are no longer supported. This appendix summarizes the main +changes. + +#### New Keywords + +The following three words are now reserved; they cannot be used as +[identifiers](01-lexical-syntax.html#identifiers): + + implicit match requires + +#### Newlines as Statement Separators + +[Newlines](https://www.scala-lang.org/files/archive/spec/2.11/) +can now be used as statement separators in place of semicolons. + +#### Syntax Restrictions + +There are some other situations where old constructs no longer work: + +##### *Pattern matching expressions* + +The `match` keyword now appears only as infix operator between a +selector expression and a number of cases, as in: + + expr match { + case Some(x) => ... + case None => ... + } + +Variants such as ` expr.match {...} ` or just ` match {...} ` are no +longer supported. + +##### *“With” in extends clauses* + +The idiom + + class C with M { ... } + +is no longer supported. A `with` connective is only allowed following an +`extends` clause. For instance, the line above would have to be written + + class C extends AnyRef with M { ... } . + +However, assuming `M` is a [trait](05-classes-and-objects.html#traits), +it is also legal to write + + class C extends M { ... } + +The latter expression is treated as equivalent to + + class C extends S with M { ... } + +where `S` is the superclass of `M`. + +##### *Regular Expression Patterns* + +The only form of regular expression pattern that is currently supported +is a sequence pattern, which might end in a sequence wildcard . Example: + + case List(1, 2, _*) => ... // will match all lists starting with 1, 2, ... + +It is at current not clear whether this is a permanent restriction. We +are evaluating the possibility of re-introducing full regular expression +patterns in Scala. + +#### Selftype Annotations + +The recommended syntax of selftype annotations has changed. + + class C: T extends B { ... } + +becomes + + class C requires T extends B { ... } + +That is, selftypes are now indicated by the new `requires` keyword. The +old syntax is still available but is considered deprecated. + +#### For-comprehensions + +[For-comprehensions](06-expressions.html#for-comprehensions-and-for-loops) +now admit value and pattern definitions. Example: + + for { + val x <- List.range(1, 100) + val y <- List.range(1, x) + val z = x + y + isPrime(z) + } yield Pair(x, y) + +Note the definition  `val z = x + y` as the third item in the +for-comprehension. + +#### Conversions + +The rules for [implicit conversions of methods to functions] +(06-expressions.html#method-conversions) have been tightened. +Previously, a parameterized method used as a value was always +implicitly converted to a function. This could lead to unexpected +results when method arguments where forgotten. Consider for instance the +statement below: + + show(x.toString) + +where `show` is defined as follows: + + def show(x: String) = Console.println(x) . + +Most likely, the programmer forgot to supply an empty argument list `()` +to `toString`. The previous Scala version would treat this code as a +partially applied method, and expand it to: + + show(() => x.toString()) + +As a result, the address of a closure would be printed instead of the +value of `s`. + +Scala version 2.0 will apply a conversion from partially applied method +to function value only if the expected type of the expression is indeed +a function type. For instance, the conversion would not be applied in +the code above because the expected type of `show`’s parameter is +`String`, not a function type. + +The new convention disallows some previously legal code. Example: + + def sum(f: int => double)(a: int, b: int): double = + if (a > b) 0 else f(a) + sum(f)(a + 1, b) + + val sumInts = sum(x => x) // error: missing arguments + +The partial application of `sum` in the last line of the code above will +not be converted to a function type. Instead, the compiler will produce +an error message which states that arguments for method `sum` are +missing. The problem can be fixed by providing an expected type for the +partial application, for instance by annotating the definition of +`sumInts` with its type: + + val sumInts: (int, int) => double = sum(x => x) // OK + +On the other hand, Scala version 2.0 now automatically applies methods +with empty parameter lists to `()` argument lists when necessary. For +instance, the `show` expression above will now be expanded to + + show(x.toString()) . + +Scala version 2.0 also relaxes the rules of overriding with respect to +empty parameter lists. The revised definition of +[_matching members_](05-classes-and-objects.html#class-members) +makes it now possible to override a method with an +explicit, but empty parameter list `()` with a parameterless method, and +_vice versa_. For instance, the following class definition +is now legal: + + class C { + override def toString: String = ... + } + +Previously this definition would have been rejected, because the +`toString` method as inherited from `java.lang.Object` takes an empty +parameter list. + +#### Class Parameters + +A [class parameter](05-classes-and-objects.html#class-definitions) +may now be prefixed by `val` or `var`. + +#### Private Qualifiers + +Previously, Scala had three levels of visibility: +*private*, *protected* and +*public*. There was no way to restrict accesses to members +of the current package, as in Java. + +Scala 2 now defines [access qualifiers](05-classes-and-objects.html#modifiers) +that let one express this level of visibility, among others. In the definition + + private[C] def f(...) + +access to `f` is restricted to all code within the class or package `C` +(which must contain the definition of `f`). + +#### Changes in the Mixin Model + +The model which details [mixin composition of classes] +(05-classes-and-objects.html#templates) has changed significantly. +The main differences are: + +1. We now distinguish between *traits* that are used as + mixin classes and normal classes. The syntax of traits has been + generalized from version 1.0, in that traits are now allowed to have + mutable fields. However, as in version 1.0, traits still may not + have constructor parameters. + +2. Member resolution and super accesses are now both defined in terms + of a *class linearization*. + +3. Scala’s notion of method overloading has been generalized; in + particular, it is now possible to have overloaded variants of the + same method in a subclass and in a superclass, or in several + different mixins. This makes method overloading in Scala + conceptually the same as in Java. + +#### Implicit Parameters + +Views in Scala 1.0 have been replaced by the more general concept of +[implicit parameters](07-implicits.html#implicit-parameters). + +#### Flexible Typing of Pattern Matching + +The new version of Scala implements more flexible typing rules when it +comes to [pattern matching over heterogeneous class hierarchies] +(08-pattern-matching.html#pattern-matching-expressions). +A *heterogeneous class hierarchy* is one where subclasses +inherit a common superclass with different parameter types. With the new +rules in Scala version 2.0 one can perform pattern matches over such +hierarchies with more precise typings that keep track of the information +gained by comparing the types of a selector and a matching pattern. +This gives Scala capabilities analogous to guarded algebraic data types. diff --git a/docs/_spec/README.md b/docs/_spec/README.md new file mode 100644 index 000000000000..d748dddedfd2 --- /dev/null +++ b/docs/_spec/README.md @@ -0,0 +1,45 @@ +# Scala Language Reference + +First of all, the language specification is meant to be correct, precise and clear. + +Second, editing, previewing and generating output for the markdown should be simple and easy. + +Third, we'd like to support different output formats. An html page per chapter with MathJax seems like a good start, as it satisfies the second requirement, and enables the first one. + +## Editing + +We are using Jekyll and [Redcarpet](https://github.com/vmg/redcarpet) to generate the html. + +Check `Gemfile` for the current versions. + +We aim to track the configuration GitHub Pages uses but differences may arise as GitHub Pages evolves. + +## Building + +Travis CI builds the spec automatically after every merged pull release and publishes to https://www.scala-lang.org/files/archive/spec/2.13/. + +To preview locally, run the following commands in the root of your checkout scala/scala: +`bundle install` to install Jekyll and `bundle exec jekyll serve -d build/spec/ -s spec/ -w --baseurl=""` to start it, +and open http://0.0.0.0:4000/ to view the spec. Jekyll will rebuild as you edit the markdown, but make sure to restart it when you change `_config.yml`. + +## General Advice for editors + +- All files must be saved as UTF-8: ensure your editors are configured appropriately. +- Use of the appropriate unicode characters instead of the latex modifiers for accents, etc. is necessary. For example, é instead of `\'e`. +- MathJAX errors will appear within the rendered DOM as span elements with class `mtext` and style attribute `color: red` applied. It is possible to search for this combination in the development tools of the browser of your choice. In chrome, CTRL+F / CMD+F within the inspect element panel allows you to do this. + +### Macro replacements: + +- While MathJAX just support LaTeX style command definition, it is recommended to not use this as it will likely cause issues with preparing the document for PDF or ebook distribution. +- `\SS` (which I could not find defined within the latex source) seems to be closest to `\mathscr{S}` +- `\TYPE` is equivalent to `\boldsymbol{type}' +- As MathJAX has no support for slanted font (latex command \sl), so in all instances this should be replaced with \mathit{} +- The macro \U{ABCD} used for unicode character references can be replaced with \\uABCD. +- The macro \URange{ABCD}{DCBA} used for unicode character ranges can be replaced with \\uABCD-\\uDBCA. +- The macro \commadots can be replaced with ` , … , `. +- There is no adequate replacement for `\textsc{...}` (small caps) in pandoc markdown. While unicode contains a number of small capital letters, it is notably missing Q and X as these glyphs are intended for phonetic spelling, therefore these cannot be reliably used. For now, the best option is to use underscore emphasis and capitalise the text manually, `_LIKE THIS_`. + +### Unicode Character replacements + +- The unicode left and right single quotation marks (‘ and ’ (U+2018 and U+2019, respectively)) have been used in place of ` and ', where the quotation marks are intended to be paired. These can be typed on a mac using Option+] for a left quote and Option+Shift+] for the right quote. +- Similarly for left and right double quotation marks (“ and ” (U+201C and U+201D, respectively)) in place of ". These can be typed on a mac using Option+[ and Option+Shift+]. diff --git a/docs/_spec/_config.yml b/docs/_spec/_config.yml new file mode 100644 index 000000000000..bd1f691c65d0 --- /dev/null +++ b/docs/_spec/_config.yml @@ -0,0 +1,11 @@ +baseurl: /files/archive/spec/2.13 +latestScalaVersion: 2.13 +thisScalaVersion: 2.13 +versionCompareMessage: "an upcoming" +safe: true +lsi: false +highlighter: false +markdown: redcarpet +encoding: utf-8 +redcarpet: + extensions: ["no_intra_emphasis", "fenced_code_blocks", "autolink", "tables", "with_toc_data", "strikethrough", "lax_spacing", "space_after_headers", "superscript", "footnotes"] diff --git a/docs/_spec/_includes/numbering.css b/docs/_spec/_includes/numbering.css new file mode 100644 index 000000000000..2a22ce28b558 --- /dev/null +++ b/docs/_spec/_includes/numbering.css @@ -0,0 +1,60 @@ +h1 { + /* must reset here */ + counter-reset: chapter {{ page.chapter }}; +} +h1:before { + /* and must reset again here */ + counter-reset: chapter {{ page.chapter }}; + content: "Chapter " counter(chapter); + display: block; +} + +h2 { + /* must increment here */ + counter-increment: section; + counter-reset: subsection; +} +h2:before { + /* and must reset again here */ + counter-reset: chapter {{ page.chapter }}; + + content: counter(chapter) "." counter(section) ; + display: inline; + margin-right: 1em; +} +h2:after { + /* can only have one counter-reset per tag, so can't do it in h2/h2:before... */ + counter-reset: example; +} + +h3 { + /* must increment here */ + counter-increment: subsection; +} +h3:before { + /* and must reset again here */ + counter-reset: chapter {{ page.chapter }}; + + content: counter(chapter) "." counter(section) "." counter(subsection); + display: inline; + margin-right: 1em; +} +h3[id*='example'] { + /* must increment here */ + counter-increment: example; + display: inline; +} +h3[id*='example']:before { + /* and must reset again here */ + counter-reset: chapter {{ page.chapter }}; + + content: "Example " counter(chapter) "." counter(section) "." counter(example); + display: inline; + margin-right: 1em; +} + +.no-numbering, .no-numbering:before, .no-numbering:after { + content: normal; + counter-reset: none; + counter-increment: none; +} diff --git a/docs/_spec/_includes/table-of-contents.yml b/docs/_spec/_includes/table-of-contents.yml new file mode 100644 index 000000000000..b70f97da5424 --- /dev/null +++ b/docs/_spec/_includes/table-of-contents.yml @@ -0,0 +1,23 @@ + +
+ +

Table of Contents

+ +
    + {% assign sorted_pages = site.pages | sort:"name" %} + {% for post in sorted_pages %} + + {% if post.chapter >= 0 %} +
  1. + {{ post.title }} +
  2. + {% endif %} + {% endfor %} +
+
+ + diff --git a/docs/_spec/_includes/version-notice.yml b/docs/_spec/_includes/version-notice.yml new file mode 100644 index 000000000000..5a7286631c11 --- /dev/null +++ b/docs/_spec/_includes/version-notice.yml @@ -0,0 +1,3 @@ +{% if site.thisScalaVersion != site.latestScalaVersion %} +
This is the specification of {{ site.versionCompareMessage }} version of Scala. See the Scala {{ site.latestScalaVersion }} spec.
+{% endif %} diff --git a/docs/_spec/_layouts/default.yml b/docs/_spec/_layouts/default.yml new file mode 100644 index 000000000000..2589a105dff2 --- /dev/null +++ b/docs/_spec/_layouts/default.yml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + {{ page.title }} | Scala {{ site.thisScalaVersion }} + + + +
+ +
+ + +
+{% include version-notice.yml %} +{{ content }} +
+ + + + + + + diff --git a/docs/_spec/_layouts/toc.yml b/docs/_spec/_layouts/toc.yml new file mode 100644 index 000000000000..1106222bd088 --- /dev/null +++ b/docs/_spec/_layouts/toc.yml @@ -0,0 +1,34 @@ + + + + + + + + + + {{ page.title }} | Scala {{ site.thisScalaVersion }} + + + + + + + + +
+
+ + Scala Language Specification + Edit at GitHub +
+
Version {{ site.thisScalaVersion }}
+
+
+{% include version-notice.yml %} +{{ content }} +
+ + + + diff --git a/docs/_spec/id_dsa_travis.enc b/docs/_spec/id_dsa_travis.enc new file mode 100644 index 000000000000..6709463580af --- /dev/null +++ b/docs/_spec/id_dsa_travis.enc @@ -0,0 +1,68 @@ +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 diff --git a/docs/_spec/index.md b/docs/_spec/index.md new file mode 100644 index 000000000000..df126db7bd44 --- /dev/null +++ b/docs/_spec/index.md @@ -0,0 +1,55 @@ +--- +title: Scala Language Specification +layout: toc +--- + +{% include table-of-contents.yml %} + +#### Authors and Contributors + +Martin Odersky, Philippe Altherr, Vincent Cremet, Gilles Dubochet, Burak Emir, Philipp Haller, Stéphane Micheloud, Nikolay Mihaylov, Adriaan Moors, Lukas Rytz, Michel Schinz, Erik Stenman, Matthias Zenger + +Markdown Conversion by Iain McGinniss. + +#### Preface + +Scala is a Java-like programming language which unifies +object-oriented and functional programming. It is a pure +object-oriented language in the sense that every value is an +object. Types and behavior of objects are described by +classes. Classes can be composed using mixin composition. Scala is +designed to work seamlessly with less pure but mainstream +object-oriented languages like Java. + +Scala is a functional language in the sense that every function is a +value. Nesting of function definitions and higher-order functions are +naturally supported. Scala also supports a general notion of pattern +matching which can model the algebraic types used in many functional +languages. + +Scala has been designed to interoperate seamlessly with Java. +Scala classes can call Java methods, create Java objects, inherit from Java +classes and implement Java interfaces. None of this requires interface +definitions or glue code. + +Scala has been developed from 2001 in the programming methods +laboratory at EPFL. Version 1.0 was released in November 2003. This +document describes the second version of the language, which was +released in March 2006. It acts as a reference for the language +definition and some core library modules. It is not intended to teach +Scala or its concepts; for this there are [other documents](14-references.html). + +Scala has been a collective effort of many people. The design and the +implementation of version 1.0 was completed by Philippe Altherr, +Vincent Cremet, Gilles Dubochet, Burak Emir, Stéphane Micheloud, +Nikolay Mihaylov, Michel Schinz, Erik Stenman, Matthias Zenger, and +the author. Iulian Dragos, Gilles Dubochet, Philipp Haller, Sean +McDirmid, Lex Spoon, and Geoffrey Washburn joined in the effort to +develop the second version of the language and tools. Gilad Bracha, +Craig Chambers, Erik Ernst, Matthias Felleisen, Shriram Krishnamurti, +Gary Leavens, Sebastian Maneth, Erik Meijer, Klaus Ostermann, Didier +Rémy, Mads Torgersen, and Philip Wadler have shaped the design of +the language through lively and inspiring discussions and comments on +previous versions of this document. The contributors to the Scala +mailing list have also given very useful feedback that helped us +improve the language and its tools. diff --git a/docs/_spec/public/favicon.ico b/docs/_spec/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9eb6ef516488909512715a5b752f0a9fe46c89e9 GIT binary patch literal 6518 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 literal 0 HcmV?d00001 diff --git a/docs/_spec/public/fonts/Heuristica-Bold.woff b/docs/_spec/public/fonts/Heuristica-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..904579683d54e3e033fe1180b36fe9e1a5dba551 GIT binary patch literal 106188 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�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�Ry1~_)C`@ant74wcM{@{B{V8|@wh@Lj-Xctk*8UYM$hENJk8d@yup;? zYgITP7i^JAg}1VN3=GzdFnpVf{Pb}lg9d80Khf8a0rE+npSa`IAl86YmbcFfW9Vsk zUd0cwE$a}ZPgU8Lt!hU%NEt@P!sJN}KS7@vgwMcF>u~%IeP#we(?r)EelX4+t`3dO z$ix4DWn{C5Bh}iV{2f`uZW7!jq&x>XAv4|RVF>GFUk%nyIRhkGXZhI6X~Wt$VYITK za-)}#PI#!|GiW1JjGNHd7h89Cjr_ADM~Sm`MLj5+y|p%AB@BY zA!q9}Ti*aBZGFSoI99@vk@f>81vJWa3Zx+f@>39Woic*0m#C3x)Pm}Rmdsxm%!rKY9G?KHQx?Glv;H&f%Zp%zzi3TvhUcVXZ!ZM>;APAD0 z(7x+87bP+Dq0MBpq|;-l)3<6&uDW)4C*OSC=LVciidp(0T7&1JenioV{HPcqr?og! zAGf~L-4ota;i)3;tcql8G9+Rbvdo=iV$`LHp(CBlvHU=LSyC;|N&xw2=?+_ot|(`n zqcsM79Fnt~9QLp10LDiwl{IxV#&Y9OTzD|)ruANUqUo#G*&3?+keSnnI;u1ZhUx!wphV;1v37E zR^fs}=>Of^vsz6mvvJdT=fi-{T3gli8!B)+$q9q&&?o9vVcT}_>@GcoriMznd-e)= zJHoZJnvI*_Hf5dD2hrDXg0HKNvnc9=NqjJd+`Bd+F!3+Y?C8$yui#S{hn;+eV2)}W zL0U|TP2#Dgky|w+)iiEUU9>U(u1mKJXziMqBjMdLF`3-nuy!NWT5lPNZJNAwgFR)k zm9mv{wqJI!O26UU^TxM2h~>HXbz+G+H(yuV@Vuq)=!JXjwD+Og&3awT=5=Qi<@!12 zP4AM|2|Rjm&yk%@KUY+9x4+Lrn=d~1(ZbZWBR9O(*&|-N{_6L&bCORmkD)nOr~aI3 z4q|*_z-1qVk=HZGgG{JNmi3&K8%&sRYHlk!m;hBZjJ-t-w;(933w)%)D_Lo+7a_aI zgeFo6`Q#;JZGw~HLG#N~6f%wluvCR8oP#mQ16Oj^XY`!aA4?3vfO$9ptt2$092{bo zAdeY85d3KMH$Ab8c&rPl59W+S^Mztb(SwP4nm12v@z6uH9;Mq}4RxEiHu|_kyhd9V z`5kDjO#$A&WDiVMnpKf$R=$OjA~-;uPU0fCWKfrzvyN1~WDU~%x*|V>i%0kZ!Jzgd zl^yqEBJc=O+0*?kuTeaYR4wZ$nhdQGxB?Qlnj3AKS)6rbw4<2|L!7ncOlKUhGUJ@! zO(Lc1w+iJ7tk4k%Z9=dU)@U||{?}WZ=+li;IPRcM6)RMy{XBN>+efUPR5jMtnB{#t zE+-muVpm|O|9tt|M+=ofm!uLO^3s-zuu8n@sE7rQ#wLr#=25hUtJEJ+BqkjpIOt_5 zs#cbwYB+qB?GsF+`5HN3naWaPB{30k_tMvjicCyKn96AWd{ zL<^(}h0{gBy4PG+0IWCY6nui5N*%Us%ANXi{Ca@u!dl05 z$wX(qAIUpMeL&QiE}}D?o@`rZ3ejbTWxu4f_RJ=}o2+1hg?b6mpv3-LAlU2C$)B9g zqgy81K1TJo`V01A$*Ehe$@<82(JtZBgT44qScWwTCs2oFnCkFv;`fKKOtIgNWLl$B zB9=Fvw-ssL+F>KgR0SKzIi15B(O^5|izn?65$)hNez1#()`ac6VgT<+UvB>L|#)Ne|ws;Ph^dR*vMfdlcb^)yK< z3QP=-ITK`DXnZln21fNpBTnPfnS-#J9G+Cg_yLX9?a|UP&5&NNrZq;LR#j76GM2Nr zN3<_%vLW~4bH&O0rs>;ZIoUQEecP?{58M`GGKqWxgDp4^@UwG&aN*t{CXuiu7FE-U zeH3tC-?g-;c#?UZ8bUk87W$}IVK{&mK)P!&%+*G65D#8g6>4auL@RTuQT$*_RZyV^ zZZy1G@$lA>5msk~8MI3Tj0?gxX#t73t!2q62ik@*+ftR)5aYe>#82=zCD@iT0TDtM zTvaVMei4PM^r2P^Ka9f_exd?D(W=5#)vDnaBltQBSNO?w_zPR$+C-o3s%C7WUxT|i zL?9s=Fr`-~98Om(UT!m=lVP{2)b41aHZr0L)hJ-PjlAEp&Fj#n=JnQl~P)@l`M`tjfUIsEPsaKoTRz-MhrkLHotD>|heSR9N4UN`j@TqT@npjWMw`WIYYma3{ zw$vOGB+u#RXfheFei$Pi=9kO~?)4 zjgAr=G~NQwB)(Q%MQlx9KC`eI_VBSRKMlC0*k1So)r^7s;%H5VD@|G~n5U2k6%Jf5 zKVXQ_$Z8va$bGH2+$bord}&_LB2j6&L-d>!qEE9eHVp<_2ChwXm8}*QR)j2!~^ z%)^~Xyd~&**5@Z>;baSJZ-+05VSCX&Q2qePcar)m(N>1~SSB+L#(`uzPl)<31c~er zp@d00h>-}Uig0S#&jPkeo|JCcOzk{H{#9XK7(q`P^VF2FJzevK#z^Fv*jSkTrcsw>z|I`@|0fs{wjpif` z#fPUDk4;5>>iFCw(IGQuu0uLc&(6pQlT%2=Os`VctW^K3aJ=41DXn14`YI}hx(n&4 z18CeLR32jec1Elv!#7SeXmpzc4c{oW3v>FM9n_T`oXi3a;C9I4prDpD;wswi0raxG zkAx#M^DS*ugH%->u1$26sVZ%QI;PQjebIb7tS^6s$0d!NofggL9XJsnm%w!lLP#&B zNZU?(>>t(8vOxb`C5BpdwEL89L=?2*hRt+YI@qQE?R%#@&3|g~1MMSRVwWp#W9HZt zm1ph)G)ww*>0IS=NY+6r3tnW9AubHLkq%Wv+s&R1!dZ$Pq?{}AiQ|G7$#tOr!U*Ao z(XxhM3&_H?iLNraTJdoSXANaL$0eOt$T@1H0G>aCMd=ly_ujE*CK%(tK}91m34vlK%VuS zmrP4~*ENPDm=~cQAh76Aan?Bl=L=@=T=H6>qAo_bw|rs<(b*A1_!FZ6N){Nr7a;ND(e^1dB7ApvheOtV$L$< zgJQx(Tp}-9o^UZf_O%Z@0`z6~=7S``;)`88W^v>PAH}?y6BJMC;e@CTQy@{|*%fw$ zg)wCQx+>%z>B6~mm9m@2S!fDvS(8QHd|W6y7lf%^`6gL@82P^js8Lb@p<^mJjD_p< zC5K6kBf3isOQJ2KW!lo}@&!B-VJSA%ssc_DZ;0?aBMi&QkdB~GU1_V)?Po)U#`Yo%Yqc&a150zdm8jB?_qsEa9XaA;c-LlE5^Vh zJaV6A{)>uIMabL>xAz>Ef`CfF@vs^feek*>ri(;NJ7!w|#D**cGHsYEQiw_5K3T45 z4ns8;L>@J2n3V%)V`y0^zfHt#vBIeiQ~{Wk8nwoSe7J7#7Qz>2rnj%lL?6Ct&u%sz zJ@0}GuFND^r7yX=k=-;Am$SzecBel-L~GU$U7=EN_RSj%%EC~&$V8$6Yj$T{#v^yn z$Fa{2HJCcI5BSh7P=)heKp~h5X{nl0#Q!O47uw-Lza7>((Affpk&S~W7cd4EEem`R zI|xF=4>QDBhCUZLfwe%N%ORxCbd)WY5j!pN6Ju&i97M%gin#a!dTXKW>UIg(tfw|} z<><&Ry@_?MuWWnp?Vds{>G?=7dEtbrmSGg-O$oa)liTxD@xlWe()Fk-VzYZajB<2v z<~*$@xvo4pb##NtpmfZbBY~99R(d=3gGQEqLw^n9xLhRT^#$NLVnATH79qec@BvoP z0#l$FjdKM<)dXOCjV9DXEq6?%=R6_I-D`);KT6P}&K{$TCVMm{^|nsba|%WmetrRt?EMC8UT+Sr=`%i*Tsw zkicnh7Xd&-&&??0>*#$hi;O81Z=B&f>8gSo)3f z0yLJp>2s;(6!dt@&T4h8L4bMi158jng$tk{pg0S4fOuI)lIW8x`J_vn?#(hl5l)o{ z&?m!CcpTg|1^d|Mt;jnFBKIML*>um5gL@go^&jLg-SbBDvW6sEV}%*?WTpsr1@th( zE(m)>tvqo8*P-)U3Vfu%Z()U5?j#tiVf5b?wiVg}8H}@RD>@4y$jxwt=Ud^q;#q*> z+dR8vtNb_`!-_$ukT-Bm*0r;NkEHo+4WTxJ1~k^-3tThVn1bgSt1vbWtFr-&$La>Z zjhmxtW8)iv&yo$INo=s${h6XTz!;5;#wJ#Q!7x*8Q$tgQlH{J+cVwGj>@tFe0mOp! zn1v>_|H*-TD5X)EO^IC!y~plb9|-2Jt@=wvzh=7n@X&_v1LuuyJ=I4H)Aff(ri1Uj zXmsliZzvzP|K*l=dcZ!IbZnY72FC}o@qaM>=#qOX>3(k(DnB*8cI2T7IYS0qt{sh<>uX@5 z?>Wxu4rFiwh!q1qQ37Wmx%XC1h{n-}+}5Vo7wVM%$_|S0IkBim$UBniOcvtaGUHE{ z$1jT*0u$>u9~cj8yzf(6#~U-Z-*ouGx49EhwM&%=PZcjJ%%n&6IIlU9n>8zxn=3QB zHc%bxXS}8k%CMk+#U>TMg7Zg)k!AL=-RF;N=lqd91u}IK(1=%k{&*g0%R2yPg!-k( z4M|mngnog*j93(y^K;InryQ_|Ef~YjuFJJl7MC_Rr|e*h%vFOqiwXUwr7B8{G{P6q z4vtoE83r$c)~1L&GEscxUwV*7I1zaSEzSnp(l)?V9ENKXUAtYyCaoP7DN!}r;fBmf zjFq9RKbV6F5#b<7Rj4LlbBv@jU}Et>41E?RIUjALQjFa~#vBbcMhS6Xo=&y6iKsir zk{C?nyhD2TnD(ctElQhtcYJ(&ebDYG`NR5RI#bDo4Sue`1vXz$DjFhOk<|s&M*2ep z2w?Gt_S8Ev8%7*?pTjgUuJGk5liKt|jQd0_Vw!ZNV*&0eFR7XT4Raa&GBrT`JJs~V zm}IMh3ylff17HZ$&uidRcS?hg9$}R1RqR*7{hU=q!OC~|#|K~eEwNIKEN__?k^?9C z`27DP?mOTkxvTqUW_zF6-h0!owrE$&CM5rufb@Uw_nXJ0Rl{xqle9FnEI$_T1_Y~P z(8W6(MuRanVqkB((iR{93|GVs8k26HpLg`dMlMX}Yq65cwX2XC989Of25V&Lb%?-l zCtFPfC+1!bjB zv_x(1)vW{urO*GIm_7OA$-e))osv6EF*97H=XBWfwsHOs|AmNz2kyI1O34`9Pj%Ku z^um4LNhu*<&UaXkNef32*=qRX@6k6B(L9X>1$FcvU(q74=XEJTDqEjL_aru!_e?(ijj5L zJ3)u}neQ+JR|0hyLQ4=x0*kD}v__^b>neikvS9SN@L;Z&0-!>u=^OSqoe#z zSw}gadvfAi|9J;pM>7!{9ipMZTEn)_59-Sgic9qH^Ck}>b9(WL_kDO@0}9OQp`l3B zU?#K03M=4WL2?ikSVM^yy77(>=tm|m?Dq_cDjI%E^G#RFKqQw(6AzBGb4C_O{a7jJ!XGp4l(kx%Kf$!N;(BE3(oUf9`88rS95mswwUUZ?FkIVGEv*xB8mmp{95#Y-2%Q?Rrxv9O>(y+%mO$ z&!J1pDGsNjxz0e%GaohM-C$F>eygOp|*))k0dv?elYgc~kp&vg~iSBdcxndyIjPq_6%3V zPOf`xT-Oc zw6^||a^$T5FLv&<(La-*VH`Phm8)U2$4Qa}K`>!O#@Q0w<>qM}v}OHcpjooGQK7}w zLxYCr>7#umQBE$O$NY%7oY55Y)uA;TX%l^wix>LsgP~pdxHIk7#q_R0i$Ry@G}I>R za}})Dn_+!Y%==~JU#D5dPjb{L0&rCC<6v_UH7~A&Qc718%(98U{m*eib~}(a2;_C3 zGUo}-kS&PlR3W^!;Hr{jTa%f$@xc&{n%e9R`L$v(^{~s6+#V#QAcf-!At^W`l9JP$ z1~WCBy9%f_&Zm4A2OL(jR@>I<^^`EIJ^ZF?vI%RDQ0w2uZ>)r4Q9+RP`(*R8k3V=j zQifHq1erSXMTX`Pru$)cZ3QBDW3^f^j*Dv=GI`3rNOlv37xjC(-GZxy?Rtv^( zz22d+Sgb7c6E>6#&MtJdT@|r*YN3xRi)>#Fl*JHL7DJVVEL9eB8(5wh)$+{n^IKEk8(%R8bdYU>sSM|#?F3t*;k_=%R`v(sfUG2PfX6Pd*Pl( zk8Ef)odLJ3TC|msjV^39*|$>J=(wbMwGn^#cibC^uBk95sFx|_6tp%&T}qg-iU%W1 zI=GwA;YJ~W>AALk7iu6IhBvAev2Y5UIugoer2d@XoVV~^Ge(6Hh!eu3noWt*fq_QM zb(f4$24w0OUnCOfkCePy)yhY6XkeQc9gZFe5A+wV&2hzxW)41Z&-qsdl4CopBT3uV z^t6Z7Xyb{(&e8CMhrRXJ zCnvuCpI1=!O~Cy)ux}ETaq6V#;OBV-ZAclD%o|5y25H^-%`*rD)Fa`m1=3p*Q z*wRIz{#3yA!#SsliaJ-LtyR#7O>o5{NXyHw@()@=muh1Q2`{~><8%|9{BuIhn=blF zQM2^#G9mY|FPF6kjUi*~XHur=>tqC7pUg*U5(4#XoI<^Ln|g`oquyFHS{+tb3j2gM z8qpbO^1O6dYwAiIkYfoB<<)Xd6u}iuwQ)s58j6OuV7H~PP|4nbZqVBo5s5=!!GOCK z32uWPgMNPS{6N_VLdzRUQk*+GxgAwu`^g{;^!OH|s+}GW7}E>+{f<~|m*<0)4_K^z zm*ddZ$lS}%zb-v{=R?PD38e)8Sl{i3%j5A#+O9pvd+lXMYd2gmwTG@D>^C(pCAf`5 zDtz*eGa(=AQ*=#zotzowc~KibC8$w&7mN{~-A3Z(ieSWCi(-r?GjzGsGO}V58trUC zbz&5@no*3DMWSS5DY9}@OomY(Ul!NV` z9ENF!Pvk~MMS*sf3G?%ja1qlc@CsXUSGY*rIHxNdDHJ=@%i#&F-keC1Ga3)lk^zpB zqIx{J_4yOHdFm4+lLZJ|riZm{Xi24U!dTa)T>)OiQ`!awxAc5C;!j(G*?6M9@6EUU zxDyfyc}JQ_|NdIMpY>c7D;Sg7{WHVi3&cYJ|!mwJM#p>YI=RRIMnX|iX6m%VH5(5}tZVBF) zj7FVmsNvT&by^&#%PBycdc*!J=gf0)b8yu7@4Du<&EFZ*_1!jn&(h1B)0Yh=Oap;y zVg{d;{k!s;@)|=tr2Bv6W#+)uZ~vcviWaWE=(26`o%hZ;x6?f;#=0h8ZL`dsHCS0q zr*=s=i%#p#9#8>TB{sxlm>Z~WUkK5|K618A@G=Q1nqUP#$n8t%mE5K*1v~W+uu}&p zJ(ECy^(99DOts$a4UudUlhE?S6NK)*5FU?`H>xvk0_xX6HBlzQw%Dfd9WkNXC^->byH*dG#pZjl%m_FQ`t`FtO-4;WMK1HD#Z##k-Q=@CnH zlEJe^G+Bw@XSyT!3;vNP;_ Xyc@|D+H99SS85pph4!GI$R*f^phbB8h2v)aeh7# zPr{2nem>uqJ1w{m_wXP1PAA5nNUDRwN{qkzuIyGA>LgSwj-YuSZmt>QU*T_b!u)BK zRx&H1z&^jS6;PtUojYxW z!Zc_Kt`CiD2sQ?-Tz^HbhU=qIU}s>>C~#N6*M;ZkxMA?gF&#MqoV=nF0aoGUZ?%SC z8^*HQZ31t7M2P^ud6yCaes??A`^SecEECtocM2iiUI^{V!#do_Fq(th0#jy2*{Gb) zS;NQ+zDSv5NI;!wQ8dD`Xo30y(K4b1R|ySov1~$k+y&oAqG%(Va8?{6sqJMNax3#B z>n?&oHj$y#fNaKBtQZF?lf%LU+cX>Syc_OTqrJ1uytGQ4mPV{x9Q z=!WHUdvf{8s8SlmS=8^3(m6Ns^k%o;xx$v=C{69x0{Tr+f91`gP=c7N{$fr`@!&kC>SPz|FAmZ#M&LS zsRA)927H(4c7e)$hyZMq6ZlrPtLd`7ldRmDMs0+ZHZ`-l`YR@1ToPnEe8p9PqJgv6 zV>g6j3HDsIIi}rdetN}yADxly@w@hCh~KzH)OLAoU^m~PCkscvVW77scyaQiK5Sq!3sfrNrezMQgUETk4**hGF%_Zm04LL9IU72^PUN~h4s}N zLkSAsa^La(JrCJeM? zb#L+N^HBSInawFP58gMp@eu}*Uw5R701Ee~l^uo4)sP!{uL zvA|1x;9cc;jFvq4ci;Qry>x-hNWnzLwq|egK5l&L_b2Otk*DEuB>{s+Q1N)`3UQUsgIpi zO8L#Y@&2LL40_JveQ_h#G$w?|quxq!W}D6&NDU5!Lq*f@8=IzbGhUF`2hL;P@mp)* z_$?7L`(RL`O(wNm=t0(On%;K20KRr?ZuV!^|C*hRM=D02bR zvXJg_m;kH*el!J-(fq(_7l#ORXN;`Y(#5>HcXFf2=2Z@LE6 zwG$-M7pR~IZBDerEzNd?#QX259~l#tJ|~3Pt)|=e+`t-NbE8}i(Rl2l*PMI!-igt7 zyo-!o&i`N>j1PR63*-p2Q%PQqz}Wp>n^xI$CTQyp7irxQ!kxNnQA)zUAs)*@v>rbh zyVps_9>MU4shzaEqEl*0uhi0zTxC^yeQCS@2|n^EdvkKggh|SseEM6@-G2L2;+#9& zv^Tg0Yh^MZ2L$_<)~7L`o-{H_W)LP4Bnc!zr-}CMlX0&6BzMv8F%^{-ekTt;_c&qv z7$~bGEwG zJj*^lduV4-f#o=U!%~ACZv3au!_l+JCMb`mNZkEmnN}9rE%ngogNHz-dju0ED0biDD+}W2Ax#I2AXS2HM|YsNwTEeRXG}ZPZBSht8l$4Px)h z=68)2FQ<8zb^1U$PdD>gi_OBD1?@*(RMk6p((N+8^uO1$VfW3c5N_KI4&Lt!8!VseD~{YpSO8eqd1^tR9$RwtS~^;^ zgJn?ifqwhdY6ZIl`#CkZCX^mqJam3MGn_FN%Ewkk)0;SF(d96!@bqX7+d=LohQ~e^ zBqmCNXjTNbMy0NmS4XIbrc(-%wUe~_> z@7OyqvhBv}Dqu(4vXiCgCrdH4-?}$*8nR*GFRh=OR;@?4`aTuD;peJL&&VsHu45U- zvnzsw)=#=NF>;WnL6l>GUqqvcxMl(SqFAjVx~NnD*L>b!6r7~dvM^;b=^fs9BGpgP z%$!oPf?F;}KNhsW9;;IA6tv_TzTuTsEjokGkh8NZYFnJ%fk2M;NT8|A6Q}@Rh^kgDOi9ML2yh(O|s6gbn6UQ2~>s_qp7&y zC^C8_L`hmCO9{s!CN1P>Q}lU*D+jA!q^n@8EEwo2V1wiYhTwHv38Mo!Vcz0*^wVl= zQWukJA0jq7d(2K7xo-Lac@0!avER+)*wTJbQW4jEFU#zB@t*06%1NhN@AnqBMx0|6 zVMJD5f3dBf58idx#S_OX75#|D2P<7Unf+|29P2btD!0eCCx1pDA9T2G6{HUErCAo)0{8es5z75PBc|_P)x(UP|a2R@$d4 z2-ebtGN$Gg{Cx5X#MHcypRZhqn3@;h74Khpg@UPh;T0WB%?mEP;>xu!HLszVn#V7J ze-9~`nn#aOOw9$v)I10GPkZ-)CL5aGe*iyd2;v^}YWBio4m71JFCjavL%Xi|6XI(Q z50WC-O@`QXl(V|N+;y7_viYz5M{-8*&8q=zVr^X?sja(`eVl0>LOwlqd8fASP`kG7 zP`kEn8X5TLh6?P&?UjWcWJkCVJATLqynHb^+7Oe#$yYs3ncan*GP{f5&PAlmZZEL- zHkzJR-nQ3BYVO1XnBXu?8TiDCP319Ce|LSG<4x zSiAo2;-kloKTrML<#PSqB~5AYB3gg9SD0^Z-*E`}?|@SpI0w(<1t|-LR_faZOx!;{ zv4yCtLp$J6+xC(oQ|Z7#__s-_^p2A%y)74#D!tgIRq3sj<+d4>drp^$wEqZv$g&&CN0^_0)vuu()y zS^soUEvYZjM{54|v9q+MSE>2KW+SbA8!Y@2Yn6pMRWLiFQ&}-gcef z-hJvi!R^2UzG$sJMU9r|ZA#<77cEC-5*NB8VEkR0sv`&Bjf_C5dBvTimaw?Bhg!n5 z@?AGtVCc5F6tU0cHd1TY?=Dxmb6rh(O-NbfETuL~^&PU>`7nIq#GvCQ$^={HUdLwYQ6p-t(BbFoGsK+b|w-W0Hw*5>Mc6uFE0B@=fpAq1eWiCgI4(_MS z44Y(HgR&kdgC3Y6x*XH7{J4*ZQzZ~5&Tc#c>pPws^n;*}6G30lvJot#e<+#vA`#>=1^oP~i}gLtvR zyepmQQ>O=HRrvv3(oR?Tc`Ju6zh}2)&ZslkF>N66Vatd1t0mkX4X#;2%+<;nubagj zf(Ob=A8E^Wa_ur=*;c=mocAagm?cSSRhIK)lJB zXzr9!d@#N`sL;@g9#S>ZLn;n;W3ZM({Je7rdq~yzc~{Mi2%vby`-4NoVk7dSI~K1E ztpI9s;my+z4y0@jJib1~Os6wzw}e`OoL5&GKXmnkTB8rtX*lHO3Zk&hcK3l=rTNoJ zG~b^f@|wE&Q^yWt-D-)AQJfT|ZWS|-s4-qE^+*g>7DCkM8vFCOV#Q7wYZ>977W~x4 zdi)8nv1w{!(-j4Xq_S3uacRvu*MhpvwFKt3gmtb3e%@5TkX{k5c)!1-)VUVA>s$-P z{?f{oOk*;#J&US0AA*PUV@gG`xC+>?##!dR?59 z%_`WFmxC;gFh7xHsX(!*^ZgBkpA4WhS!fct$?j8bp>#<$KlW*>F#%Yn0Y$Lp!%P5! zVay9$j6$>MO*nO2iA1ywb1~br7$*K;isdRTQrQyyE>@y(3=fOtpTRc)`sT z;J96Z*42b*WfPgixz0)?U$a_F>Vt$8 z@)KWEej+C&7dWnNSh6@sPaLHu0@4#f>51YM@5kaqdgfV|J5pB554?}Y;-}S#$Gj*t zfo>^j$KVWW)r+&&2bW}gq^m*uIDNG z@}My9OXlMgh?(dCMzSLYZ~?dFGb!-3PMStLg_Ytwzez{qfh_Aq1|P2i_~)GxUY6AY{xA{*v7dT2l!Ge+eIEs|@&JCMVetQW~uC*-oa7O}xP_K7bD zzj)_$d-m$>jY0S9?&0eA_-%Kv*E;vR zLiJDfopWTiRNi|1^+TBf&A{$gzG%u7g|l$fn7jktxMcWl9GYPhPBs-9r&4zI5!iiHFwBOi{fnu6u)PVHT5R}KbHcHQOdxK3);l&(`? zI+FJ6;)6of8xp*}P8n)BE9^O~UbUU8^_}B)lj73z#(rBF%Q-W#VtIx8p=Fa>Mp%|s ztg^496{{#SmsN$S!5nrZp4l!jEL+UlyBcXD_fBL^3FwC^WpLVm_n z#0oo%{PM6p6Ka^3f{Q-lcqWF6rzK^?o|BTXA5nY})x_n&P@5aCEx4+-TrL9Si=W7A zxPb3moh8%zG1t^f3Tb>47>yMW3Xj9XIVGFGgBvTQSdVpR8f6v(s+dXO*OUe$+(=dL z7`<9QJ@SSzP1vgB5cDm5@5IH|z3RL`MhM*Yam$N-Y5m__Kb%MfssAR^j2UEZ?8av2 z&~dZZB@Os;jEzTZ_Chhm*~s3gV8p6kY>1(B!T5siH@0?vgvv=tA4a+`^Lj&NfMyOT zuhfX@dY`K|Gxt+UX!WADf{=pD&1|7nA_e8AM zTdxynzkrVuUBX{yy;f&1rK$!&;NG0h>bcO;Z$5Zsj5LtoYm#RY#RRz!*rg z!YxMtyFUZ%$#Ht$sfB#|yj;rnk){@mWW`{Xiv+Dw1k(k_tmM+g*bYk~KSsGOI|iSm z;5P85$B-&z_q|bfzRwXVn&CV3fOSf9m(v}nnEuBU&8IKD(RV1H$x;({u5mbFIlx^x z;`UD(vsztl-$!i!G?>tU-)_)E-}hEu2w!qs>iky>e(Q)<-W%&Rk+0mdFTxRAc@yl5 zXMrd8vr|lKFVbPAt`vjrC5883;c`4S$!_WQwu?hCfwdK&6`~6X`t?KrV^&y*KhFnn zBP>vRND8j%m4!js9v-NOqo)=IO%=PQnl1P`zS`boK5enOQ|a05?JgJ= zt4n=@^6msSBS}ml&+Z+Bmjk0s((iC$3XkKR60@5o?e{mugmd20SD5RZTG3Vx4dhls zt`So}nU$~=9%`*&1+&BqRuGJVR;`*+KB4OH4|0Mgrxje|nr|9noF~wn3Z{}?N8K78 zDH?fetQHg83Lt}yD^v;v8?Vs@HHoqQbfFe0C9lvV^zU(5xL8g5mP~42WTek`*~gPX z_C`l3Sy>UFws0&LA063dtQyA0QotYgEYE18q_^G=<-D8#?NmFYfT|NoNs*9qOYh0H zn`k5AbEXD9&b)QVW<;160?@;E`eyS=-)wXwtKdjhx*>XQefAHhchUB$dPL9;Qw7+Y z_nl-urSnFjeT7bc6OZb>;>S1yidZRDNCzS*fKpphC#I}yn|+3E1NV?^_eZPy zgs}5zjSyg;gT8G^fKVVW4G|!Wucih4nNm!Ig$B>?bX!*VlB% zyW+YP2um+5(>lHnfla!G#E}1y;(tg*6ZZ97rKb%>7weV+_KB#Q>E1tmY9Y01sf=A5 z=EcV84Q?vx1~(Ocp0A*@S;cD$uB$h=sZ_zRo!;Q4`!+rLOA|=gbv3$K5h~tbzZ+r; z4{fyPO=lyh?t60;$Qj~XjUYy4zk+iWXrI$^bt*{&cOqI@h|$1sUElAZYay|YF6^r& z_Lf)(Q~3&nGaz5oeGu~%S?t=O$QAN64DvOo#%V#&tcnUW3l_~*v6DrEt2oHd0~^EY z!Bv$+4Ij!NTA$Y1_)1mD)x*`(Xc0|Ib+6q8B!fk*1%mXBDahdr?JyNoOcpK8Bg$w1?)p!PWq#gSMsX_PwFTGiW+78%j0yV# z=}f5$ba2(%Yc(>G%bQTHI$ImuJR7L6tqc)wS48C4ft^G|YNn-c$kG*W7CX{)T4-32 z5V;l3d+I|Sk)p9Cxds{b$$N;sx^($j&Fi=?GW}q&{(>PjWh4~mL^=-|7Z*x_jFUo& zl#Cd3lAD!FCi`N`#1P0DJ$MEt+Vdu4h0P)Dx8boa&c{6iAV-O|yC8+~2xSlKDh>z> z0cSK*A~`3p;~-e}fhbstcrq`i92A@4ptHqjJk&UJnh<(@lUg~kD1mQDo;PvEh6cf( z*Cpg~y-UL>&q&;#w4}!N+%Wyn{Xyr_L1*J_FNtMSr8kyd^SJ(;Ircp8=A8r0m$N^O zk0qn-VYA)Lmd`mjntt7*p8WZH=FUGl`<6)JO%GPm+TF2K<#I~5LqNyRf&R%dWz4=O z7zTctb!L~_H?U%bQp&cHQYqk6OHJ~)v44i>q1_w=*cRfsf5C&;8vZDS^C7^pAlrVy z`GiH#gu-bO;_RYEL&>r-$N)b+l1Med>a#l9P(*(y6-@Leo9_4DHGS?wk7xRdbH(UI z?~M-CqPxyzhYlUf^q$PDH}mhHBJ{?-po4emY~^Ov`bPx{}-XLbzOb=MSyD<}J3 zPkCmB@JtPMW$$4qtBfM9Pelw=#415`Ae=J32B(xeoPtc#Xmd-W!!5XJ3zdNs)_#(3 zk{4QmNWDoqnSUypcJ>uUyIChsTH=Xy0koVrx_-7n?3s2F(mI(ZJ~}fS&FXTQ_pO(M z+)nS@iT)!Me&QchjD&pDNBC%r*@}2S^_6@y4WtAIFDqNAZEJAUNQa}w2}kvVn@-(~ z`FN_|sQB!_k_&PADn)Z}k?pX1fM*0GllWu(f>;N~+bQ`6noVg-3f3TMpYCGEdKSJM zNB`U}VAIiQnK_%I33TA9p2vdY4YB+by3j?<-F!~-P4;aBFX%_@3j6eW>;285NvW`(TC3jQv}%RDiB{N~ z+IT^v=LGjvKF5BN`!|9=z^E$zZQbLsb;e^;kH^`m_#Z_Cg|o#UpfwOA=W!2f=+1{) ziDNu$DxSGr_x4Wy+VQQBgpnUJ0A$9=13KkiVnA9n~O{i&V( zxTBN(s3bq(VS5u(+IH_Dk%LcpynWVcfuz7WrEH!;g#?>YxCF{2s-!}dwh$i+58uwF zi}wcVC)P@TtRB4S{#8mT_OCAw^1guWB`;pHvVzuC+z2{pl3AAPDkkxgWt41clGH}B zsW;LLp&7-3sfDJT3AqJBbhMQZ1rcBM_k&@&}S)@xK~nNiKHCyR0QQVv{ZbYa|9r;NTM7Ai68NNmDVu+Fj!H(cvaYkIkB%i)8A zJN8#&e!tG;DozHc*Ok}w$FCf^dDu0RjwC>;{_Hk3-JRED=UxFamB9EMktuRk>A=+q zA*d3OoRCK@SQ0IDHKtM=OOz0ofF@)}I+9KxOBX99E5xN-Rd5oj+zX`x5Y4@as&khT zAX5pDtui0yx!^0{U&`$u^HVZ}G# zz6riSR`bo<&=dG!+wywO!V~bGy(b{!2JWSP0Q>K49K)PEnlt$U6jKCi^>tvo!!WZ~ z%RRf|N(QqD_Iw!)!bl?W+Pp0&80mQy_U-~xY>hCW*J&l9XsgI+K-khtk6p{GB%P5r35dS@ck(0+$>uCy`hLqU)E>g<-~Ca5=oZqygy)*CdCVf)sc$ z!_s61TcHtJ2CIvh5Ca~sGbXbmtei45U`ALHqnwo~(;05q+44Ic&z!TR6uIo->7zT} zdhygnf9CU)&a+%_*`0eX_VeW}nf~yd`;Q%b^xAzPU-}IfTaUuplXcVF0(|)MThRl& z8fNnnvhvw8@uA4JS9KV-!|QMWoPT2^I63uQ%%{6HcbNXwnvQ^1cB`&Yy-y#_{A zAfrmGXH;!p`xfxEf3EVi>CCEUH?uuG&8+QjlX-F9ZTs7FO#Np!rpaE%gneNjq3d5l zDm~L4(dY{?qW1bH<@JAr(1~rYe{DUZlKb7h3w)On^BC>7g)I?V<|0}|Gxoa;gJMD# z)1@sgWQ1ZEyvU4^8A357jh7_@Y!`yHmVxYv4O-X}8+bW?R18(n{9)s?JZ;D<*$sm8 zQ*KWYoJ~A|r2s=%`z5DP?F+0M3-l*rNPqFUM`PY8zU02=migSmp32!J7 z-QV1{$DZ*1UFp1MJ@(AdCpqJd$F977Kk*-yf3a-ik_12VFPIj`$VlsM4FwoQ!dMcV z8wg`DGQ&t1+AXxmWB*&xkM(0dN)KN}PJ_nAxM^$}=cH{!eTbY@YD#Ip;QX|ObK4`) zoD4k^2KpornVm7lFk%NhFmHp;@ny&<`TRg4w;(!!Lji|2eU@Ouw`8P18T4zl8UdKf6jEK+p_I8h`>xs|!S@K@SJ96r|a(53YuG0Bj zrt_Q0oL=0XQ{e3A9PV{aIL6CmUH#8wOn0?)bzq&gn)D{8HfTF?&;M)Bs(yB}8rd8h zl#%E4D4Cb9J+JCI=XD{hS5ej*ERl;}wdK@*7J8%Ho17AS&k3qGaK9lVeCF0U9vai6 zb87pGWJYwY3TL-gQ$4R0nO8Rt+S+SX*w9+t4{P-w%sjJlX6Ei^m^;rfpEfh5Gc*2d zmy5_2nb&pPuVG#bu(Rl%R~|Qi>M@x*V=}4c^&!yB*T_2Kqhwysw&&$LyA><-sxz>k z)Pu5a){}V&?Rojv>EGp-XSgqcf0tq&?!=~61;!Dh6$KC{T@jsckm^FUQPu1aAS;^La5{nw z#j^SdO%6irq&|Y3G|s$_X^knJvWGdj1rmC_VyUuFmZLfo>XzVxF#Q5O&oLGTjY@xT zv4Nglj?!hat5^7-@M&GZ94nQ_2uO)j60nc9yMUQhq<~}$PuFsVL5z9E1aSnT*|d>( zW<8C>m17+J%5$9O?Jgb~@S6ASq7Ba~LuY6+Ud%cR_Hrmc=)&V0T|nPwC8lQ z-E(?1%qo{$zAg#gT8+MQnkZBzJ<`>~mcis8ooEpFFwd_7z+d9U@<}NOLKy&|3>1P8OzyM~kyJD)$wJDkm7UZ$SI{a1lP1=b!J zp<NKeuPe)Z<4vH)L6c1Ncwm?*R8dUtxiKSI~8sr6RsN2=2 zIG4>+XytOHbXrdXxQGA1cSuhIY+vBX=JF*~^EpiHBV7$v-dA#6R_|1NTGW?4W6R=O zQ&)Bef{#9b=i-_=F3i3{)xen6cs~FPjl|v-O2b2t=z7=-IRucmiQfn4as)SMH zu4ZORz|sAx^gNnR^7>XiKs2mYM< z0x&;+l38GRVW8FlqQ-a4F3K0um{a2FCo$VHR$51MU;teLMY0)0kZNyWl?tgSyYAX3 z!+{gl(716?w@p9z>Ye!_XWnfO2vtwS<8`;YeKsl?evdPoxFML2D-}?>?_PROemYO8 zpNf&bRg>jdX6(VKt8T(`jin!Ok8=xrPHX18Oa%0F2KfJ_#NKeCx69t#19!mSh*Ou2 z_(g7QM>w~Z!C99hO!plVOaIKhmi-^FgMY<27#oahRyD4^^^eQn8CQN2#)aoN-zNK^ zr(X)znaQelcf9l(ndJ;?wz=eC2z|8NZQM_RKA+>9%RiFoql_oI@$m$D8&7_DLZ*)$ zc6N>~`mmyQ#dz4xc-S=p1g;(e1cW|CnLbZ(`sM%aq7N(jHa;F_Z{r~~%$obaAO8cl z2Yp|xWp4LJIm4cOmF05w$j?|qeSq|9`3U=8WIP{f$4}1O6|Aie zRpUu-d_3Wvb%snIJ2ysjM*aoW8B?G$J|XLjDA5@^RpW_ld_11s#*+t~LFmJNp2l1W zeGb5QPVZ2Z#@vRUWXCMe{qwZ{Bboc^e=FPL-^wpry+QxLyb;E8s+%|X&fl{4*!QzD_djsa)8@WTJJ~gNUC(pBh0OgU zoSS*0lgyp+F!xQFhmC}X`+DQyU{5@={0HD+H`!Bsf&D7s;axDEygZ)dnZ_f}DJRd# z20rdhU30R+AiJMo?L5P(o>Oph6a|b&yIhjz^d!5{j`28y`=26LJCGD*LxzBID`riF-0Vt{16IllfCCK=@|*6V_=n6XC9nGz z!4?}=th;la>+Vzs(=5H%;;$^{uvQA~DEn?=M@wzHwGKN<@&Fc=OV~e}*uGa2+qYL` z`-Wewxy|-(=O?)Y;zxKCd4$k$)96;wBk#){q zqH`*$wI12HjQ8~><8z>MUQTrmyF_%({@!#>r1w4b5qVENN%qtg)gE?W<9pa-Z+qA| z*i%#bJjMQ&?5X$nw#WDOx~C3sx61ruCj3)_WUpyMR4>KSN{D9E~F%?5UG^Uytz)N+0U?|M4{XY~0V@ z9Qr&({QkGCpwHR({rMiplUfdFO|lMsjOfsT-gIcZHywI#S&O)*#P5Hc=+J|`jVIsR zc%tn*#iwbWB2(*$f6_g!*8%Xm9+&yYM#ghaPyEws+#w1++%;W3T&=hDO82&22f+3{ zF7uC%@XtBDjVIY7|9}s7O_vW>>&=cvdh?kMfDcFM^Au+xcJ!Ry#uMsoJke!~Odkt3 zO>Ao9_0_~ z9PZG(mDpQ9^GTE0cmp{@T8m4M$Tb5?}eoe?8Cn&`!ziAYldIQ ze2?Ydf?q?=&pyXCiC=S`$}U;f9b5RG99vi-v4u{(XmGt}GLFsJ(kpX1AfFNamW=1$ z&MHpvJ=woviGMZxLgt+;KLGv}U8kqm67jFjQ^hH?>xxs%EI-Q~rE)odlF(?cQur(+ zA=Z;zmX?3VeoK~1x?es2g&K`an*eu|`y!D`6Oqe?N-nD#m&;gh{(Wg#%YI9i%fBUZ zdA@2q1Ap@I5c+g@hx?EAYUS3uTB;1`LXW(o?dBcs7cbOk2=BZM?A?D;+FXa6Jfg}* z**GWLdZNt@AXi`493-@Ph`Ap|bHT>#ovK#e-79T?Z~j}hcQztdBPu=)Y@Cm6z47r@ z;GM5?uOM>udLmaBY&bT0KG=r;mSfmU#5Rnmbg^?ix_J2q%SCw{Pq8l}`uqabGra54 z=UbO8@;EGDqk>)IfRV3fF3V3W{Xiba7ub8rzEn+*_`2xv*m6Mg8Zr(OcZ{rYQ&p=l zvi@3y@y@2O(qox4*>i&*@ByMj|C0SbveszaiyNq+VC4DbeSb?*DvvPGVgg}a>aPV+46-QcFhN2Zs4AkXOw zoP+S;g(^Nwtcwq^7f(%dJ?P4<7PiFJ-AwQ0HGw=s-%7l|wUA9pXI@z}<5V7rbOdW-8^v-~^FQ4&}9Fn1Ff&qb>G zjdj}IVfC?bZ?T^aeg43`{xtgh>Cf;w^!W()+SBN>&NI9&`sg=-J~a1gsZF1rup)}R z#xg05X#IPKhc;gSKDAku>mAR~=AE70wj9vzlJ)ev ziJl%)>BYW3`B~>R%UaD*`K)Ry(bE^5#dwA`>+Eb9c4r!ks%CV&r<&1hPx~Vi*yUMy z@AyxIcP?&^CVCd5$@RuND%$+KO&cohsk75S1HEHGA&rj6_(ETA(}qXrFk9D<~xKold6;db$Ts! zby2Rq2YGP9S{J!r0Bt_X{tcncF;!mJnMOnXR2}T`f7O*QH>uJ$v5o6LZ*Tg~34UsX z{SV@&euL=0W2*7=|Hz($=^(ubMDP_wqWWCe!&zF8r@j%%3Eq z{tgX(r1=ay`vc6EnPnGw_Qqm@R6l|5*wIh-p2c~}CNZm`pNRjc=%@Fq##7k%c=~$Q zPa41WG9t_0WB$cO`dP>{uANbqJL~k1l?T5UX& ztBz-qj%RX(PC1{*-!OB1ci$1s=ZO6Im>XTB|A;zYZIku>v)ThdlP}3K{Z8g5@;c|l zxo(=wb!alTLZ&~Uc^{GK2bf>TCr>{5q_<9@9E*Otn_Q~E!#&|$xfH6 z{!PFF*(3XRG~TkSEfc9T`ge?~U(od+n=ltaule?E%8V?}udP^>wkjzzvQo$4^cj^_ zol%L-sDw{rG-N*4aGzxYFuw`r(@e{Q*rX2HVKRk%z#{Qef>bjvw#r(jC?()U5MH#j z8rsK?6(|V;h|~xI zvBCK%rz;lkuaZtcV-CT&U|^iCVeH~r*1`{lnqo~5`#Qb%B7zi4Hwgl4gF?U>R=4TLmu2gzLrFS>vF-16)f)$LC2Gj16U&&>J~1urLPc>`~EdaV5H zmUJj4=twKc_uO;Uty6=eg^6sZ|0(LVrpf^7})4NTo$bLArNgr2@r?m0$Y=TYdr#6Xw2eARAHjRtxj1yeH{5$psvK_gV zJmWX*XJprFN0xq1aTb6smk_#?UQpj*H;jj3HoSLv4;jzi4cBttvh;iGn}W|^^5hwH z)to$ka{ps5@ZNseANl|p&$+7c_%=QsS8wB?ePgIUbRprJdRtGX&cYwcZbH4!wO#S9 zb2sb{(K;S#8(&X!|06xw#@Hs*`s^pV|A|#%RKwll8Sac{xZ5@sJ4ZePtBUfHD zJjJ$c?9gYC9XyRb8@G+IO<41LRP>?oF2zpOyT?=SjHj-$jX0+-Q`@K+Cbsbr<=HD@ zR8O?Oe<`tzk5JplT2;33Offd5J?`!p)e{@GjYL0D+bHX&M^ySCwQ*Y%+XVe2+eWIN zo=}Y^v+?mHde={vYn~yp{1|hgOV2jDV;q|V^`fljhc$bgS#}inQOuo}UakI1jF|5n zDwbFOOZip*Ql9>$Jo;5#b6R9Rd&W8GH+4?iX4allrRO=R?8wSFscb}TQ|6?y5jeLs z{#K<&o}lykKFsUe%wI7#pFXdkIu>Btgn3O9eR3yrpFFSIe_r0*lu8(f%-=fd`QlIuN7 z9arnI-gFDn=bN%^`y#Oe)bVdVh1K|1VfmQ$3 z0R3A7f|3*0nU`YUo^M|IO`X>^=GD(LuaQldm)gdynAhRXymoHNyi~l3^Hcw=J)OTb zyeWM6Rbt~l%N#p>Ui&+LYo@Me@9EmwRWf)5h&o%V2Ld4t4l9yKLOJ zN@tF*Q)h0Hpi&*K@yj-j=x{bVR^)BpRrT4k&!wqZ*R(G&=&Y;Kc*raP! z0y|l4VTW_sT$#<07GiK0UccPPmW&Obemi%^geyK|{DUFl3_67K?g<%E2EBXAwDfV) zm|HN!4W?ApAPCpRz5e{i?0;W~ac@p%^;~G_Hy^w*rqN}y8qQ{SUGkZ1^6i~YR}aOK zZ+%fM*#=XDinnB~7QCM!~sY z^#^i&_yw0BCYz$$DMqk)c6PW?^J2@i#_%9g$B-Io_`FUZQZh%+K!B8X)<8PnxkzU` zN;^AO3^|UMyuMsrC;Cl(M!hm2Qq@Avb$Ov{e*eT=+;0+bBC+X6L~m# z!WpGCULYHBIX+4Kn~Af?pV&k^2=KQW)Zb#p$#{-->&;EfgQjvwZBLHr&Js_YMVzkE zn|&gD+p2xTxXn)&I!*gC+NB5)yj-maYy$Pc66Swqx;;l+}Xd5tv;jjsxvCn z8I_53r*e4}_gRq3z08+am&-BKcX%Et&A|uT@$|9@FKT#txrWT{Lzxw8H5A*0z4Yu- zCB6GmdiM&>mb-7RNe& zjL1|Z*1U-SV?`~VDlc$W#-J@qI=0AE2ez;YL&e4Z&h_-U=gPm;7B=)-&CiwJKFq#} zy_owtV`F>_E858swhorq=w6f@OkF7Au)tR&Mi0@slTa;GEX@i$hWH)E6pd#*2scwHB|zDby-r=F~!( z3PGBe3~X^hpbZ%7AP4CTnDbl-_}3ta#ij^SVT>k;0pluQ;PgVYh}>@xL?fDf+U#?Z zB`5XB!`L#|fSo|W#={NHkecLxm+c&|_UkQ4Ti-S>tJPYk@r`BpHhg@)HN74J||2Q@M4}b2> z&%sSM(qMM^b=&~ZU?1i?L{LkB+>h5K&b%l#YobsQ3w6m2Z*nzO++PuOr=+T|IKQo` zGZv*?!lJmVB5i|zs(dHYVLn7YjSGv?0icd<7ia*j;NHF)X|PQYy-hLbT(H@PhPU9I zsvr$a0`20?`TlfsoM;VhAN)oKUm(8`1j$foqTcwF&F)L3OVnEs13*j^e6Md9)WxKx zfh0tl`Uc_suy2^q62E~RCv)y7tM9sjO;&UIEK-_Hjk#0S3sNkn)oL9!uQqd$HAn7n ztj0W(^XoN%gg$cwZ`#hon_8_erfJ_`wVW2dmksE3bMVvaF5tDE>ZFIyh5Aie9^Tov zHy!;N)*G;nY;%x1#!@iPdm7t}3*vKMsnU>p@u za|-($OFSI{_P9lrjEN`?_WfnE*%r5Y@>;v=M)o^@+2@NGoe7&U7J4_er|?{^%Ke(p zYTg2W%>dVLk-zt9Cd1y$egpoxS49I>G*=`G`9qB?b{*A&WA|#i=O6ty+H{rh&s^>= zcs}ZNwy0h`3eVW3dWLT0GiWbfpVz@2CBJ_iJZY5t{tNIVe8%!^%m2=7 zqSsc@tU1=lKEVD@|B}JNeJuG$gky$%$hEAA{WkX@#=!)b8K$Mvf%WxaBi#zNie^D# zP~-C89rL{80f{jJ8AI@|hX=i4)1e;mIpL*|Av=aarNcO?hlaa`IXp=_d#$&8sB!L2 zdcAFGzy<%${(1GHL-q5A$<^4r{iQ|tACBoFmIsQzhjGD}zQMFKFeZ_!!vIP^wZD)S z?8N?ymYV3Sh=#i4F)m74cddrw#8Kh4sC4pMTS!A$u51^XlakN>oKAGB8vSbEzVW2w zwPUAWooM9ewMLDrIB&qKDDd-6!3%Faczv>R$9H)6l|^*(^8vTdRcyhv(|nov6k`;; zzJStY*~V}htpVH$x2Ju#yVJ63xKY>CaUTS1k%HCT!loMaM#|1!^w;|@ZjP8o%v!f! z_piEqKx_W+^ldp#|04E{|FC13DOSClCKS?e-fHnfhCgEn{~U+)`&XbyEZWUF4_m3-f9&G@=dkDdli}@C)!CTqZ;ZBCqhm|-B{S#LD36|iIers( zG|u!h_scxW!}P4+A&TLOlm>n+R2IA>8IBc8WNryy*#s};U`_!r?*U#kVal708Uo-Y z6Sa|L_(CoLhLCrMT{u;n(9+qx=r#tNq6H?6ora|Vu(#+HL|0SdF)PkQC*C0D66Tl$ zHxT{0dRx1dIiH&n1s%Q<<0F-A(*q{1uUX%I{77YM%5l5j70G!5ku>|R z=?42m9EH z!CRgQ_h~f~!!Mf(z9-HbxX}}q;i+wr%S+kZ%&lWppBc6pg#S*#+W3J3OUyf%mKn!X zgcb3EM4AXIW>Jxp$*Rapkw$Od04rpG)pOu2DGK9t!KW@>wBe_HaJ#P}*Wh3Id4v6(Q}bpeS{GiDG|b^^SDr1VP31@1zgS|XemQeUag@rJEYhjgGaVP z4R?E$Ajzb*2wjwiR)(MIyiuVP=qCc!#BviyOimk_%dS$;iR!+Xr#%Dv^E!o8cx zy*t|9)533WB==s_zQ@A%u4O~;ytlRQ>EL^>BKO|jzGs7bFD8HY(e}Lt-1~F#y?1o| z_HOdMced}@;oe>3-n-~Mkgu)FzvCX^J`7$B@Nxm8a!Dg-8eBFPyl`1zy{25eskp$f z1*;8Q%DTiD7sYUGL9Zh>e050=H#}gJDiyK+loWF=iZLFw>q3^UWR^!yWWhl-P!$$4 z)(?u6GiXumWI*w{yq;WvEGCnLPck{|b1qq3u&{O_wy@O%voWcGbv2}rcMV`u^yDIE z*4ByB4l=#fQ_&hn+%;F@&|SOFola)MV=tNgj{9p`(-%CSww4;hd(+wC;I8_QHLSB7 zjFg8i*i-C(`26uxWqV(Fc4~NN`q;a|?q|XYhS4&msarA@B-@PfC6Pyak`iTNs4<11F^bd_Q~= zemw863*a5_cm=&R9}I-yZ3wU8AU|Il9DuiVyo#gz{K#kn-Zt^tf**Dq2T)ioxQ(wy znk{$;z6v#4@JoDEYcf*;FZcsNSm+VEBNQH}HAl-U|Bsz=^MVh*H8}Kq-;>=BaMFN* zxTZ9#gDs5NGdVqjDl#ufao8YAM~ZMavvc3!W2jblr$C!--G;{F*kO1%yPKwPQ|;*i ztpS@F*KigC!>)Q`QcFId$_3R4Y+y7g_|L9|d%8x#kW+d74L53|Np5Hm&8D3zO-Bk| zpDX5ZTTJ^5dUwtI)z4{0>_RwNitV3LeR#!%U(;?=?rS&;tF?Uh*#7#=vHgS7hqX?J zKg}EDf37$;h-oP4tJtsl+P z+f%&gKPgpX&xzTST6j0_oAQIp+xYR1zD8W$DnFmAW?e76!QBpvx8TRYtKGMa!Hk}^sSKx!W-3O1H&aShQz^>XVesvW;2~jzUZWUGL*7(!3x=D3 zS?gvt_{#C^cO2iE;n;}to=`Ymz9F^rIjzZFFINw1C^Y_Pu}Jen$8u=h$m$IoqL{}2yWz1sg@+n0dHaaVcP(f3)^xB9+Y z-Reu-Qn&85ZdtYz$98PT2FG?tZ0Euxu|p0LW&MG4CNRvo>`|6&e!e&q8psw92wk)21aPLRs`X{YKKvjlP3 zR+^QvLNS&tmzSJ+stV$wjpj=ru1H^kB%TM8%9bEg7Q3o&6Gzg7C9}6OI2U(=Cz~hv zq(`Z=+A+GcuGIB*^oM#27V3;1r$) zyWl6LIB^s{vSeAQmqw9V$^o@fs*jfNYIFd^dK;!GEJ#2!2C^_Z1!r+03!D{Bw~Pb& z6fdT*OXIXP#%dFjXxwP5dV$C6k2qrpBkuqhow5FErJqL_>Hg{%Jxcc#=>9USfFT2W z<^(xVZiEz;M?)5K63YGbJzjk>=GPbt|CYD;V@W-IU%;xhM*lj<^{LCcpzi62U1tft z;5Td4@96t8wT@3QW2a6Pv6iWgUZ;8!upk$?8E8vpLhJ@+_wJN;^v zXZ1y!g;DwaYSv_OG~V_oNj z)G~Z-gJo>?9{dPF0~eZpFgfyv7#k{bsaPPmn^*4U;DU>`TdpeCh*ZO{Tn&@u2w5KZ z*iX`AImR^-v6SSniK`a-dUbGCTK5?hLU|i*r-9#1?ij+1i5ziRqdqziU!U~ zYeICj)3mXqE1~pn!Hj`hUT5cu-G8Cids(w3s-@%F5x2_rzLphhco@ggD?=9FfbL~& zg7fgHo1T8Vb+2O4K9--Qwfpvb$L$33lhiN)vBPGqF8@>X+5a&aVqsCmc)Y`ZzuLI} z5ygN>^|cetm)IAWFK{XLL-f0`-p78F{y(d~3@>ejktbW z^Ci_;vi@O~0eXtMgZ?!B3-{kl>VtQ%c;7qVa~_jF=RKsZc#8g!^f?;o@5ijIKisi? zl573^)AIU#OisPg{pJ`ub z|I&c#cfePe&=1adDFVarOBgEEBiLQcLY|Q& zg|BQ%F9%^}2V^HkEJ~E>Mv}rAOJMbS!LyAPg!ox8!>@>A>%%4$Cuo<&eu(`0z?}WV zzkWh8XC*LaN=Y+zt=(C*@yvy*R4UI~R zrtM!}J4XzS8FC6UhoU8SF7{T|k`~+A>5iUwB9lqPE%Lv|MsnFuFiWSTf8lrUqwb;~pihEKYq3Kj zNi`8@6l`5vdqmXZZ^g1J1L?oq0!=}_w9igc_8;14%68X|9gm~vgARQf|7IJrWYf^E z5Z%GUYxHaIdni7_N9fm9P53`q`x@h*cdCf4Jq zjw7IgRn$c-oM@}9B6QuMmC}DyQ8TtPi)Abo2<1}UkK2?dkvFpiVf?HZa;}JTvO@mt zt5*;OD}#c8BI)ht1WQ|n-pvVVNYJ@+kjxJlNvys&vOsiU|G!o62GOrdnXkL*W?Q9S ztg5B7DLW|$Tj{bEa$xkbv_r~xA>XAB#yu`R#F;g>UvAl>GPpARm-TJ!yMeo2ecgKo z2EvgyJ$a~*D85Ub_r?32hEt}S4oBXyXJ8BSB_bpZ#^CvzVU-ZTnKJ218#G4R;qCc z)v_2jtkf%r;0yw%2ZZ!92%H{)b0Tn_GVUXqKk< z5F1+-jdl|GwGdMaRiF`shRFz~v0=sI zGh=&SxrZ3Y3fJ&fx-bEq;2O1wF))>I-EIyIMy|^niRSVHW9>$eGn%aOLZvzYIl`vq zPwb5Y>^@8-21O@kn(h|tLAniiRtBE*c_H{}ZD>z|@g!3o0RILt0O$?8S{t^?;G06j zhsP27Lx=gc0i*BRJ$Yj3x0UT5Uv$AVqmr|$t-h*ceHvH${L)R`m^$jvNf zQ3AH@yJgSxFA5_m>nFC1{@x$FdvWZ;Cgwo3G#1T8m9@gX`0rHT9tO8)KY=B)DfdGC4BJsvB9zm0hTsT>&jwq;_5Y=!b0mR{v zb!dvt9GF(y049n-4?yYM49c~x4UO|T09OfsC8{iYI;4O%?4em^1BiPyl^^auUWxKf z4QsE=CJcuTpVC|(9xQFkk0)FnuSI9loz(EY+Yi?s=>X3AlZjj`##zk=9?coGtI_)_ z#YE5%AL62nGw4`7viiHUY-hg%cqPHk{)(zkqQ<();uto5h|N_@ZCo+69BQn+EP7j} zHXxbWaH*b$%cTKuije6I)2>@!(1Z} z_9IqH;7Q&7c8iOhQGX!Zi4r9ajDgCfFpD1)IWOR3+h}PklD=t)PPJRs3eiOgLBvlh zO=r2uWv8tj`zGqzXWoB5=+o9jn`!+vABh#G$NYgv)aU5(bH17{V|Terq1y5JXkg9U z{t6WBjX2S&kGBl$-}w^VrCKQF3FmFjx$Ca&y^MS4z&1+8?n{7Og_@#%O4ZYdR}y{X zAnKN)dwq(jbs=zFWibzAVq3QuB%6ymMiTh3vtk6iqnw$KExnp=*R5W_s@UcoGn~)^ z!Dj}8&-I$cQeQA~USFoC*t=#T4X8|=L`jh%Vqtl3yI(<_@)~7Z)6u z>Wyr0jg|75RaKi`ygz%T;ul{8tS0IBCVp{T0c(6yzu0nwZD36muw}6sN@IZ1=>HFo zxZV#M6+bvS){zcaJ3L{>;+h}KAkwwL|L6WtySXdWhlh@g%@r@|3ROAJl#F~k+Ns$~ z3K#H#E!ZhpFPXmrtWyxZ{+6l_pi1J)a}o;52?b^1_d+edmv8w!#JFC-lU$xmr)EiM zl@^8yf)D&3w;~R2?*F281oN5Eum5Jdgnj|gFD{E|0J(b;Ed_@ciFT)pojQu7LUEn5 z!xnhHAD!I*Viy|}vn3$)R{*)tALRa8_-H{d?Ue#uqrnljkX-Ng%0Jw9tQxa9)QqOv zGaWw}IkInH3;p{AgKtRpGtE(eujmSFzy0dXd|tKyc)a(WOq%Ya11VM$320XDE)1uv zkI8oPYXqNJ>St7a3boN!7HjxgBh<#taB68|NXBJpP;zn#Sc7Efc(5P6-CRN3y18!3 z=&b3ck9a|~=;euSZp7y)P%?ic+JR1&*QbyaCpRTyGFStB>K>mU`V_jDK508_<;8s6 z<{AYKuT5*1FJH{rtye1I@f5>(K7S|0mR|?lda18c^%!bZ03a(UXcaa%y0os{WR?mz zwYnKBS%9Ud&9OnW7Ta3t6-^ z=k+0zn=3g(mz+8px~a``ZRV}s_%LXJ?aW>DkK0U^?4M2pZ|A9>Q}sE8rE^N$KF0}J zjN7Z2e`k?gEdi%}Xupy`OZt$OjOcL$L72c>U9F66R0`sUO0=-8AjHm!LCcD`U54Uw zuX@^{m;orJ+cjheGz8M{Ax=zt&#B>59QB^tOijAe(=#1-bfwMgI zxfOGjYGZxBGUP&TtyHCftj)r8wFY3>+t)qM*ACS#&35^$_AZm#^4`$lUB$_i(`K_l z#IAk&KJy+EXDD@rOYWO%kM>TqVdeCeYxz*zHCK*%qrr4=GqZHzv| zAdcPb+K;a6(!FrH{{ZOa6i(^|yOQH^_a7i0ZQkr60&-DL*AkiS{LF2gF7F~j!wz1^ z8A@`Lbj`Q>#PdlKU0UxaiA_*xK(2PuVXEFu=9NIIb|_M{Ly@W-oY03-Qv-61OvzrdwqmTG&+(cW*8>gP>5fIkbeN(bDv2bB%DiMmq|796L1{U^J^vGo$^t+B zwoMHFMB?NhYhF^WZ5zG8`Glj{q|3~Y>F=^{UQ?Js1P*5ctHOFE!Ej8iVDD%x^DM)#k5Wd;NrAnypzYG@R|I(9df zss3trf0gjz3FZanGWI&Ei^@_snyNOe2sV^;ylf;NpGse z&0Nyvtta$+q!0*=PP=@71G#Wcjz-~vdC48hdT>{jt#u;x?Vo18&-AnRQD&U;Hx!mw zxgl>+_8V2@?X#r(^z;*P&h9N*hNcI0Y;nvD?HnilmO2xq|Bd-I$eNuh zQi8T1@MUZ|kb!FuGzY<)le!-Gy*TW8Xbhz5VQ+08PAVITll6?v%UAq|B9CY6` z-Rds9mp>NwO8+MC_wH)G#w;=#;70)YVM2cJV79t|9~e^xC4TUPA1>qvzaoZklot76 zBK+v$;Z#dW{J=d7(iVm)cFB<)N#w__?4GyG{yjO|=SgH|QueMm9zQqy{v+8h;^E$O z@75DDK0(r0yK)!riPJbJ$h$YO{Tdj^5LDk-}}AX18U1BYM9un(m-WBQaR2;5oGXeArxG$>n+6)_3_8aZNa!CW+0yVr*!I8IwV zf-^h7=0Kb%n4pJMXCz9zI8NM3fs^pWrS zYqh@W5&oyKpf54LI%TUKAIa^^1;aOlqM`iucSZY>T^v8*cXX>iNpNsC4>5g=mKvZw zO9_>NsDtOxRg~dtb79G&qpnc(G`h^3McPCWEQlGb>yQF?Fs>(?@8$i6jg*?w_2A|S zG?8W3icsc+9NH6~V@2>4#2m0=2)7ncWJ(*fj<-E!^917=d{M68Qe8dri-`e z#y$Nr(LlJ@t8>`>*;}qYcGJNf_kHZ9Yu*uxWb?K1ksT*2AxFr1T`khxH(;~Je7W8G z=EfIws_1Q3UArjFKXw5hdqAE|6i-q`_{$L9kNYV#F&8 zGFb&Sz*Z0pXK{QC#l7^6ACgxU2xf>FAd;X8=LEVvuTbR$11IV+?^bc56=!qxTtlr_ zLENCjBP=(J1}j`z@W@gP%F5AlwV6_>b$X-OB2(JJ1WD+meK@gdq*}JeG+np;^);uC z9IzfpoQ|&k{%~U}b2RkuKOSA39Y1qsoF2GIvg7#t3-J6Y>J|!=U|k79u#zGEFb>HT zmKFn zGk?jEs~$ad&B<^$=jj?NzV*$OsWA^dZZsV;I^NBckM243&IN+^0rKAIhp8adL)}2t z{qjUSP710qN>t;cTaBn3S@M`6du!TV>~fOL9y(MDvz6>A!(r|mEG0s?htlfu+i=vl z%gjq#ZLB!#PT`9d*8I|1r9-2YDvPZEK+yrM=D_0g;tg*;lJ`aQ&Y|LdALCt&?K|?m zTW`PqflQ{imP?jnz8_t4c6e%6DjZ3_<%72byRY7J^#gZzS7&bB3!jofY)=5THv_gl zYKQ`7xTVh(*loaP^pl|holEkkI*d|iAL=pf=GS)AXp{{#WUDNQ8fgKZ7^0UA z-p>sTo3wM61jmMQCQW%>_wUh|$wXiNsOictjs5wN;o->aVl8jd^j+t&+3ghWv6%x5 zjxisF*daiP-B{KatUwXxfe_1@X=uN*BgO@l_J3!(%0lO?-rP);c__`wl@Mc?JNNz$p2x7LJsK|3fUZP>)} z8;NR0NeG1#d1+qc-GiM9X-{d|Y_VjS?GEyy0O_$yxsctV<>LO!XPMD$GuOZG+8b|} zO5D7E->GMF=~BR*Oy(z&f$lDMB~82OiNjluz5nhL55IeUeu2I=Khm3eOSyK6*rEL( z6QlIYl$AOm#T9ic9yFNs8-W9OvZNwoymh077-XGZn*Gq7ZH4!IXVf83V20^sH5yur zU|9yKwqVRb(H3i4%;Cmdxf;>N}kLHkx{wu%M6!`RXKj&S& zL7MU7n`Y)NhPG*N@(#W)d1n}NkP3rL|Aeah(2qFE5DbE8DUqs%5HAx;6D!qCEZG6N zMCqt9JP(WI4kKwbg9T2mdkEa#sah6;wsS(D-Io~Uo@I=KhciP4YK9Ed9K$Bcz$+AK zqPV$+$8E&10$7XVS{<(2cYCVLZciYnOpvfDqlr*r@eREbN<)lz7qMk_rM6J1(RkA8 zi^NFAS;0+{+Us>cS0ChJ(YJ)UGVj02;tdQ2E_r)8ZVst#z5jS}(&Vcc>CYMIFGjrT z)juEkz}A6>OsnrG9I>4lD&6?c570~xdwV_)yuGgZ8v8WxwoFy2Z&UR$T8ntL47t2v zMRbM0Mfc%LHu^zK$k>g}RxU4TB;M+gw>;tPKmq?40P=S9%&(szyww8_4a*`6IXtuc z%;=YYQauX(T8tiM)G+QBhWtPIcAi-moiW#Dc3VnMluJ#q?cdfWNe@RGGXS4 z&kuny%$=g} ze9J~3w6V;>0>SnJ%nBP~KTkQRUJ8O)Vrz{y^fpEo?W zWbc$*P$2!B52S)K7C%?nT?-6dzisMr_R3hXXe;S$JOu7L_smLh_v65;Eo;1j+}Z|F zw+u)L65A!6#4CFnuMiAyW`mt@w7sb7@sxuPA9bJhy8li&V`uHX@spt$v(wZ%f#;4g z1Iz))?d|y7d_mgb$G3L)$+^9>R}Y@u*?Mk9dhWG7W6EPkXI(*Uw{Id63U8mBnqeND za#{>=LwtIYzhuw2QZs))_T#AqIzVmKfEUI&p@2?&2*^XEw=~qR@2PvS+>`)Xg6ywN zgyGhNT-6vsW@$4-}w--T%$Xig3BH}3N@}VR$G9}DLj(CNGe-fXjwxGmN`@1J=xK*+SK&I z#>#jAqGTZr!3ZgFdd}B6T;C^Y8<^=RYf1%hrM9`gtf>nPUp1cIYOfut?Z8%v!)5%K0pFNCKO!{vG6K5=Wqa}BHK{GnOH5Yfy^dtpzDD2874l|?29K9&+%FQVM#0;`bz8`=#7DbJOZO6h3TF!gxVbaX+=-iLzV=>-jJurKD-l$!DcVReX`NS=BFyL4{ zf{lC`H=I=~QD3L(A;hfE7%MSmlAGUg2$I$Y(6cq zNyp$qGoIDwX8Srj0_vPMTG@P>llzyBs(-Zv+?IzoC7XzUrDiX6n2xl~tz4#X_%g*f zUd9QNXz><-yf}&E#jUMAB4mnaw+^-}Vx)7lrDvaH8O5=kpgaYlJV&JNAt=w4n;XYn zVEGQWtByR@G7Pv3ZEX4M;u<@59fr$&cqCIU#i8L{hdakQ3qaesYv19`+Jp53j=JD` zNt79vgQP*|NgRl~&uE6pw$J&wdc+~VONws+dy5kBJ5 znKH)K%nP#}-#Kv zPOvVE$*444splF-J*g(+QAl!)Xfgtq2|Th?LvM{FrFZA*jTUPpnn<>rB)6YTcTw$g z%at|JS|5o?)8z+KPM6hZGwS;1!Bc3>k^Y^1>jJGq-NSw3qqWaEQ-S{IEA+KRZ*l8X zqAip<^h94aCDo_nGQa%P!;sGbzv>2L5bCMZfDCeSdg+e>;-nl64Dtbk0*Jk61_lK; zDG8~UMZfJF7Ec66wfk&3AtI4c4{RN6vsofr*znwj18)?Xs z3etI*693rwxbU&xF;A?2@;8p7Z95~+awe$(>UUJV%E*Jbo6W`Ztvw8Gba=T|C5gB` zF`FafGCi_J3>F0MS<%6*C~sW;!mGoCD_Bd?FN@*mx;f6gv_q+z$1TmR+l|t6yb(e} zIJSj`pVu40kyQ8EjwHR_U_{EgI*91-Vm#P~p01}?emk=mbhpPRQo?VS-@>tB_GROI z%b|qF&HEf?P2z~w*)w>cx;E9~o9KR9_~`DTE%Y0k%&sIO1HP}&--`^*m16SLN-qDo zRr~$#O3{_|11gk^51HkRQWMm-sCrG_zcAF=zYtb%3U9i9A%nHdK53S-Z99ro0h}y| zQ!+XSUwKSXYQwS^$lxnm&cADcdP;9_xc!0F>#*U9Y)$AaLG=wmoDmxt#kQOm;&Q<> zC~$n|?gV7~A3v+vhJZdYe5AHEra757N%+tF&x?#{%2=cqF754@(L^l1ao!otGxCgP zmO4Y#gQzVYfMPS_irRu@**MnCa|OkGuH$qngRc--Ga6wI)B@71bAof30=_z3;Yc#I z_TusN6p7Pu4>%K((dU<5t4W$&>t*}=lREA4?8;OxD{Vf5L-?vNE<8F@j&7W>0rsIS z{U!4<)8Cq%(H8K%qjfEVwRA1GLV;CgXQWrjb-wn3G=}w91EB{66)$K81=c30$*4mm ztc_KY5H9>OO3z27`5ETr2Oq@gnP#&YV4kCD?7f_pU0FRrNq?XDG|-+Tw4W)oZsz;c zC)xXe_I-usXI`Mb%3cSw?kOeytiIBG zw)si;yf>QO*ZqWcXVcmIdh=6oAJq)J-a}~V)u)@!H!p?zR-5MEzeH%4H}lPJG;ar5 zvl)CnOHuy`R$hFe004N}V_;-pU;yHtol9TE^V@u7kY|1Y6k#~)({d3;|Ks`B&ML@K z!obYH!N3Gk4FHQf4TpHzV_;-pVBYqhje&tx@E^~=!>ob~DGcHaNPzhd0D#E`F#vel zt(SRhl~o$X-*e7|DT)kB7;!-iC{)y1!3A)L7ARVlNNuHXDN7Y?sdNCVR67Hfw(0~V zDWyy>aVZ!S*NAb!sEJ5i$mk@a6Eks7T!QkCOb9NKd;GoUe&3~haIJsLO`bg8{g$)5 z@AEFlE|DjMpW82DW^?gAJY1%mw`G?tmTSON*CHG3_fjK=<#Dq}T7WZ$6Tp>V5?BB>M`Kom_xYAFZqjkUo#rk1hx@avvCV$J-6@5( zTUNNf@Lxa~_>li+OQC56?f#&BmU}a#(A_Vi?ALOZ{Yh5YF77Em4vwocZ%0(sc3 zmb=_mX|${T0mi$_)=MAuvB115Ti{ce|5o-Srb9YxrA%hrQuB_K*&4aZd?ID0+Hbcf z$xwT{Tx%+1xtZ;sw3B3pxmxO6r15 zJ=h=HhkG5e(=znz12lt!I19B(dFLY1I2CxRSV*AsA z-A@45?02g#f**Py_@M_}e<{lOA&Ggi30|qb=!xKq+ZpdR?5lP^0o+iTtiA|-=!puc z3%;lb*X6g6c8w4e%T(;`yb)Cu)}@A7T6zu=+=BlVJBF@peA{ zi6zx@j)^PPc3vjt(EX^u_0kah-26M3_zW?ECW1fWI3-q+Z04euZYd6$`gK7JP2Hunvh&+0Crse~4y`DZ5Ak>i99lD13O{_trK?%5!JT}ODArpy@2D6Ygu992KOc_bOTkh`7#v5xnNqu2z{Sn*B ze#S{H^A)IFY=3#$_V>Hs`KDNQSSLHkK`lV}Q_Z!e!|#O$f)~h59^OYjE`!&BJ=CFA z5l>ZnS@sjb#b78H3C`uiLY|KYE#PHP2AaSgK-J3}099Zd zSOpe?Vz31C2iq9uk8#c;*EZYBvb8DLy!B{3N|nmk)O6mdmS#5s9?g3@WWE~)s~?_# zwXW&+U;EE>ywr2OyxC;km%wo)glWnt{YvNKiBSioH; zYf`Cbo-Qd#)xy6phiX9m-xu3HLp`2>@6cUpuv}n1B8K9eSS4vYSqfN70s2Yjaw>H# zc>q%|I{+Oj&GL9%|R2J(NeoibaT0P37Ywe*jdCFb@(^R*>)lM>^n(^e6IB| zo4?CdkLHMl&RFIPd0jN`N~IpTUWdJaDLfNv(TAKl-7*>_sf z`o8W_?P`}w+KrK7yF_k`;v?<;6Y5slH8S1~_8RAQ?HjbNYYyti_%U~(>yxFQdlLKW z-m$&z-~Jfo>>6rwn^eZE+Q_Y8Kz2>!IPF`~Ay+5zLN0H_`GMSKzRc!0<#;UBOz>Nj zgM!x7P7&wJ>8c6!i)tv?Hp%gR(5Tu`wWR(;gJbOw{G(b_J;dwY8TL|o9QUk^HAib> zK0a}c^uI*^Q`-N99xP9dJicc7eWt=cpUc%r&eqDJW7+SEczcLH{Ga5!wKU7q^4k5V z{R=~n81pfC9s3{48+)m-4G~X;tH{Bh!BgRvqPj3G;rF)#(ZkX*!f7jfR(0$Raf>hKn=#bB_tvYs4XYW^+v z@ZKGL2Fb=;eXo;tuuzuzM(TMzSO6A-TCgd)t^)(XaMS6(fm6VMKHwB^d2~HM&!4GQ z^nRE;I!o50+hWcuEA`Bh`h+^0J-^I}xDqsin?M_=2Tfo_#Lp<}8A;=r-s26&)&y6-UI*g+v!z0HBXvqYJQvVX1(3^Zr{x}^f`XG-{1@Vnf?l2;~(+o`X*oO zr~5zo1)05>(#(d;l+4=9d71gY0rr0PHrB9;}%Ey*UrI=xHlv-PAE3z62+hCMRMYJg@LXD6SYJdH|f34#< zk9XetzV7on&-1$9_wh#j@h=(tKTr{o7DOD^Fyi=L5hpZ_s5~B4M4V{t#HkTg)SNUF zHbqp;fRXS_7Xrg!1vWRBRG+PkSd_zPFuPv)aw6fOf zKtwjJt?6$=SKEyd?aCuA(6>Er?P>1dwZo&J_l1=sI?|EjHHY7e>ADzaF1}0VL|n@6 zWy>NuJ9oMIF8p=X;|lv%=-b^|kA)FEXGC0y^C}vzmQ61`ded-C{fIvJ|6CYx?Q~cb zaox6v>*aI3ysqCG(U12V_3E!jf!YGKH_7NGbp!YvNYlU;V11xj+=BZST!Z)~d*KbD?x^o^t8VZFxFFrJnP?%4#q z6KQ>f*GXz8*`M4QGK(WBfes%;PjXzB6K)`f2K?ne7wwKcVlF@_W+z zr<|E?_S0#Y?%dNfKF#klGJ4kE&-!}?e>3!XPM_zUovD5%Pc!wJCC4InYPLDd(d%#b z0UvYS*}3lDJRI|I%rp1-de1kT`LcLHRxjdQAddy|c**QuGW(Zh{<3omW&027uULP@ z`m1L1Dou;@TV&R+@&20o`MSQZ^Z5pCZ{mGRzs1%TTU#vSCA=@ewM2ezTYHLf5Bnxnp1B`Dt4$ygk|ATTk z=)V8L^DjIf;^h#I3LF(UD)gwJ^)OwBc{+^mFuq^$9ii)p{i8G-b$5>8KPH#oc=^rU z{N3IBeQP8!6c$C2b_j zXBMwn`ZuJr;S9jtkk5u2p*)gCm7p%P0Qws7-Dog4(@392dm?G9Pvct96mo$7#&k5k z52k|rn(*4h`KCJ~IY<9y-JmbvYNoCkJuP@^X}uL+*)qz;eV)D6ytcOH`IEG119EAn zwjC|)X}B;Wl8!RUv38L>I?1k+ex3B|L`$xIx%}mtRqp&qE;Sd=pQJMnUF6escqCWw z;MtS(G{>I$T#4%{xLO{)@%QFG5AQXyxJE90P-TobV+@Z&C zUPh=L!S@JRj!<)FWuWUWcjRt*?lI?k?B6Tfd-c4(Fp^PnC^YK_&E+9y#?UsF?{V(H z!}1vK%y>CZ(0?MWk6N2#c2o6#42Ne$@+8jb?#46bIm7yM^v>jSmOP5}olW;_nawtr zIp#Ol?B?2^C&T%2c!7o&<@1vDzw@wApI2yHgzGh&uhI89Pye+3run_4=UY52c4o2l zw{iT-*>`v?;iE(rCFWXUt|jJL!c&Qw5_vCo&zDXu8u_k~(;C^V)pIRf>)g|I@+osC%4jK5x8B`fZ>}Gh z?}zf(z}p6Q^`mK#d~AOst)J5NxokJN2b*wiHrLJgzQFf|o?D#RqIRp?wz~6Q()y*G z|6_(on<&G}n8zN7bhy1qA$ zowV-6zYG5^{6EOx2ifg5yWR5NL)RWU_c*uLeD?D7qfCF4+fQoCoQ`teQTx>H z)B9(&Kl8a?2K!}yK%NKi9#ne}?=SK@ME4=OE9kDE`>;E6ST4Vs>#xrKO7{`-Kce=C z%#XU8M`d)BhGR4w!~2{4-`qvt+llY()c1Dkdpq^Lo%-HR(^}9Mc0_vI$Ve+yg6WYS zUmw){DF@0TJ)s}W0di?%-5ETb;oO;pk!DndS>W7R!y~QdXGT4}>TQfPlg3OnnLK9FQa=M0M%sYS2DlpF zI=c_>c{Yt%v}W1Q(yO8UM%6(Mjk94zq)p`21b5SlNY8mR(q?sG0npOiUUU4-X>C3~ z(iUw1|G9EGwJ_FQB2lGacm7K^_;jfMt<(G>?w_`>$AMHAQ z?q_%2y6fMa{_ec?;H5`HFc-gn(jIj6w11_{ubLX^)$Us_T)k<`Gow7d^4yI)oOw9& z_|8+C$44HYdFGsFc74q4TDVTWH{j^6zCXVHdU!6So=K_aQJQZq`84MHtmNa$rzzil zzWoCG1@;T<7dT%)^UX56*&GMRbpYN0dJph^AP;{r|3Pr8K11joisN>39Zsj;O6gtr z?>6guow;A1`%5Ez0M7$-drqaJ@E7V=NaJYdM?3$Zy)k@@g>mlAIBO5%ACF^#dpps& ziDvr<|C8iB3CAQEPIgx(`)o{>#}sF$xYJY3$nT=`F?y!a^921*;eQ&QF}G)Zrf2B= zygAO~qX2^DV9?){$gH>X)LCz7=JMx z#pW`*>VE;e4eZDO00001000CJ0BisS0Dk}m00ICR073v7000310tEmB00ns3%~Z{9 z(?AgZ8izKv_ZnB;cPD?vX6x9=eVu1i6WX%JRgNJ2Dkxo)nh-jn2qdU<=^|1C zN|6rIk)CiFE~cCIN!m!7@AmN#1L)7Z%q-GHV^?mkU4)n($F|@+ zp1*wAA%nTb7hjdvRc@%tL1kLb{P++Shg7$c(pzBLik(4TK@`u%A>~36N-?K;LvmBk zP@rW*eeXL!`Rz5o-M+0)wsNNf$XN4|3g@k$FH>!k%mB;bM{`<>JIXhGBv|i_R*oG0 z(L29hpp3ME)a*pxa2QH=>)Q?E`fOVVTpKhCst8DjfXDXisd>|*H zO+w%JHX8AQM3clbU$;2ahT4m_?y&)##qp%X>{e157FoT=I4|B%8y^B~dLNc$va-f0 zW?sO{?EYk0AZO)?Wor4?kLJS%-hVSBH<%86{9#_GAKDJze%|x|sXTPu#Z_< zT4-L%m{JTjNMfuxL#cX3=ngC#6_R)e5~G}@gh2O?A$DHNW#5)rESd#Jy$7L_iROda zpeuYe!#h~|4C_7*d9Utg-@P8}Kj@oxozgR&0)W!Xgx_dv!$*DAr3Kd(MYRiVm{UD# zcC1Y`GQ)Ke>UT$H99ZP{Xxhx~IB+z3x6uGshZBTCj>2lX-~W)bkmP@nWL99lr z8q3`vQlsgPRc?^1(cZ?=Hr)2q;K7PC0F#SG01qC=?WXC;B)-vx3cL8tM>mz!xVXjNnIn;{QSal!ix|@e(455Z|i;5=H-y|+H$YsD8rH&!u62M z5Q6*h+mw>IM}>F5Ss|4=9L5Ph-oJQM2IVdc&oyEsfUI<0xnA8dIGKR@)4_C3u;Km} z_;gmrgZw!Zec4!7pWPglIAlE?u379C3>G>a=q(NgOZ9!#JCJP?ml_y8U}zJl>jfQ% zwTabfbz=G3lxu@el>?@}RZH^Q+|grGS_cvA`(`fdy-M-hkSjp6+$s?Y){93w@bBU=iuN0IG7*5N=Dv5wq2 zygNm?Bi|dxLlNo7@y1(I6guAR;Z!J+9eI0rI0f2Kz>lM&+z!v?$7@q$!jT0y(M{&# zw1RKt6t3{M%Vj|X*m2}?$r0sFc)lHugCZQBV~01V$cMjM!6{NC!t++}K@>=Mff|mL zB6yaqhBulWf#au$p5;W~?I?<8{O6+)!~!bE`N%o3lUl&E z{HK7}Ro65v*Vj)c#Cu9Ltx65F26ecEi=BX$R&UdtobFiKK2E#tBy4HQ%GZW5}dP8NH=gw@(?lk*n30UCPCLmbzk^)-tdk3QV(J4VTJk{ zAz@)xw9YL{F3_rDn}_Ja@E>#cM23XsOafn{Wi`u&z;l*m!;-S<&xUI2^S(>Me@$#L z^_`I9n*hGpeC@TF9{u(0onQYSmjZ-yMB%!Y$0c^OjveA@arHVkhL`J8zk^QDXTFiV z06KKH?@L~>cr@NOmKUgv#`q@jf(Fq0zR^g4C>rgXh!iVFZ~7vTKxg!vZweB$fu{II z*#J1ueZC1cVp-@l-#8nfIeN-B*#4 zKFd7M$S`{(|DJii!a!L+FQBE#rMhYJ3SVWhe}iPH-nK02gJk*eHUsK?Z3$>w3{_QI zR=v%QDyuDZ-&RJ|)|PK?)1r#JN_e(KP?af|5pdPp`K^j-ndIv74iSI9rTfj3HU92P zTFp}>W#AevW^lDb05WA}#~=Q#P=%oJu0&WNC;^>bM{o0I^ENIFOYfwnpP@WFd&95yq4ZhQP+g<;da?>oJTUSruX@ zHO@#DXt$8U=#gdHZR*BBWHD5NSJ?s~FeKKi$a>R6`Q^>`J)3SJ`gPOp1SqOt7voM= zZnyOw=OK%S(?WV`hq22O^y+wm7V9Nr7l;h)w%Gzrt^pAkLO4SP^9 z?R;n%W>_%8aj1E0Yd4Waz8+>~hn*oahncQSG?V$l%vZ2SWSUcBwTV=>Ex z^kL^j9hv9!aVK_iY~0=`^L06^}tY6+wx zbR0^6vf>lGTsK`48nosG1O)^Iiv@}W^)%n6h_hKL)Y~{x1{ud;V_kWu)ZSEbvfmMYM@?-dyZV7?{Ul6meFMC9-%~e; zeTa|7?{mljqqQ>P8-8>2VMJ}Qpxt-=<)>fz^|igm>*X@^`_yJu*e_($NneSgR57t4 zl1L&R3~5@>$^KjZU(HLMJYWx(0(6u4NLva|hg(pe;D~R7EkA7TOqkArQ}(0|1J`*N z&0M)tLm3ByLbJV##3UA`T1Rds@`(PWOnZEg++L)A85j`g-Z8rbrl(}2jg?3jJT#jZAv3Xm_FIc;ZEuY_YkGKUyZTp}HH(uk z!Is78!R)Mg=38KZ8JW4S#&p|nr*163%>~Op)bRWdx-VCeh$OnS{RZjPs4B2Nh$C@{ znI>kT$yLu$&A4+$wq3IPa=?BPD%fP{dN$YNh8D3NXO@w;J`oRs#P!c7Rn5_B14Z;M z3X>Y_Zuek-QKR5-kDikJhpn^5+=~cLv_?;Z+d`MI^$_B{oiYt`A?{h%NMzo<&LkSm zn_P#i&sCpu4r_Z`NSCS#7r{{Yh)+{2DDfg=&**&9FqmA5m>rQs<@Q#&{&NBi4s zjE;%#owdTi&0})Q92%wdF^xf zO7L8Xv_C%9L%BJ6fL|3<>zrn(W%as5#&!J3M(jR)T0Smy*$Y)h>~3q8+1t70vMaf< zz5$|HS+;p+5@SNnW^2r=Hp)QV;NXtwFSh!61a#r%UdKI|R?6mM zc@}S)!>pg5b|>FwI|(dksB6R6`t;cq)13uYG!m@X6~X$_in%fp@^9C;?S@+{`$uHGei-kH zI4n4N60@uZ1sCyNLUY%xkr&{ zMLP5!$u&f}bW-e7Atu46ob)yJQtDmQ`*WNlsEaR;luVq zgk+F$p)=6i;_cHMZgDv}w^vs3o^_8!r}mZPF;is3hwLpf`iLUQao!I>S2)tIHx?>PMxn;%DLxf+ z$~opAnDg-SsQ%(if$BNwTnwcR3Qf8_PSQ0 zI%y*4$VwFh7sq~6xo=z$b8JI|{l7Ti2D(zC_-WK=!IBL>C{-x zm8h%4)SiDPD>0pP5v*r7sgTKyA}A~QMa9^EE4EEOApr)!-;kod{Lxd5ej`n z{I_K5!8Fyx{91tUZ&%u76jg9Z?Q%E+mHj7>Xo@TU524#ArhrzBIh>mc{^?4@;>!PB zaPAt5EV!sf6HZHI`pHFt2}4h!j2ZOob2VW>6ljV7bKx9}Bo zwf{?MI1~WgBAUdrs_}iwK?k9oCcTpc3O${21@nZ36M z%uvZY;mD}E$IsGN_$R{(%k|%9uD;biUCRIH@TlR(QO3c`6Dt(MfxC~pU3hrMOTI%! zQ@wbjqY{p36U?s*xn{I%NB}r*b2_dlGP6(z`!o;g%pdX&TvIHsJdW~vtkOVd6*--9 z)O44w*)w4O!w=80_O0P9e zFTa{v-)e_&CRd$zKxU;?sdsJpFe#dAlQ?3Et$m?ee3hUS3^qSyRzVH1{<50Ty()n} za)Cd;5B4`}rgY?6#%%=}1}s?bxmlKFu_aIUk#_=N-zWRLV7zs&+EKH0uigasR7o1n z^vxBuKoviyxX<|PSiq2#uVRX3E31bqL(cfKq)tPAwX1^XAwv+aFL(cT83&V6Ym)gf z e$4PBJjpjdeSl>yD=_jj9mjnLQV`M5{XdDd%$`gJ-|a7$!~ib*mCOZ5|iw} z-KZ4lAe|>``kXq%8?_mm6Im?!j7fycG-f}aE6PevUHbSLOZW5P(7|E5t8MT4+AD$b zYJBC@yhX8%e)`*^$8zr0okRu=z$M=KB2Jt8@bq|{M;|_%(v-t36@K^OOhhJ4E?oF8 D(%L$| literal 0 HcmV?d00001 diff --git a/docs/_spec/public/fonts/Heuristica-Regular.woff b/docs/_spec/public/fonts/Heuristica-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..f5c1f8b2dbc554962717e3b45cb9a9f24edf9e35 GIT binary patch literal 141416 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 )dvUAhF*A_%b_kOw19$Ro!rN>Y2LZ@;n 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 literal 0 HcmV?d00001 diff --git a/docs/_spec/public/fonts/Heuristica-RegularItalic.woff b/docs/_spec/public/fonts/Heuristica-RegularItalic.woff new file mode 100644 index 0000000000000000000000000000000000000000..d2c8664593dced0b39e0d3e0721613b5f28254d2 GIT binary patch literal 104700 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 TBzJ^0kbl+U47hQI0Kk6%XYVT3 literal 0 HcmV?d00001 diff --git a/docs/_spec/public/fonts/LuxiMono-Bold.woff b/docs/_spec/public/fonts/LuxiMono-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..8581bb5aa4584a8281f1ae761b5fe5da8352832c GIT binary patch literal 26560 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 literal 0 HcmV?d00001 diff --git a/docs/_spec/public/fonts/LuxiMono-RegularOblique.woff b/docs/_spec/public/fonts/LuxiMono-RegularOblique.woff new file mode 100644 index 0000000000000000000000000000000000000000..26999f990fa9f9302762b55318c27d4d24f26bf8 GIT binary patch literal 29300 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 literal 0 HcmV?d00001 diff --git a/docs/_spec/public/fonts/LuxiSans-Bold.woff b/docs/_spec/public/fonts/LuxiSans-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..162621568b5307556c0cf0539c7e4ee3b572564e GIT binary patch literal 13592 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 literal 0 HcmV?d00001 diff --git a/docs/_spec/public/images/classhierarchy.pdf b/docs/_spec/public/images/classhierarchy.pdf new file mode 100644 index 0000000000000000000000000000000000000000..58e050174b65669c9d5d12cfd5f0420e14265e24 GIT binary patch literal 84078 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

auKIU$nHF%T}-@XFY!Z2Q^}R6X2S;g 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 literal 0 HcmV?d00001 diff --git a/docs/_spec/public/images/classhierarchy.png b/docs/_spec/public/images/classhierarchy.png new file mode 100644 index 0000000000000000000000000000000000000000..3da25ecbf2d579a8330754ff3d6173c4a6dcfa57 GIT binary patch literal 117555 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

2DhI`_^IAc^h42bKL{P~ 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
  • ') + .addClass(opts.itemClass(i, heading, $h, opts.prefix)) + .append(a); + + ul.append(li); + }); + el.html(ul); + }); +}; + + +jQuery.fn.toc.defaults = { + container: 'body', + listType: '
      ', + selectors: 'h1,h2,h3', + prefix: 'toc', + activeClass: 'toc-active', + onHighlight: function() {}, + highlightOnScroll: true, + highlightOffset: 100, + anchorName: function(i, heading, prefix) { + if(heading.id.length) { + return heading.id; + } + + var candidateId = $(heading).text().replace(/[^a-z0-9]/ig, ' ').replace(/\s+/g, '-').toLowerCase(); + if (verboseIdCache[candidateId]) { + var j = 2; + + while(verboseIdCache[candidateId + j]) { + j++; + } + candidateId = candidateId + '-' + j; + + } + verboseIdCache[candidateId] = true; + + return prefix + '-' + candidateId; + }, + headerText: function(i, heading, $heading) { + return $heading.text(); + }, + itemClass: function(i, heading, $heading, prefix) { + return prefix + '-' + $heading[0].tagName.toLowerCase(); + } + +}; + +})(jQuery); diff --git a/docs/_spec/public/stylesheets/fonts.css b/docs/_spec/public/stylesheets/fonts.css new file mode 100644 index 000000000000..36efb2bbd5a0 --- /dev/null +++ b/docs/_spec/public/stylesheets/fonts.css @@ -0,0 +1,73 @@ +@font-face { + font-family: 'Luxi Sans'; + src: local('Luxi Sans Regular'), + url('../fonts/LuxiSans-Regular.woff') format('woff'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'Luxi Sans'; + src: local('Luxi Sans Bold'), + url('../fonts/LuxiSans-Bold.woff') format('woff'); + font-weight: bold; + font-style: normal; +} + +@font-face { + font-family: 'Luxi Mono'; + src: local('Luxi Mono Regular'), + url('../fonts/LuxiMono-Regular.woff') format('woff'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'Luxi Mono'; + src: local('Luxi Mono Oblique'), + url('../fonts/LuxiMono-BoldOblique.woff') format('woff'); + font-weight: normal; + font-style: oblique; +} +@font-face { + font-family: 'Luxi Mono'; + src: local('Luxi Mono Bold'), + url('../fonts/LuxiMono-Bold.woff') format('woff'); + font-weight: bold; + font-style: normal; +} +@font-face { + font-family: 'Luxi Mono'; + src: local('Luxi Mono Bold Oblique'), + url('../fonts/LuxiMono-BoldOblique.woff') format('woff'); + font-weight: bold; + font-style: oblique; +} + +@font-face { + font-family: 'Heuristica'; + src: local('Heuristica Regular'), + url('../fonts/Heuristica-Regular.woff') format('woff'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'Heuristica'; + src: local('Heuristica Italic'), + url('../fonts/Heuristica-RegularItalic.woff') format('woff'); + font-weight: normal; + font-style: italic; +} +@font-face { + font-family: 'Heuristica'; + src: local('Heuristica Bold'), + url('../fonts/Heuristica-Bold.woff') format('woff'); + font-weight: bold; + font-style: normal; +} +@font-face { + font-family: 'Heuristica'; + src: local('Heuristica Bold Italic'), + url('../fonts/Heuristica-BoldItalic.woff') format('woff'); + font-weight: bold; + font-style: italic; +} diff --git a/docs/_spec/public/stylesheets/print.css b/docs/_spec/public/stylesheets/print.css new file mode 100644 index 000000000000..f0efff28b203 --- /dev/null +++ b/docs/_spec/public/stylesheets/print.css @@ -0,0 +1,42 @@ +/* This removes a few things from screen.css for printing */ + +body { + padding: 0px; + margin: 0px; +} + +.anchor, #navigation, .to_top, .version-notice, .hidden-print { + display: none !important; +} + +.print-only { + display: block; +} + +#content-container { + width: 100%; + float: none; +} + +/* no scrollbars, jump to next row.. */ +.highlight pre code { + overflow: hidden; + white-space: pre-wrap; +} + +main { + position: relative; + top: 32px; + margin: 0 0 0 0; + padding: 0px 32px; + max-width: none; + min-width: none; + min-height: none; + background-color: #FFF; +} + +/* Avoid clipped headings https://github.com/pdfkit/pdfkit/issues/113#issuecomment-7027798 */ +h2, h3, h4, h5, h6 { + padding: 0px; + margin: 0px; +} diff --git a/docs/_spec/public/stylesheets/screen-small.css b/docs/_spec/public/stylesheets/screen-small.css new file mode 100644 index 000000000000..674db7c49000 --- /dev/null +++ b/docs/_spec/public/stylesheets/screen-small.css @@ -0,0 +1,57 @@ +body { + padding: 0px; + margin: 0px; +} +aside.left { + position: relative; + margin: 0px auto; + overflow: visible; + height: inherit; + margin-bottom: 40px; + background-color: #073642; +} +header { + position: relative; + height: inherit; + min-height: 32px; +} +main { + max-width: 1000px; + min-width: 600px; + margin: 0 auto; +} + +#chapters a { + font-size: 14px; + max-height: 32px; + padding: 4px 8px; + white-space: nowrap; + display: inline-block; +} +#chapters > #github { + padding: 14px; +} + +#toc { + overflow: visible; +} +#toc .toc-active { + background: inherit; +} +#toc .toc-h1 { + display: inherit; +} +#toc .toc-h1 a { + padding-left: 10px; + color: #FFFFFF; + background: #72D0EB; +} +#toc .toc-h2 a { + padding-left: 30px; +} +#toc .toc-h3 a { + padding-left: 50px; +} +#toc a { + font-size: 14px; +} diff --git a/docs/_spec/public/stylesheets/screen-toc.css b/docs/_spec/public/stylesheets/screen-toc.css new file mode 100644 index 000000000000..7a04bd00f96c --- /dev/null +++ b/docs/_spec/public/stylesheets/screen-toc.css @@ -0,0 +1,37 @@ +body { + padding: 0px; + margin: 0px; +} +header { + height: 96px; + padding: 0px; + width: 100%; + position: relative; + color: #FFFFFF; +} +#header-main { + height: 68px; + line-height: 1.2; + font-size: 32px; +} +#header-sub { + padding-left: 64px; + height: 28px; + background-color:#72D0EB; + vertical-align: middle; +} +#scala-logo { + padding: 10px; +} +#title { + vertical-align: middle; +} +#github { + height: 40px; + padding: 14px; + float: right; + font-size: 0px; +} +li { + margin: 5px; +} diff --git a/docs/_spec/public/stylesheets/screen.css b/docs/_spec/public/stylesheets/screen.css new file mode 100644 index 000000000000..2073613eaea7 --- /dev/null +++ b/docs/_spec/public/stylesheets/screen.css @@ -0,0 +1,521 @@ +/* from https://gist.github.com/andyferra/2554919 */ + +body { + font-family:Heuristica,Georgia,serif; + color: #222222; + line-height: 1.6; + + padding-bottom: 10px; + background-color: white; + padding-left: 30px; +} + +#content-container > *:first-child { + margin-top: 0 !important; +} +#content-container > *:last-child { + margin-bottom: 0 !important; +} + +a { + color: #08C; + text-decoration: none; +} +a:hover, a:focus { + +} +a.absent { + color: #cc0000; +} +a.anchor { + display: block; + margin-left: -35px; + padding-left: 10px; + cursor: pointer; + position: absolute; + top: 0; + left: 0; + bottom: 0; + color: black; + width: 35px; height: 100%; +} + +a.anchor span { + vertical-align: middle; +} + +h1, h2, h3, h4, h5, h6 { + margin: 30px 0 0px; + padding: 0; + /* Fix anchor position due to header */ + padding-top: 32px; + margin-top: -32px; + font-weight: bold; + -webkit-font-smoothing: antialiased; + cursor: text; + position: relative; + pointer-events: none; +} + +h1, h2 { + font-weight: normal; +} + +h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { + text-decoration: none; +} + +h1:hover a.anchor span, h2:hover a.anchor span, h3:hover a.anchor span, h4:hover a.anchor span, h5:hover a.anchor span, h6:hover a.anchor span { + display: inline-block; +} + +h1 a.anchor span, h2 a.anchor span, h3 a.anchor span, h4 a.anchor span, h5 a.anchor span, h6 a.anchor span { + display: none; +} + +h1 a.anchor:hover span, h2 a.anchor:hover span, h3 a.anchor:hover span, h4 a.anchor:hover span, h5 a.anchor:hover span, h6 a.anchor:hover span { + display: inline-block; +} + +h1 tt, h1 code { + font-size: inherit; +} + +h2 tt, h2 code { + font-size: inherit; +} + +h3 tt, h3 code { + font-size: inherit; +} + +h4 tt, h4 code { + font-size: inherit; +} + +h5 tt, h5 code { + font-size: inherit; +} + +h6 tt, h6 code { + font-size: inherit; +} + +h1 { + font-size: 28px; + color: black; +} + +h2 { + font-size: 24px; + color: black; +} + +h3 { + font-size: 18px; +} + +h4 { + font-size: 16px; +} + +h5 { + font-size: 14px; +} + +h6 { + color: #777777; + font-size: 14px; +} + +p, blockquote, ul, ol, dl, li, table, pre { + margin: 5px 0 15px; + -moz-font-feature-settings: "onum"; + -ms-font-feature-settings: "onum"; + -webkit-font-feature-settings: "onum"; + font-feature-settings: "onum"; +} + +hr { + background: transparent repeat-x 0 0; + border: 0 none; + color: #cccccc; + height: 4px; + padding: 0; +} + +body > h2:first-child { + margin-top: 0; + padding-top: 0; +} +body > h1:first-child { + margin-top: 0; + padding-top: 0; +} +body > h1:first-child + h2 { + margin-top: 0; + padding-top: 0; +} +body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child { + margin-top: 0; + padding-top: 0; +} + +a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 { + margin-top: 0; + padding-top: 0; +} + +h1 p, h2 p, h3 p, h4 p, h5 p, h6 p { + margin-top: 0; +} + +li p.first { + display: inline-block; +} + +ul, ol { + padding-left: 30px; +} + +ul :first-child, ol :first-child { + margin-top: 0; +} + +ul :last-child, ol :last-child { + margin-bottom: 0; +} + +dl { + padding: 0; +} +dl dt { + font-size: 14px; + font-weight: bold; + font-style: italic; + padding: 0; + margin: 15px 0 5px; +} +dl dt:first-child { + padding: 0; +} +dl dt > :first-child { + margin-top: 0; +} +dl dt > :last-child { + margin-bottom: 0; +} +dl dd { + margin: 0 0 15px; + padding: 0 15px; +} +dl dd > :first-child { + margin-top: 0; +} +dl dd > :last-child { + margin-bottom: 0; +} + +blockquote { + border-left: 4px solid #dddddd; + padding: 0 15px; + color: #222222; +} +blockquote > :first-child { + margin-top: 0; +} +blockquote > :last-child { + margin-bottom: 0; +} +blockquote:before { + content: "Example"; + color: #777777; + font-size: 14px; + font-weight: bold; +} + +table { + padding: 0; + margin: 0; + border: none; + border-collapse: collapse; +} +table tr { + background-color: white; +} +table tr:nth-child(2n) { + background-color: #f8f8f8; +} +table tr th { + background-color: #EAEAEA; + font-weight: bold; + text-align: left; + padding: 5px 13px; +} +table tr td { + text-align: left; + padding: 5px 13px; +} +table tr th :first-child, table tr td :first-child { + margin-top: 0; +} +table tr th :last-child, table tr td :last-child { + margin-bottom: 0; +} + +img { + max-width: 100%; +} + +span.frame { + display: block; + overflow: hidden; +} +span.frame > span { + border: 1px solid #dddddd; + display: block; + float: left; + overflow: hidden; + margin: 13px 0 0; + padding: 7px; + width: auto; +} +span.frame span img { + display: block; + float: left; +} +span.frame span span { + clear: both; + color: #333333; + display: block; + padding: 5px 0 0; +} +span.align-center { + display: block; + overflow: hidden; + clear: both; +} +span.align-center > span { + display: block; + overflow: hidden; + margin: 13px auto 0; + text-align: center; +} +span.align-center span img { + margin: 0 auto; + text-align: center; +} +span.align-right { + display: block; + overflow: hidden; + clear: both; +} +span.align-right > span { + display: block; + overflow: hidden; + margin: 13px 0 0; + text-align: right; +} +span.align-right span img { + margin: 0; + text-align: right; +} +span.float-left { + display: block; + margin-right: 13px; + overflow: hidden; + float: left; +} +span.float-left span { + margin: 13px 0 0; +} +span.float-right { + display: block; + margin-left: 13px; + overflow: hidden; + float: right; +} +span.float-right > span { + display: block; + overflow: hidden; + margin: 13px auto 0; + text-align: right; +} + +pre, code, tt { + font:14px "Luxi Mono", 'andale mono', 'lucida console', monospace; + line-height:1.5; +} + +.highlight pre { + background-color: #F8F8F8; + border-radius: 3px; + overflow: auto; + padding: 6px 10px; + white-space: nowrap; +} + +code { + background-color: transparent; + border: none; + margin: 0; + padding: 0; + white-space: pre; +} + +aside.left { + height: 100%; + position: fixed; + direction: rtl; + overflow: auto; + left: 0px; + width: 320px; + bottom: -32px; + font-family: "Luxi Sans", serif; + background-color: #073642; +} + +aside.left > nav { + direction: ltr; + top: 32px; + padding-bottom: 32px; +} + +article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { + display: block; +} + +audio, canvas, img, svg, video { + vertical-align: middle; +} + +audio, canvas, progress, video { + display: inline-block; + vertical-align: baseline; +} + +main { + position: relative; + top: 32px; + margin: 0 0 0 320px; + padding: 0px 32px; + max-width: 800px; + min-width: 800px; + min-height: 580px; + background-color: #FFF; +} + +header { + position: fixed; + top: 0px; + left: 0px; + height: 32px; + width: 100%; + background-color: #002B36; + margin: 0px 0px; + padding: 0px 0px; + font-family: "Luxi Sans", serif; + font-weight: bold; + z-index: 10; + overflow: hidden; + text-shadow: 1px 1px 0px rgba(0, 43, 54, 0.15); +} + +#chapters a { + color: #FFFFFF; + text-decoration: none; + font-size: 0.63vw; + padding: 100% 5px; +} + +#chapters a:hover, #chapters a:focus, #github:hover, #github:focus { + background: #DC322F; + -webkit-transition: background .2s ease-in; + -moz-transition: background .2s ease-in; + -ms-transition: background .2s ease-in; + -o-transition: background .2s ease-in; + transition: background .2s ease-in; +} + +#chapters a.chapter-active { + background: #72D0EB; +} + + +#toc ul { + margin: 0; + padding: 0; + list-style: none; +} + +#toc li { + margin: 0; + padding: 0; +} + +#toc a { + color: #FFFFFF; /*#073642;*/ + font-weight: bold; + font-size: 12px; + display: block; + text-shadow: 1px 1px 0px rgba(0, 43, 54, 0.15); +} + +#toc a:hover, #toc a:focus { + background: #DC322F; + text-decoration: none; + -webkit-transition: background .2s ease-in; + -moz-transition: background .2s ease-in; + -ms-transition: background .2s ease-in; + -o-transition: background .2s ease-in; + transition: background .2s ease-in; +} + +#toc .toc-h1 { + display: none; +} + +#toc .toc-h2 a { + padding-left: 10px; +} + +#toc .toc-h3 a { + padding-left: 30px; +} + +#toc .toc-active { + background: #72D0EB; +} + +#toc .toc-active a { + color: #FFFFFF; +} + +#chapters > #github { + padding: 0px; + float: right; +} + +.hljs{ + background: #f8f8f8; +} +/* proper rendering of MathJax into highlighted code blocks */ +.fixws { white-space: pre; } +.fixws .math { white-space: nowrap; } + +.version-notice { + background-color: #C93A3A; + color: #f2f2f2; + border:1px solid #ccc; + padding: 1em; + margin-bottom: 1em; +} +.version-notice a { + color: #f2f2f2; + font-weight: bold; + text-decoration: underline; +} + +.print-only { + display: none; +} diff --git a/docs/_spec/spec-toc.xslt b/docs/_spec/spec-toc.xslt new file mode 100644 index 000000000000..437b15e3e6f4 --- /dev/null +++ b/docs/_spec/spec-toc.xslt @@ -0,0 +1,64 @@ + + + + + + + Table of Contents + + + ./public/stylesheets/fonts.css + + + + +

      Table of Contents

      +
      + + +
      + +
    • + + + +
        + added to prevent self-closing tags in QtXmlPatterns + +
      +
    • + + From 34b836b463410ba2f7a44a0f3fe1c72385cf4f8f Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 13:51:03 +0200 Subject: [PATCH 101/657] Add copy of reference as TODO reference taken from https://github.com/lampepfl/dotty/commit/7919cb375a3d1c931ed3841617774bdd8c49c62c --- .../changed-features/changed-features.md | 7 + .../changed-features/compiler-plugins.md | 128 +++ .../changed-features/eta-expansion-spec.md | 77 ++ .../changed-features/eta-expansion.md | 42 + .../implicit-conversions-spec.md | 117 +++ .../changed-features/implicit-conversions.md | 65 ++ .../changed-features/implicit-resolution.md | 169 ++++ .../TODOreference/changed-features/imports.md | 60 ++ .../changed-features/interpolation-escapes.md | 14 + .../changed-features/lazy-vals-init.md | 80 ++ .../changed-features/main-functions.md | 87 ++ .../changed-features/match-syntax.md | 56 ++ .../changed-features/numeric-literals.md | 7 + .../changed-features/operators.md | 173 ++++ .../changed-features/overload-resolution.md | 102 +++ .../changed-features/pattern-bindings.md | 59 ++ .../changed-features/pattern-matching.md | 243 ++++++ .../changed-features/structural-types-spec.md | 153 ++++ .../changed-features/structural-types.md | 191 ++++ .../changed-features/type-checking.md | 7 + .../changed-features/type-inference.md | 10 + .../changed-features/vararg-splices.md | 40 + .../changed-features/wildcards.md | 50 ++ .../contextual/by-name-context-parameters.md | 65 ++ .../contextual/context-bounds.md | 53 ++ .../contextual/context-functions-spec.md | 79 ++ .../contextual/context-functions.md | 154 ++++ .../TODOreference/contextual/contextual.md | 83 ++ .../TODOreference/contextual/conversions.md | 76 ++ .../contextual/derivation-macro.md | 205 +++++ .../TODOreference/contextual/derivation.md | 425 +++++++++ .../contextual/extension-methods.md | 306 +++++++ .../TODOreference/contextual/given-imports.md | 117 +++ docs/_spec/TODOreference/contextual/givens.md | 193 ++++ .../contextual/multiversal-equality.md | 227 +++++ .../contextual/relationship-implicits.md | 206 +++++ .../right-associative-extension-methods.md | 52 ++ .../TODOreference/contextual/type-classes.md | 282 ++++++ .../TODOreference/contextual/using-clauses.md | 153 ++++ .../dropped-features/auto-apply.md | 96 ++ .../dropped-features/class-shadowing-spec.md | 26 + .../dropped-features/class-shadowing.md | 33 + .../dropped-features/delayed-init.md | 32 + .../dropped-features/do-while.md | 41 + .../dropped-features/dropped-features.md | 7 + .../dropped-features/early-initializers.md | 16 + .../dropped-features/existential-types.md | 35 + .../TODOreference/dropped-features/limit22.md | 17 + .../TODOreference/dropped-features/macros.md | 16 + .../dropped-features/nonlocal-returns.md | 29 + .../dropped-features/package-objects.md | 48 + .../dropped-features/procedure-syntax.md | 19 + .../TODOreference/dropped-features/symlits.md | 24 + .../dropped-features/this-qualifier.md | 33 + .../dropped-features/type-projection.md | 18 + .../dropped-features/weak-conformance-spec.md | 54 ++ .../dropped-features/weak-conformance.md | 47 + .../dropped-features/wildcard-init.md | 23 + .../TODOreference/dropped-features/xml.md | 39 + docs/_spec/TODOreference/enums/adts.md | 173 ++++ .../_spec/TODOreference/enums/desugarEnums.md | 215 +++++ docs/_spec/TODOreference/enums/enums-index.md | 7 + docs/_spec/TODOreference/enums/enums.md | 222 +++++ .../TODOreference/experimental/canthrow.md | 281 ++++++ docs/_spec/TODOreference/experimental/cc.md | 738 ++++++++++++++++ .../experimental/erased-defs-spec.md | 64 ++ .../TODOreference/experimental/erased-defs.md | 231 +++++ .../experimental/explicit-nulls.md | 543 ++++++++++++ .../experimental/fewer-braces.md | 7 + .../experimental/main-annotation.md | 97 +++ .../experimental/named-typeargs-spec.md | 41 + .../experimental/named-typeargs.md | 34 + .../experimental/numeric-literals.md | 257 ++++++ .../TODOreference/experimental/overview.md | 29 + .../experimental/tupled-function.md | 82 ++ .../TODOreference/features-classification.md | 199 +++++ .../language-versions/binary-compatibility.md | 13 + .../language-versions/language-versions.md | 7 + .../language-versions/source-compatibility.md | 43 + .../metaprogramming/compiletime-ops.md | 294 +++++++ .../TODOreference/metaprogramming/inline.md | 390 +++++++++ .../metaprogramming/macros-spec.md | 254 ++++++ .../TODOreference/metaprogramming/macros.md | 823 ++++++++++++++++++ .../metaprogramming/metaprogramming.md | 47 + .../metaprogramming/reflection.md | 131 +++ .../metaprogramming/simple-smp.md | 232 +++++ .../TODOreference/metaprogramming/staging.md | 121 +++ .../metaprogramming/tasty-inspect.md | 57 ++ .../dependent-function-types-spec.md | 125 +++ .../new-types/dependent-function-types.md | 49 ++ .../new-types/intersection-types-spec.md | 108 +++ .../new-types/intersection-types.md | 68 ++ .../TODOreference/new-types/match-types.md | 247 ++++++ .../TODOreference/new-types/new-types.md | 7 + .../new-types/polymorphic-function-types.md | 94 ++ .../new-types/type-lambdas-spec.md | 116 +++ .../TODOreference/new-types/type-lambdas.md | 17 + .../new-types/union-types-spec.md | 172 ++++ .../TODOreference/new-types/union-types.md | 46 + .../other-new-features/control-syntax.md | 47 + .../creator-applications.md | 57 ++ .../other-new-features/experimental-defs.md | 318 +++++++ .../other-new-features/export.md | 234 +++++ .../other-new-features/indentation.md | 509 +++++++++++ .../other-new-features/kind-polymorphism.md | 47 + .../other-new-features/matchable.md | 141 +++ .../other-new-features/opaques-details.md | 126 +++ .../other-new-features/opaques.md | 179 ++++ .../other-new-features/open-classes.md | 80 ++ .../other-new-features/other-new-features.md | 7 + .../parameter-untupling-spec.md | 89 ++ .../other-new-features/parameter-untupling.md | 77 ++ .../other-new-features/safe-initialization.md | 343 ++++++++ .../other-new-features/targetName.md | 118 +++ .../threadUnsafe-annotation.md | 18 + .../other-new-features/trait-parameters.md | 88 ++ .../other-new-features/transparent-traits.md | 70 ++ .../other-new-features/type-test.md | 181 ++++ docs/_spec/TODOreference/overview.md | 155 ++++ docs/_spec/TODOreference/soft-modifier.md | 27 + docs/_spec/TODOreference/syntax.md | 472 ++++++++++ 121 files changed, 15330 insertions(+) create mode 100644 docs/_spec/TODOreference/changed-features/changed-features.md create mode 100644 docs/_spec/TODOreference/changed-features/compiler-plugins.md create mode 100644 docs/_spec/TODOreference/changed-features/eta-expansion-spec.md create mode 100644 docs/_spec/TODOreference/changed-features/eta-expansion.md create mode 100644 docs/_spec/TODOreference/changed-features/implicit-conversions-spec.md create mode 100644 docs/_spec/TODOreference/changed-features/implicit-conversions.md create mode 100644 docs/_spec/TODOreference/changed-features/implicit-resolution.md create mode 100644 docs/_spec/TODOreference/changed-features/imports.md create mode 100644 docs/_spec/TODOreference/changed-features/interpolation-escapes.md create mode 100644 docs/_spec/TODOreference/changed-features/lazy-vals-init.md create mode 100644 docs/_spec/TODOreference/changed-features/main-functions.md create mode 100644 docs/_spec/TODOreference/changed-features/match-syntax.md create mode 100644 docs/_spec/TODOreference/changed-features/numeric-literals.md create mode 100644 docs/_spec/TODOreference/changed-features/operators.md create mode 100644 docs/_spec/TODOreference/changed-features/overload-resolution.md create mode 100644 docs/_spec/TODOreference/changed-features/pattern-bindings.md create mode 100644 docs/_spec/TODOreference/changed-features/pattern-matching.md create mode 100644 docs/_spec/TODOreference/changed-features/structural-types-spec.md create mode 100644 docs/_spec/TODOreference/changed-features/structural-types.md create mode 100644 docs/_spec/TODOreference/changed-features/type-checking.md create mode 100644 docs/_spec/TODOreference/changed-features/type-inference.md create mode 100644 docs/_spec/TODOreference/changed-features/vararg-splices.md create mode 100644 docs/_spec/TODOreference/changed-features/wildcards.md create mode 100644 docs/_spec/TODOreference/contextual/by-name-context-parameters.md create mode 100644 docs/_spec/TODOreference/contextual/context-bounds.md create mode 100644 docs/_spec/TODOreference/contextual/context-functions-spec.md create mode 100644 docs/_spec/TODOreference/contextual/context-functions.md create mode 100644 docs/_spec/TODOreference/contextual/contextual.md create mode 100644 docs/_spec/TODOreference/contextual/conversions.md create mode 100644 docs/_spec/TODOreference/contextual/derivation-macro.md create mode 100644 docs/_spec/TODOreference/contextual/derivation.md create mode 100644 docs/_spec/TODOreference/contextual/extension-methods.md create mode 100644 docs/_spec/TODOreference/contextual/given-imports.md create mode 100644 docs/_spec/TODOreference/contextual/givens.md create mode 100644 docs/_spec/TODOreference/contextual/multiversal-equality.md create mode 100644 docs/_spec/TODOreference/contextual/relationship-implicits.md create mode 100644 docs/_spec/TODOreference/contextual/right-associative-extension-methods.md create mode 100644 docs/_spec/TODOreference/contextual/type-classes.md create mode 100644 docs/_spec/TODOreference/contextual/using-clauses.md create mode 100644 docs/_spec/TODOreference/dropped-features/auto-apply.md create mode 100644 docs/_spec/TODOreference/dropped-features/class-shadowing-spec.md create mode 100644 docs/_spec/TODOreference/dropped-features/class-shadowing.md create mode 100644 docs/_spec/TODOreference/dropped-features/delayed-init.md create mode 100644 docs/_spec/TODOreference/dropped-features/do-while.md create mode 100644 docs/_spec/TODOreference/dropped-features/dropped-features.md create mode 100644 docs/_spec/TODOreference/dropped-features/early-initializers.md create mode 100644 docs/_spec/TODOreference/dropped-features/existential-types.md create mode 100644 docs/_spec/TODOreference/dropped-features/limit22.md create mode 100644 docs/_spec/TODOreference/dropped-features/macros.md create mode 100644 docs/_spec/TODOreference/dropped-features/nonlocal-returns.md create mode 100644 docs/_spec/TODOreference/dropped-features/package-objects.md create mode 100644 docs/_spec/TODOreference/dropped-features/procedure-syntax.md create mode 100644 docs/_spec/TODOreference/dropped-features/symlits.md create mode 100644 docs/_spec/TODOreference/dropped-features/this-qualifier.md create mode 100644 docs/_spec/TODOreference/dropped-features/type-projection.md create mode 100644 docs/_spec/TODOreference/dropped-features/weak-conformance-spec.md create mode 100644 docs/_spec/TODOreference/dropped-features/weak-conformance.md create mode 100644 docs/_spec/TODOreference/dropped-features/wildcard-init.md create mode 100644 docs/_spec/TODOreference/dropped-features/xml.md create mode 100644 docs/_spec/TODOreference/enums/adts.md create mode 100644 docs/_spec/TODOreference/enums/desugarEnums.md create mode 100644 docs/_spec/TODOreference/enums/enums-index.md create mode 100644 docs/_spec/TODOreference/enums/enums.md create mode 100644 docs/_spec/TODOreference/experimental/canthrow.md create mode 100644 docs/_spec/TODOreference/experimental/cc.md create mode 100644 docs/_spec/TODOreference/experimental/erased-defs-spec.md create mode 100644 docs/_spec/TODOreference/experimental/erased-defs.md create mode 100644 docs/_spec/TODOreference/experimental/explicit-nulls.md create mode 100644 docs/_spec/TODOreference/experimental/fewer-braces.md create mode 100644 docs/_spec/TODOreference/experimental/main-annotation.md create mode 100644 docs/_spec/TODOreference/experimental/named-typeargs-spec.md create mode 100644 docs/_spec/TODOreference/experimental/named-typeargs.md create mode 100644 docs/_spec/TODOreference/experimental/numeric-literals.md create mode 100644 docs/_spec/TODOreference/experimental/overview.md create mode 100644 docs/_spec/TODOreference/experimental/tupled-function.md create mode 100644 docs/_spec/TODOreference/features-classification.md create mode 100644 docs/_spec/TODOreference/language-versions/binary-compatibility.md create mode 100644 docs/_spec/TODOreference/language-versions/language-versions.md create mode 100644 docs/_spec/TODOreference/language-versions/source-compatibility.md create mode 100644 docs/_spec/TODOreference/metaprogramming/compiletime-ops.md create mode 100644 docs/_spec/TODOreference/metaprogramming/inline.md create mode 100644 docs/_spec/TODOreference/metaprogramming/macros-spec.md create mode 100644 docs/_spec/TODOreference/metaprogramming/macros.md create mode 100644 docs/_spec/TODOreference/metaprogramming/metaprogramming.md create mode 100644 docs/_spec/TODOreference/metaprogramming/reflection.md create mode 100644 docs/_spec/TODOreference/metaprogramming/simple-smp.md create mode 100644 docs/_spec/TODOreference/metaprogramming/staging.md create mode 100644 docs/_spec/TODOreference/metaprogramming/tasty-inspect.md create mode 100644 docs/_spec/TODOreference/new-types/dependent-function-types-spec.md create mode 100644 docs/_spec/TODOreference/new-types/dependent-function-types.md create mode 100644 docs/_spec/TODOreference/new-types/intersection-types-spec.md create mode 100644 docs/_spec/TODOreference/new-types/intersection-types.md create mode 100644 docs/_spec/TODOreference/new-types/match-types.md create mode 100644 docs/_spec/TODOreference/new-types/new-types.md create mode 100644 docs/_spec/TODOreference/new-types/polymorphic-function-types.md create mode 100644 docs/_spec/TODOreference/new-types/type-lambdas-spec.md create mode 100644 docs/_spec/TODOreference/new-types/type-lambdas.md create mode 100644 docs/_spec/TODOreference/new-types/union-types-spec.md create mode 100644 docs/_spec/TODOreference/new-types/union-types.md create mode 100644 docs/_spec/TODOreference/other-new-features/control-syntax.md create mode 100644 docs/_spec/TODOreference/other-new-features/creator-applications.md create mode 100644 docs/_spec/TODOreference/other-new-features/experimental-defs.md create mode 100644 docs/_spec/TODOreference/other-new-features/export.md create mode 100644 docs/_spec/TODOreference/other-new-features/indentation.md create mode 100644 docs/_spec/TODOreference/other-new-features/kind-polymorphism.md create mode 100644 docs/_spec/TODOreference/other-new-features/matchable.md create mode 100644 docs/_spec/TODOreference/other-new-features/opaques-details.md create mode 100644 docs/_spec/TODOreference/other-new-features/opaques.md create mode 100644 docs/_spec/TODOreference/other-new-features/open-classes.md create mode 100644 docs/_spec/TODOreference/other-new-features/other-new-features.md create mode 100644 docs/_spec/TODOreference/other-new-features/parameter-untupling-spec.md create mode 100644 docs/_spec/TODOreference/other-new-features/parameter-untupling.md create mode 100644 docs/_spec/TODOreference/other-new-features/safe-initialization.md create mode 100644 docs/_spec/TODOreference/other-new-features/targetName.md create mode 100644 docs/_spec/TODOreference/other-new-features/threadUnsafe-annotation.md create mode 100644 docs/_spec/TODOreference/other-new-features/trait-parameters.md create mode 100644 docs/_spec/TODOreference/other-new-features/transparent-traits.md create mode 100644 docs/_spec/TODOreference/other-new-features/type-test.md create mode 100644 docs/_spec/TODOreference/overview.md create mode 100644 docs/_spec/TODOreference/soft-modifier.md create mode 100644 docs/_spec/TODOreference/syntax.md diff --git a/docs/_spec/TODOreference/changed-features/changed-features.md b/docs/_spec/TODOreference/changed-features/changed-features.md new file mode 100644 index 000000000000..cacdc2598a02 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/changed-features.md @@ -0,0 +1,7 @@ +--- +layout: index +title: "Other Changed Features" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features.html +--- + +The following pages document the features that have changed in Scala 3, compared to Scala 2. diff --git a/docs/_spec/TODOreference/changed-features/compiler-plugins.md b/docs/_spec/TODOreference/changed-features/compiler-plugins.md new file mode 100644 index 000000000000..20bdb7f49836 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/compiler-plugins.md @@ -0,0 +1,128 @@ +--- +layout: doc-page +title: "Changes in Compiler Plugins" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/compiler-plugins.html +--- + +Compiler plugins are supported by Dotty (and Scala 3) since 0.9. There are two notable changes +compared to `scalac`: + +- No support for analyzer plugins +- Added support for research plugins + +[Analyzer plugins][1] in `scalac` run during type checking and may influence +normal type checking. This is a very powerful feature but for production usages, +a predictable and consistent type checker is more important. + +For experimentation and research, Scala 3 introduces _research plugin_. Research plugins +are more powerful than `scalac` analyzer plugins as they let plugin authors customize +the whole compiler pipeline. One can easily replace the standard typer by a custom one or +create a parser for a domain-specific language. However, research plugins are only +enabled for nightly or snaphot releases of Scala 3. + +Common plugins that add new phases to the compiler pipeline are called +_standard plugins_ in Scala 3. In terms of features, they are similar to +`scalac` plugins, despite minor changes in the API. + +## Using Compiler Plugins + +Both standard and research plugins can be used with `scalac` by adding the `-Xplugin:` option: + +```shell +scalac -Xplugin:pluginA.jar -Xplugin:pluginB.jar Test.scala +``` + +The compiler will examine the jar provided, and look for a property file named +`plugin.properties` in the root directory of the jar. The property file specifies +the fully qualified plugin class name. The format of a property file is as follows: + +```properties +pluginClass=dividezero.DivideZero +``` + +This is different from `scalac` plugins that required a `scalac-plugin.xml` file. + +Starting from 1.1.5, `sbt` also supports Scala 3 compiler plugins. Please refer to the +[`sbt` documentation][2] for more information. + +## Writing a Standard Compiler Plugin + +Here is the source code for a simple compiler plugin that reports integer divisions by +zero as errors. + +```scala +package dividezero + +import dotty.tools.dotc.ast.Trees.* +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Decorators.* +import dotty.tools.dotc.core.StdNames.* +import dotty.tools.dotc.core.Symbols.* +import dotty.tools.dotc.plugins.{PluginPhase, StandardPlugin} +import dotty.tools.dotc.transform.{Pickler, Staging} + +class DivideZero extends StandardPlugin: + val name: String = "divideZero" + override val description: String = "divide zero check" + + def init(options: List[String]): List[PluginPhase] = + (new DivideZeroPhase) :: Nil + +class DivideZeroPhase extends PluginPhase: + import tpd.* + + val phaseName = "divideZero" + + override val runsAfter = Set(Pickler.name) + override val runsBefore = Set(Staging.name) + + override def transformApply(tree: Apply)(implicit ctx: Context): Tree = + tree match + case Apply(Select(rcvr, nme.DIV), List(Literal(Constant(0)))) + if rcvr.tpe <:< defn.IntType => + report.error("dividing by zero", tree.pos) + case _ => + () + tree +end DivideZeroPhase +``` + +The plugin main class (`DivideZero`) must extend the trait `StandardPlugin` +and implement the method `init` that takes the plugin's options as argument +and returns a list of `PluginPhase`s to be inserted into the compilation pipeline. + +Our plugin adds one compiler phase to the pipeline. A compiler phase must extend +the `PluginPhase` trait. In order to specify when the phase is executed, we also +need to specify a `runsBefore` and `runsAfter` constraints that are list of phase +names. + +We can now transform trees by overriding methods like `transformXXX`. + +## Writing a Research Compiler Plugin + +Here is a template for research plugins. + +```scala +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Phases.Phase +import dotty.tools.dotc.plugins.ResearchPlugin + +class DummyResearchPlugin extends ResearchPlugin: + val name: String = "dummy" + override val description: String = "dummy research plugin" + + def init(options: List[String], phases: List[List[Phase]])(implicit ctx: Context): List[List[Phase]] = + phases +end DummyResearchPlugin +``` + +A research plugin must extend the trait `ResearchPlugin` and implement the +method `init` that takes the plugin's options as argument as well as the compiler +pipeline in the form of a list of compiler phases. The method can replace, remove +or add any phases to the pipeline and return the updated pipeline. + + +[1]: https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/nsc/typechecker/AnalyzerPlugins.scala +[2]: https://www.scala-sbt.org/1.x/docs/Compiler-Plugins.html diff --git a/docs/_spec/TODOreference/changed-features/eta-expansion-spec.md b/docs/_spec/TODOreference/changed-features/eta-expansion-spec.md new file mode 100644 index 000000000000..a62d45df9e11 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/eta-expansion-spec.md @@ -0,0 +1,77 @@ +--- +layout: doc-page +title: "Automatic Eta Expansion - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/eta-expansion-spec.html +--- + +## Motivation + +Scala maintains a convenient distinction between _methods_ and _functions_. +Methods are part of the definition of a class that can be invoked in objects while functions are complete objects themselves, making them first-class entities. For example, they can be assigned to variables. +These two mechanisms are bridged in Scala by a mechanism called +[_eta-expansion_](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#eta-expansion-section) +(also called eta-abstraction), which converts a reference to a method into a function. Intuitively, a method `m` can be passed around by turning it into an object: the function `x => m(x)`. + +In this snippet which assigns a method to a `val`, the compiler will perform _automatic eta-expansion_, as shown in the comment: + +```scala +def m(x: Int, y: String) = ??? +val f = m // becomes: val f = (x: Int, y: String) => m(x, y) +``` + +In Scala 2, a method reference `m` is converted to a function value only if the expected type is a function type, which means the conversion in the example above would not have been triggered, because `val f` does not have a type ascription. To still get eta-expansion, a shortcut `m _` would force the conversion. + +For methods with one or more parameters like in the example above, this restriction has now been dropped. The syntax `m _` is no longer needed and will be deprecated in the future. + +## Automatic eta-expansion and partial application +In the following example `m` can be partially applied to the first two parameters. +Assigning `m` to `f1` will automatically eta-expand. + +```scala +def m(x: Boolean, y: String)(z: Int): List[Int] +val f1 = m +val f2 = m(true, "abc") +``` + +This creates two function values: + +```scala +f1: (Boolean, String) => Int => List[Int] +f2: Int => List[Int] +``` + +## Automatic eta-expansion and implicit parameter lists + +Methods with implicit parameter lists will always get applied to implicit arguments. + +```scala +def foo(x: Int)(implicit p: Double): Float = ??? +implicit val bla: Double = 1.0 + +val bar = foo // val bar: Int => Float = ... +``` + +## Automatic Eta-Expansion and query types + +A method with context parameters can be expanded to a value of a context type by writing the expected context type explicitly. + +```scala +def foo(x: Int)(using p: Double): Float = ??? +val bar: Double ?=> Float = foo(3) +``` + +## Rules + +- If `m` has an argument list with one or more parameters, we always eta-expand +- If `m` is has an empty argument list (i.e. has type `()R`): + 1. If the expected type is of the form `() => T`, we eta expand. + 2. If m is defined by Java, or overrides a Java defined method, we insert `()`. + 3. Otherwise we issue an error of the form: + +Thus, an unapplied method with an empty argument list is only converted to a function when a function type is expected. It is considered best practice to either explicitly apply the method to `()`, or convert it to a function with `() => m()`. + +The method value syntax `m _` is deprecated. + +## Reference + +For more information, see [PR #2701](https://github.com/lampepfl/dotty/pull/2701). diff --git a/docs/_spec/TODOreference/changed-features/eta-expansion.md b/docs/_spec/TODOreference/changed-features/eta-expansion.md new file mode 100644 index 000000000000..c05378135e54 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/eta-expansion.md @@ -0,0 +1,42 @@ +--- +layout: doc-page +title: "Automatic Eta Expansion" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/eta-expansion.html +--- + +The conversion of _methods_ into _functions_ has been improved and happens automatically for methods with one or more parameters. + +```scala +def m(x: Boolean, y: String)(z: Int): List[Int] +val f1 = m +val f2 = m(true, "abc") +``` + +This creates two function values: +```scala +f1: (Boolean, String) => Int => List[Int] +f2: Int => List[Int] +``` + +The syntax `m _` is no longer needed and will be deprecated in the future. + +## Automatic eta-expansion and nullary methods + +Automatic eta expansion does not apply to "nullary" methods that take an empty parameter list. + +```scala +def next(): T +``` + +Given a simple reference to `next` does not auto-convert to a function. +One has to write explicitly `() => next()` to achieve that. +Once again since the `_` is going to be deprecated it's better to write it this way +rather than `next _`. + +The reason for excluding nullary methods from automatic eta expansion +is that Scala implicitly inserts the `()` argument, which would +conflict with eta expansion. Automatic `()` insertion is +[limited](../dropped-features/auto-apply.md) in Scala 3, but the fundamental ambiguity +remains. + +[More details](eta-expansion-spec.md) diff --git a/docs/_spec/TODOreference/changed-features/implicit-conversions-spec.md b/docs/_spec/TODOreference/changed-features/implicit-conversions-spec.md new file mode 100644 index 000000000000..dc19e10c8b8f --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/implicit-conversions-spec.md @@ -0,0 +1,117 @@ +--- +layout: doc-page +title: "Implicit Conversions - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/implicit-conversions-spec.html +--- + +## Implementation + +An implicit conversion, or _view_, from type `S` to type `T` is +defined by either: + +- An `implicit def` which has type `S => T` or `(=> S) => T` +- An implicit value which has type `Conversion[S, T]` + +The standard library defines an abstract class [`Conversion`](https://scala-lang.org/api/3.x/scala/Conversion.html): + +```scala +package scala +@java.lang.FunctionalInterface +abstract class Conversion[-T, +U] extends Function1[T, U]: + def apply(x: T): U +``` + +Function literals are automatically converted to `Conversion` values. + +Views are applied in three situations: + +1. If an expression `e` is of type `T`, and `T` does not conform to + the expression's expected type `pt`. In this case, an implicit `v` + which is applicable to `e` and whose result type conforms to `pt` + is searched. The search proceeds as in the case of implicit + parameters, where the implicit scope is the one of `T => pt`. If + such a view is found, the expression `e` is converted to `v(e)`. +1. In a selection `e.m` with `e` of type `T`, if the selector `m` does + not denote an accessible member of `T`. In this case, a view `v` + which is applicable to `e` and whose result contains an accessible + member named `m` is searched. The search proceeds as in the case of + implicit parameters, where the implicit scope is the one of `T`. If + such a view is found, the selection `e.m` is converted to `v(e).m`. +1. In an application `e.m(args)` with `e` of type `T`, if the selector + `m` denotes some accessible member(s) of `T`, but none of these + members is applicable to the arguments `args`. In this case, a view + `v` which is applicable to `e` and whose result contains a method + `m` which is applicable to `args` is searched. The search proceeds + as in the case of implicit parameters, where the implicit scope is + the one of `T`. If such a view is found, the application + `e.m(args)` is converted to `v(e).m(args)`. + +# Differences with Scala 2 implicit conversions + +In Scala 2, views whose parameters are passed by-value take precedence +over views whose parameters are passed by-name. This is no longer the +case in Scala 3. A type error reporting the ambiguous conversions will +be emitted in cases where this rule would be applied in Scala 2: + +```scala +implicit def conv1(x: Int): String = x.toString +implicit def conv2(x: => Int): String = x.toString + +val x: String = 0 // Compiles in Scala2 (uses `conv1`), + // type error in Scala 3 because of ambiguity. +``` + +In Scala 2, implicit values of a function type would be considered as +potential views. In Scala 3, these implicit value need to have type +`Conversion`: + +```scala +// Scala 2: +def foo(x: Int)(implicit conv: Int => String): String = x + +// Becomes with Scala 3: +def foo(x: Int)(implicit conv: Conversion[Int, String]): String = x + +// Call site is unchanged: +foo(4)(_.toString) + +// Scala 2: +implicit val myConverter: Int => String = _.toString + +// Becomes with Scala 3: +implicit val myConverter: Conversion[Int, String] = _.toString +``` + +Note that implicit conversions are also affected by the [changes to implicit resolution](implicit-resolution.md) between Scala 2 and Scala 3. + +## Motivation for the changes + +The introduction of [`scala.Conversion`](https://scala-lang.org/api/3.x/scala/Conversion.html) +in Scala 3 and the decision to restrict implicit values of this type to be +considered as potential views comes from the desire to remove surprising +behavior from the language: + +```scala +implicit val m: Map[Int, String] = Map(1 -> "abc") + +val x: String = 1 // Scala 2: assigns "abc" to x + // Scala 3: type error +``` + +This snippet contains a type error. The right-hand side of `val x` +does not conform to type `String`. In Scala 2, the compiler will use +`m` as an implicit conversion from `Int` to `String`, whereas Scala 3 +will report a type error, because `Map` isn't an instance of +[`Conversion`](https://scala-lang.org/api/3.x/scala/Conversion.html). + +## Migration path + +Implicit values that are used as views should see their type changed to `Conversion`. + +For the migration of implicit conversions that are affected by the +changes to implicit resolution, refer to the [Changes in Implicit Resolution](implicit-resolution.md) for more information. + +## Reference + +For more information about implicit resolution, see [Changes in Implicit Resolution](implicit-resolution.md). +Other details are available in [PR #2065](https://github.com/lampepfl/dotty/pull/2065). diff --git a/docs/_spec/TODOreference/changed-features/implicit-conversions.md b/docs/_spec/TODOreference/changed-features/implicit-conversions.md new file mode 100644 index 000000000000..eef236f39a07 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/implicit-conversions.md @@ -0,0 +1,65 @@ +--- +layout: doc-page +title: "Implicit Conversions" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/implicit-conversions.html +--- + +An _implicit conversion_, also called _view_, is a conversion that +is applied by the compiler in several situations: + +1. When an expression `e` of type `T` is encountered, but the compiler + needs an expression of type `S`. +1. When an expression `e.m` where `e` has type `T` but `T` defines no + member `m` is encountered. + +In those cases, the compiler looks in the implicit scope for a +conversion that can convert an expression of type `T` to an expression +of type `S` (or to a type that defines a member `m` in the second +case). + +This conversion can be either: + +1. An `implicit def` of type `T => S` or `(=> T) => S` +1. An implicit value of type `scala.Conversion[T, S]` + +Defining an implicit conversion will emit a warning unless the import +`scala.language.implicitConversions` is in scope, or the flag +`-language:implicitConversions` is given to the compiler. + +## Examples + +The first example is taken from [`scala.Predef`](https://scala-lang.org/api/3.x/scala/Predef$.html). +Thanks to this implicit conversion, it is possible to pass a +[`scala.Int`](https://scala-lang.org/api/3.x/scala/Int.html) +to a Java method that expects a `java.lang.Integer` + +```scala +import scala.language.implicitConversions +implicit def int2Integer(x: Int): java.lang.Integer = + x.asInstanceOf[java.lang.Integer] +``` + +The second example shows how to use `Conversion` to define an +`Ordering` for an arbitrary type, given existing `Ordering`s for other +types: + +```scala +import scala.language.implicitConversions +implicit def ordT[T, S]( + implicit conv: Conversion[T, S], + ordS: Ordering[S] + ): Ordering[T] = + // `ordS` compares values of type `S`, but we can convert from `T` to `S` + (x: T, y: T) => ordS.compare(x, y) + +class A(val x: Int) // The type for which we want an `Ordering` + +// Convert `A` to a type for which an `Ordering` is available: +implicit val AToInt: Conversion[A, Int] = _.x + +implicitly[Ordering[Int]] // Ok, exists in the standard library +implicitly[Ordering[A]] // Ok, will use the implicit conversion from + // `A` to `Int` and the `Ordering` for `Int`. +``` + +[More details](implicit-conversions-spec.md) diff --git a/docs/_spec/TODOreference/changed-features/implicit-resolution.md b/docs/_spec/TODOreference/changed-features/implicit-resolution.md new file mode 100644 index 000000000000..bf15baa3299c --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/implicit-resolution.md @@ -0,0 +1,169 @@ +--- +layout: doc-page +title: "Changes in Implicit Resolution" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/implicit-resolution.html +--- + +This section describes changes to the implicit resolution that apply both to the new `given`s and to the old-style `implicit`s in Scala 3. +Implicit resolution uses a new algorithm which caches implicit results +more aggressively for performance. There are also some changes that +affect implicits on the language level. + +**1.** Types of implicit values and result types of implicit methods +must be explicitly declared. Excepted are only values in local blocks +where the type may still be inferred: +```scala + class C { + + val ctx: Context = ... // ok + + /*!*/ implicit val x = ... // error: type must be given explicitly + + /*!*/ implicit def y = ... // error: type must be given explicitly + } + val y = { + implicit val ctx = this.ctx // ok + ... + } +``` +**2.** Nesting is now taken into account for selecting an implicit. Consider for instance the following scenario: +```scala + def f(implicit i: C) = { + def g(implicit j: C) = { + implicitly[C] + } + } +``` +This will now resolve the `implicitly` call to `j`, because `j` is nested +more deeply than `i`. Previously, this would have resulted in an +ambiguity error. The previous possibility of an implicit search failure +due to _shadowing_ (where an implicit is hidden by a nested definition) +no longer applies. + +**3.** Package prefixes no longer contribute to the implicit search scope of a type. Example: +```scala + package p + + given a: A = A() + + object o: + given b: B = B() + type C +``` +Both `a` and `b` are visible as implicits at the point of the definition +of `type C`. However, a reference to `p.o.C` outside of package `p` will +have only `b` in its implicit search scope but not `a`. + +In more detail, here are the rules for what constitutes the implicit scope of +a type: + +**Definition:** A reference is an _anchor_ if it refers to an object, a class, a trait, an abstract type, an opaque type alias, or a match type alias. References to packages and package objects are anchors only under `-source:3.0-migration`. +Opaque type aliases count as anchors only outside the scope where their alias is visible. + +**Definition:** The _anchors_ of a type _T_ is a set of references defined as follows: + + 1. If _T_ is a reference to an anchor, _T_ itself plus, if _T_ is of the form _P#A_, the anchors of _P_. + 1. If _T_ is an alias of _U_, the anchors of _U_. + 1. If _T_ is a reference to a type parameter, the union of the anchors of both of its bounds. + 1. If _T_ is a singleton reference, the anchors of its underlying type, plus, + if _T_ is of the form _(P#x).type_, the anchors of _P_. + 1. If _T_ is the this-type _o.this_ of a static object _o_, the anchors of a term reference _o.type_ to that object. + 1. If _T_ is some other type, the union of the anchors of each constituent type of _T_. + + **Definition:** The _implicit scope_ of a type _T_ is the smallest set _S_ of term references such that + + 1. If _T_ is a reference to a class, _S_ includes a reference to the companion object + of the class, if it exists, as well as the implicit scopes of all of _T_'s parent classes. + 1. If _T_ is a reference to an object, _S_ includes _T_ itself as well as + the implicit scopes of all of _T_'s parent classes. + 1. If _T_ is a reference to an opaque type alias named _A_, _S_ includes + a reference to an object _A_ defined in the same scope as the type, if it exists, + as well as the implicit scope of _T_'s underlying type or bounds. + 1. If _T_ is a reference to an abstract type or match type alias + named _A_, _S_ includes a reference to an object _A_ defined in the same scope as the type, if it exists, as well as the implicit scopes of _T_'s given bounds. + 1. If _T_ is a reference to an anchor of the form _p.A_ then _S_ also includes + all term references on the path _p_. + 1. If _T_ is some other type, _S_ includes the implicit scopes of all anchors of _T_. + + +**4.** The treatment of ambiguity errors has changed. If an ambiguity is encountered in some recursive step of an implicit search, the ambiguity is propagated to the caller. + +Example: Say you have the following definitions: +```scala + class A + class B extends C + class C + implicit def a1: A + implicit def a2: A + implicit def b(implicit a: A): B + implicit def c: C +``` +and the query `implicitly[C]`. + +This query would now be classified as ambiguous. This makes sense, after all +there are two possible solutions, `b(a1)` and `b(a2)`, neither of which is better +than the other and both of which are better than the third solution, `c`. +By contrast, Scala 2 would have rejected the search for `A` as +ambiguous, and subsequently have classified the query `b(implicitly[A])` as a normal fail, +which means that the alternative `c` would be chosen as solution! + +Scala 2's somewhat puzzling behavior with respect to ambiguity has been exploited to implement +the analogue of a "negated" search in implicit resolution, where a query `Q1` fails if some +other query `Q2` succeeds and `Q1` succeeds if `Q2` fails. With the new cleaned up behavior +these techniques no longer work. But there is now a new special type [`scala.util.NotGiven`](https://scala-lang.org/api/3.x/scala/util/NotGiven.html) +which implements negation directly. For any query type `Q`, `NotGiven[Q]` succeeds if and only if +the implicit search for `Q` fails. + +**5.** The treatment of divergence errors has also changed. A divergent implicit is treated as a normal failure, after which alternatives are still tried. This also makes sense: Encountering a divergent implicit means that we assume that no finite solution can be found on the corresponding path, but another path can still be tried. By contrast, +most (but not all) divergence errors in Scala 2 would terminate the implicit search as a whole. + +**6.** Scala 2 gives a lower level of priority to implicit conversions with call-by-name parameters relative to implicit conversions with call-by-value parameters. Scala 3 drops this distinction. So the following code snippet would be ambiguous in Scala 3: + +```scala + implicit def conv1(x: Int): A = new A(x) + implicit def conv2(x: => Int): A = new A(x) + def buzz(y: A) = ??? + buzz(1) // error: ambiguous +``` +**7.** The rule for picking a _most specific_ alternative among a set of overloaded or implicit alternatives is refined to take context parameters into account. All else being equal, an alternative that takes some context parameters is taken to be less specific than an alternative that takes none. If both alternatives take context parameters, we try to choose between them as if they were methods with regular parameters. The following paragraph in the [SLS §6.26.3](https://scala-lang.org/files/archive/spec/2.13/06-expressions.html#overloading-resolution) is affected by this change: + +_Original version:_ + +> An alternative A is _more specific_ than an alternative B if the relative weight of A over B is greater than the relative weight of B over A. + +_Modified version:_ + +An alternative A is _more specific_ than an alternative B if + + - the relative weight of A over B is greater than the relative weight of B over A, or + - the relative weights are the same, and A takes no implicit parameters but B does, or + - the relative weights are the same, both A and B take implicit parameters, and A is more specific than B if all implicit parameters in either alternative are replaced by regular parameters. + +**8.** The previous disambiguation of implicits based on inheritance depth is refined to make it transitive. Transitivity is important to guarantee that search outcomes are compilation-order independent. Here's a scenario where the previous rules violated transitivity: +```scala + class A extends B + object A { given a ... } + class B + object B extends C { given b ... } + class C { given c } +``` + Here `a` is more specific than `b` since the companion class `A` is a subclass of the companion class `B`. Also, `b` is more specific than `c` + since `object B` extends class `C`. But `a` is not more specific than `c`. This means if `a, b, c` are all applicable implicits, it makes + a difference in what order they are compared. If we compare `b` and `c` + first, we keep `b` and drop `c`. Then, comparing `a` with `b` we keep `a`. But if we compare `a` with `c` first, we fail with an ambiguity error. + +The new rules are as follows: An implicit `a` defined in `A` is more specific than an implicit `b` defined in `B` if + + - `A` extends `B`, or + - `A` is an object and the companion class of `A` extends `B`, or + - `A` and `B` are objects, + `B` does not inherit any implicit members from base classes (*), + and the companion class of `A` extends the companion class of `B`. + +Condition (*) is new. It is necessary to ensure that the defined relation is transitive. + + + + + +[//]: # todo: expand with precise rules diff --git a/docs/_spec/TODOreference/changed-features/imports.md b/docs/_spec/TODOreference/changed-features/imports.md new file mode 100644 index 000000000000..2058ef08b7db --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/imports.md @@ -0,0 +1,60 @@ +--- +layout: doc-page +title: "Imports" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/imports.html +--- + +The syntax of wildcard and renaming imports (and exports) has changed. + +## Wildcard Imports + +Wildcard imports are now expressed with `*` instead of underscore. Example: +```scala +import scala.annotation.* // imports everything in the annotation package +``` + +If you want to import a member named `*` specifically, you can use backticks around it. + +```scala +object A: + def * = ... + def min = ... + +object B: + import A.`*` // imports just `*` + +object C: + import A.* // imports everything in A +``` + +## Renaming Imports + +To rename or exclude an import, we now use `as` instead of `=>`. A single renaming import no longer needs to be enclosed in braces. Examples: + +```scala +import A.{min as minimum, `*` as multiply} +import Predef.{augmentString as _, *} // imports everything except augmentString +import scala.annotation as ann +import java as j +``` + +## Migration + +To support cross-building, Scala 3.0 supports the old import syntax with `_` for wildcards and `=>` for renamings in addition to the new one. The old syntax +will be dropped in a future versions. Automatic rewritings from old to new syntax +are offered under settings `-source 3.1-migration -rewrite`. + +## Syntax + +``` +Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} +ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec + | SimpleRef `as` id +ImportSpec ::= NamedSelector + | WildcardSelector + | ‘{’ ImportSelectors) ‘}’ +NamedSelector ::= id [‘as’ (id | ‘_’)] +WildCardSelector ::= ‘*' | ‘given’ [InfixType] +ImportSelectors ::= NamedSelector [‘,’ ImportSelectors] + | WildCardSelector {‘,’ WildCardSelector} +``` diff --git a/docs/_spec/TODOreference/changed-features/interpolation-escapes.md b/docs/_spec/TODOreference/changed-features/interpolation-escapes.md new file mode 100644 index 000000000000..594e7671c5ab --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/interpolation-escapes.md @@ -0,0 +1,14 @@ +--- +layout: doc-page +title: "Escapes in interpolations" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/interpolation-escapes.html +--- + +In Scala 2 there is no straightforward way to represent a single quote character `"` in a single quoted interpolation. A `\` character can't be used for that because interpolators themselves decide how to handle escaping, so the parser doesn't know whether the `"` character should be escaped or used as a terminator. + +In Scala 3, we can use the `$` meta character of interpolations to escape a `"` character. Example: + +```scala + val inventor = "Thomas Edison" + val interpolation = s"as $inventor said: $"The three great essentials to achieve anything worth while are: Hard work, Stick-to-itiveness, and Common sense.$"" +``` diff --git a/docs/_spec/TODOreference/changed-features/lazy-vals-init.md b/docs/_spec/TODOreference/changed-features/lazy-vals-init.md new file mode 100644 index 000000000000..131ac6ad7bb2 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/lazy-vals-init.md @@ -0,0 +1,80 @@ +--- +layout: doc-page +title: Lazy Vals Initialization +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/lazy-vals-init.html +--- + +Scala 3 implements [Version 6](https://docs.scala-lang.org/sips/improved-lazy-val-initialization.html#version-6---no-synchronization-on-this-and-concurrent-initialization-of-fields) +of the [SIP-20] improved lazy vals initialization proposal. + +## Motivation + +The newly proposed lazy val initialization mechanism aims to eliminate the acquisition of resources +during the execution of the lazy val initializer block, thus reducing the possibility of a deadlock. +The concrete deadlock scenarios that the new lazy val initialization scheme eliminates are +summarized in the [SIP-20] document. + +## Implementation + +Given a lazy field of the form: + +```scala +class Foo { + lazy val bar = +} +``` + +The Scala 3 compiler will generate code equivalent to: + +```scala +class Foo { + import scala.runtime.LazyVals + var value_0: Int = _ + var bitmap: Long = 0L + val bitmap_offset: Long = LazyVals.getOffset(classOf[LazyCell], "bitmap") + + def bar(): Int = { + while (true) { + val flag = LazyVals.get(this, bitmap_offset) + val state = LazyVals.STATE(flag, ) + + if (state == ) { + return value_0 + } else if (state == ) { + if (LazyVals.CAS(this, bitmap_offset, flag, , )) { + try { + val result = + value_0 = result + LazyVals.setFlag(this, bitmap_offset, , ) + return result + } + catch { + case ex => + LazyVals.setFlag(this, bitmap_offset, , ) + throw ex + } + } + } else /* if (state == || state == ) */ { + LazyVals.wait4Notification(this, bitmap_offset, flag, ) + } + } + } +} +``` + +The state of the lazy val `` is represented with 4 values: 0, 1, 2 and 3. The state 0 +represents a non-initialized lazy val. The state 1 represents a lazy val that is currently being +initialized by some thread. The state 2 denotes that there are concurrent readers of the lazy val. +The state 3 represents a lazy val that has been initialized. `` is the id of the lazy +val. This id grows with the number of volatile lazy vals defined in the class. + +## Note on recursive lazy vals + +Ideally recursive lazy vals should be flagged as an error. The current behavior for +recursive lazy vals is undefined (initialization may result in a deadlock). + +## Reference + +* [SIP-20] + +[SIP-20]: https://docs.scala-lang.org/sips/improved-lazy-val-initialization.html diff --git a/docs/_spec/TODOreference/changed-features/main-functions.md b/docs/_spec/TODOreference/changed-features/main-functions.md new file mode 100644 index 000000000000..4460300d003e --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/main-functions.md @@ -0,0 +1,87 @@ +--- +layout: doc-page +title: "Main Methods" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/main-functions.html +--- + +Scala 3 offers a new way to define programs that can be invoked from the command line: +A [`@main`](https://scala-lang.org/api/3.x/scala/main.html) annotation on a method turns this method into an executable program. +Example: + +```scala +@main def happyBirthday(age: Int, name: String, others: String*) = + val suffix = + age % 100 match + case 11 | 12 | 13 => "th" + case _ => + age % 10 match + case 1 => "st" + case 2 => "nd" + case 3 => "rd" + case _ => "th" + val bldr = new StringBuilder(s"Happy $age$suffix birthday, $name") + for other <- others do bldr.append(" and ").append(other) + bldr.toString +``` + +This would generate a main program `happyBirthday` that could be called like this + +``` +> scala happyBirthday 23 Lisa Peter +Happy 23rd birthday, Lisa and Peter +``` + +A [`@main`](https://scala-lang.org/api/3.x/scala/main.html) annotated method can be written either at the top-level or in a statically accessible object. The name of the program is in each case the name of the method, without any object prefixes. The [`@main`](https://scala-lang.org/api/3.x/scala/main.html) method can have an arbitrary number of parameters. +For each parameter type there must be an instance of the [`scala.util.CommandLineParser.FromString[T]`](https://scala-lang.org/api/3.x/scala/util/CommandLineParser$$FromString.html) type class that is used to convert an argument string to the required parameter type `T`. +The parameter list of a main method can end in a repeated parameter that then takes all remaining arguments given on the command line. + +The program implemented from a [`@main`](https://scala-lang.org/api/3.x/scala/main.html) method checks that there are enough arguments on +the command line to fill in all parameters, and that argument strings are convertible to +the required types. If a check fails, the program is terminated with an error message. + +Examples: + +``` +> scala happyBirthday 22 +Illegal command line after first argument: more arguments expected + +> scala happyBirthday sixty Fred +Illegal command line: java.lang.NumberFormatException: For input string: "sixty" +``` + +The Scala compiler generates a program from a [`@main`](https://scala-lang.org/api/3.x/scala/main.html) method `f` as follows: + + - It creates a class named `f` in the package where the [`@main`](https://scala-lang.org/api/3.x/scala/main.html) method was found + - The class has a static method `main` with the usual signature. It takes an `Array[String]` + as argument and returns [`Unit`](https://scala-lang.org/api/3.x/scala/Unit.html). + - The generated `main` method calls method `f` with arguments converted using + methods in the [`scala.util.CommandLineParser`](https://scala-lang.org/api/3.x/scala/util/CommandLineParser$.html) object. + +For instance, the `happyBirthDay` method above would generate additional code equivalent to the following class: + +```scala +final class happyBirthday: + import scala.util.CommandLineParser as CLP + def main(args: Array[String]): Unit = + try + happyBirthday( + CLP.parseArgument[Int](args, 0), + CLP.parseArgument[String](args, 1), + CLP.parseRemainingArguments[String](args, 2)) + catch + case error: CLP.ParseError => CLP.showError(error) +``` + +**Note**: The `` modifier above expresses that the `main` method is generated +as a static method of class `happyBirthDay`. It is not available for user programs in Scala. Regular "static" members are generated in Scala using objects instead. + +[`@main`](https://scala-lang.org/api/3.x/scala/main.html) methods are the recommended scheme to generate programs that can be invoked from the command line in Scala 3. They replace the previous scheme to write program as objects with a special `App` parent class. In Scala 2, `happyBirthday` could be written also like this: + +```scala +object happyBirthday extends App: + // needs by-hand parsing of arguments vector + ... +``` + +The previous functionality of [`App`](https://www.scala-lang.org/api/3.x/scala/App.html), which relied on the "magic" [`DelayedInit`](../dropped-features/delayed-init.md) trait, is no longer available. [`App`](https://scala-lang.org/api/3.x/scala/App.html) still exists in limited form for now, but it does not support command line arguments and will be deprecated in the future. If programs need to cross-build +between Scala 2 and Scala 3, it is recommended to use an explicit `main` method with an `Array[String]` argument instead. diff --git a/docs/_spec/TODOreference/changed-features/match-syntax.md b/docs/_spec/TODOreference/changed-features/match-syntax.md new file mode 100644 index 000000000000..dba50e9beb6a --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/match-syntax.md @@ -0,0 +1,56 @@ +--- +layout: doc-page +title: "Match Expressions" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/match-syntax.html +--- + +The syntactical precedence of match expressions has been changed. +`match` is still a keyword, but it is used like an alphabetical operator. This has several consequences: + + 1. `match` expressions can be chained: + + ```scala + xs match { + case Nil => "empty" + case _ => "nonempty" + } match { + case "empty" => 0 + case "nonempty" => 1 + } + ``` + + (or, dropping the optional braces) + + ```scala + xs match + case Nil => "empty" + case _ => "nonempty" + match + case "empty" => 0 + case "nonempty" => 1 + ``` + + 2. `match` may follow a period: + + ```scala + if xs.match + case Nil => false + case _ => true + then "nonempty" + else "empty" + ``` + + 3. The scrutinee of a match expression must be an `InfixExpr`. Previously the scrutinee could be followed by a type ascription `: T`, but this is no longer supported. So `x : T match { ... }` now has to be + written `(x: T) match { ... }`. + +## Syntax + +The new syntax of match expressions is as follows. + +``` +InfixExpr ::= ... + | InfixExpr MatchClause +SimpleExpr ::= ... + | SimpleExpr ‘.’ MatchClause +MatchClause ::= ‘match’ ‘{’ CaseClauses ‘}’ +``` diff --git a/docs/_spec/TODOreference/changed-features/numeric-literals.md b/docs/_spec/TODOreference/changed-features/numeric-literals.md new file mode 100644 index 000000000000..bba837dbf67d --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/numeric-literals.md @@ -0,0 +1,7 @@ +--- +layout: doc-page +title: "Numeric Literals" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/numeric-literals.html +--- + +[Document was moved](../experimental/numeric-literals.md) diff --git a/docs/_spec/TODOreference/changed-features/operators.md b/docs/_spec/TODOreference/changed-features/operators.md new file mode 100644 index 000000000000..0cf25d77bc11 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/operators.md @@ -0,0 +1,173 @@ +--- +layout: doc-page +title: "Rules for Operators" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/operators.html +--- + +The rules for infix operators have changed in some parts: + +First, an alphanumeric method can be used as an infix operator only if its definition carries an `infix` modifier. + +Second, it is recommended (but not enforced) to augment definitions of symbolic operators +with [`@targetName` annotations](../other-new-features/targetName.md). + +Finally, a syntax change allows infix operators to be written on the left in a multi-line expression. + +## The `infix` Modifier + +An `infix` modifier on a method definition allows using the method as an infix operation. Example: + +```scala +import scala.annotation.targetName + +trait MultiSet[T]: + + infix def union(other: MultiSet[T]): MultiSet[T] + + def difference(other: MultiSet[T]): MultiSet[T] + + @targetName("intersection") + def *(other: MultiSet[T]): MultiSet[T] + +end MultiSet + +val s1, s2: MultiSet[Int] + +s1 union s2 // OK +s1 `union` s2 // also OK but unusual +s1.union(s2) // also OK + +s1.difference(s2) // OK +s1 `difference` s2 // OK +s1 difference s2 // gives a deprecation warning + +s1 * s2 // OK +s1 `*` s2 // also OK, but unusual +s1.*(s2) // also OK, but unusual +``` + +Infix operations involving alphanumeric operators are deprecated, unless +one of the following conditions holds: + + - the operator definition carries an `infix` modifier, or + - the operator was compiled with Scala 2, or + - the operator is followed by an opening brace. + +An alphanumeric operator is an operator consisting entirely of letters, digits, the `$` and `_` characters, or +any Unicode character `c` for which `java.lang.Character.isIdentifierPart(c)` returns `true`. + +Infix operations involving symbolic operators are always allowed, so `infix` is redundant for methods with symbolic names. + +The `infix` modifier can also be given to a type: + +```scala +infix type or[X, Y] +val x: String or Int = ... +``` + +### Motivation + +The purpose of the `infix` modifier is to achieve consistency across a code base in how a method or type is applied. The idea is that the author of a method decides whether that method should be applied as an infix operator or in a regular application. Use sites then implement that decision consistently. + +### Details + + 1. `infix` is a soft modifier. It is treated as a normal identifier except when in modifier position. + + 2. If a method overrides another, their infix annotations must agree. Either both are annotated with `infix`, or none of them are. + + 3. `infix` modifiers can be given to method definitions. The first non-receiver parameter list of an `infix` method must define exactly one parameter. Examples: + + ```scala + infix def op1(x: S): R // ok + infix def op2[T](x: T)(y: S): R // ok + infix def op3[T](x: T, y: S): R // error: two parameters + + extension (x: A) + infix def op4(y: B): R // ok + infix def op5(y1: B, y2: B): R // error: two parameters + ``` + + 4. `infix` modifiers can also be given to type, trait or class definitions that have exactly two type parameters. An infix type like + + ```scala + infix type op[X, Y] + ``` + + can be applied using infix syntax, i.e. `A op B`. + + 5. To smooth migration to Scala 3.0, alphanumeric operators will only be deprecated from Scala 3.1 onwards, +or if the `-source future` option is given in Dotty/Scala 3. + +## The `@targetName` Annotation + +It is recommended that definitions of symbolic operators carry a [`@targetName` annotation](../other-new-features/targetName.md) that provides an encoding of the operator with an alphanumeric name. This has several benefits: + + - It helps interoperability between Scala and other languages. One can call + a Scala-defined symbolic operator from another language using its target name, + which avoids having to remember the low-level encoding of the symbolic name. + - It helps legibility of stacktraces and other runtime diagnostics, where the + user-defined alphanumeric name will be shown instead of the low-level encoding. + - It serves as a documentation tool by providing an alternative regular name + as an alias of a symbolic operator. This makes the definition also easier + to find in a search. + +## Syntax Change + +Infix operators can now appear at the start of lines in a multi-line expression. Examples: + +```scala +val str = "hello" + ++ " world" + ++ "!" + +def condition = + x > 0 + || + xs.exists(_ > 0) + || xs.isEmpty +``` + +Previously, those expressions would have been rejected, since the compiler's semicolon inference +would have treated the continuations `++ " world"` or `|| xs.isEmpty` as separate statements. + +To make this syntax work, the rules are modified to not infer semicolons in front of leading infix operators. +A _leading infix operator_ is + - a symbolic identifier such as `+`, or `approx_==`, or an identifier in backticks that + - starts a new line, and + - is not following a blank line, and + - is followed by at least one whitespace character and a token that can start an expression. + - Furthermore, if the operator appears on its own line, the next line must have at least + the same indentation width as the operator. + +Example: + +```scala + freezing + | boiling +``` + +This is recognized as a single infix operation. Compare with: + +```scala + freezing + !boiling +``` + +This is seen as two statements, `freezing` and `!boiling`. The difference is that only the operator in the first example +is followed by a space. + +Another example: + +```scala + println("hello") + ??? + ??? match { case 0 => 1 } +``` + +This code is recognized as three different statements. `???` is syntactically a symbolic identifier, but +neither of its occurrences is followed by a space and a token that can start an expression. + +## Unary operators + +A unary operator must not have explicit parameter lists even if they are empty. +A unary operator is a method named "unary_`op`" where `op` is one of `+`, `-`, `!`, or `~`. diff --git a/docs/_spec/TODOreference/changed-features/overload-resolution.md b/docs/_spec/TODOreference/changed-features/overload-resolution.md new file mode 100644 index 000000000000..bd7782ded520 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/overload-resolution.md @@ -0,0 +1,102 @@ +--- +layout: doc-page +title: "Changes in Overload Resolution" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/overload-resolution.html +--- + +Overload resolution in Scala 3 improves on Scala 2 in three ways. +First, it takes all argument lists into account instead of +just the first argument list. +Second, it can infer parameter types of function values even if they +are in the first argument list. +Third, default arguments are no longer relevant for prioritization. + +## Looking Beyond the First Argument List + +Overloading resolution now can take argument lists into account when +choosing among a set of overloaded alternatives. +For example, the following code compiles in Scala 3, while it results in an +ambiguous overload error in Scala 2: + +```scala +def f(x: Int)(y: String): Int = 0 +def f(x: Int)(y: Int): Int = 0 + +f(3)("") // ok +``` + +The following code compiles as well: + +```scala +def g(x: Int)(y: Int)(z: Int): Int = 0 +def g(x: Int)(y: Int)(z: String): Int = 0 + +g(2)(3)(4) // ok +g(2)(3)("") // ok +``` + +To make this work, the rules for overloading resolution in [SLS §6.26.3](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#overloading-resolution) are augmented +as follows: + +> In a situation where a function is applied to more than one argument list, if overloading +resolution yields several competing alternatives when `n >= 1` parameter lists are taken +into account, then resolution re-tried using `n + 1` argument lists. + +This change is motivated by the new language feature +[extension methods](../contextual/extension-methods.md), where emerges the need to do +overload resolution based on additional argument blocks. + +## Parameter Types of Function Values + +The handling of function values with missing parameter types has been improved. We can now +pass such values in the first argument list of an overloaded application, provided +that the remaining parameters suffice for picking a variant of the overloaded function. +For example, the following code compiles in Scala 3, while it results in a +missing parameter type error in Scala2: + +```scala +def f(x: Int, f2: Int => Int) = f2(x) +def f(x: String, f2: String => String) = f2(x) +f("a", _.toUpperCase) +f(2, _ * 2) +``` + +To make this work, the rules for overloading resolution in [SLS §6.26.3](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#overloading-resolution) are modified +as follows: + +Replace the sentence + +> Otherwise, let `S1,…,Sm` be the vector of types obtained by typing each argument with an undefined expected type. + +with the following paragraph: + +> Otherwise, let `S1,…,Sm` be the vector of known types of all argument types, where the _known type_ of an argument `E` +is determined as followed: + + - If `E` is a function value `(p_1, ..., p_n) => B` that misses some parameter types, the known type + of `E` is `(S_1, ..., S_n) => ?`, where each `S_i` is the type of parameter `p_i` if it is given, or `?` + otherwise. Here `?` stands for a _wildcard type_ that is compatible with every other type. + - Otherwise the known type of `E` is the result of typing `E` with an undefined expected type. + +A pattern matching closure + +```scala +{ case P1 => B1 ... case P_n => B_n } +```` + +is treated as if it was expanded to the function value + +```scala +x => x match { case P1 => B1 ... case P_n => B_n } +``` + +and is therefore also approximated with a `? => ?` type. + +## Default Arguments Are No longer Relevant for Prioritization + +In Scala 2 if among several applicative alternatives one alternative had default arguments, that alternative was dropped from consideration. This has the unfortunate +side effect that adding a default to a parameter of a method can render this method +invisible in overloaded calls. + +Scala 3 drops this distinction. Methods with default parameters are not treated +to have lower priority than other methods. diff --git a/docs/_spec/TODOreference/changed-features/pattern-bindings.md b/docs/_spec/TODOreference/changed-features/pattern-bindings.md new file mode 100644 index 000000000000..2de338fc1dde --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/pattern-bindings.md @@ -0,0 +1,59 @@ +--- +layout: doc-page +title: "Pattern Bindings" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html +--- + +In Scala 2, pattern bindings in `val` definitions and `for` expressions are +loosely typed. Potentially failing matches are still accepted at compile-time, +but may influence the program's runtime behavior. +From Scala 3.2 on, type checking rules will be tightened so that warnings are reported at compile-time instead. + +## Bindings in Pattern Definitions + +```scala +val xs: List[Any] = List(1, 2, 3) +val (x: String) :: _ = xs // error: pattern's type String is more specialized + // than the right-hand side expression's type Any +``` +This code gives a compile-time warning in Scala 3.2 (and also earlier Scala 3.x under the `-source future` setting) whereas it will fail at runtime with a `ClassCastException` in Scala 2. In Scala 3.2, a pattern binding is only allowed if the pattern is _irrefutable_, that is, if the right-hand side's type conforms to the pattern's type. For instance, the following is OK: +```scala +val pair = (1, true) +val (x, y) = pair +``` +Sometimes one wants to decompose data anyway, even though the pattern is refutable. For instance, if at some point one knows that a list `elems` is non-empty one might want to decompose it like this: +```scala +val first :: rest = elems // error +``` +This works in Scala 2. In fact it is a typical use case for Scala 2's rules. But in Scala 3.2 it will give a warning. One can avoid the warning by marking the right-hand side with an [`@unchecked`](https://scala-lang.org/api/3.x/scala/unchecked.html) annotation: +```scala +val first :: rest = elems: @unchecked // OK +``` +This will make the compiler accept the pattern binding. It might give an error at runtime instead, if the underlying assumption that `elems` can never be empty is wrong. + +## Pattern Bindings in `for` Expressions + +Analogous changes apply to patterns in `for` expressions. For instance: + +```scala +val elems: List[Any] = List((1, 2), "hello", (3, 4)) +for (x, y) <- elems yield (y, x) // error: pattern's type (Any, Any) is more specialized + // than the right-hand side expression's type Any +``` +This code gives a compile-time warning in Scala 3.2 whereas in Scala 2 the list `elems` +is filtered to retain only the elements of tuple type that match the pattern `(x, y)`. +The filtering functionality can be obtained in Scala 3 by prefixing the pattern with `case`: +```scala +for case (x, y) <- elems yield (y, x) // returns List((2, 1), (4, 3)) +``` + +## Syntax Changes + +Generators in for expressions may be prefixed with `case`. +``` +Generator ::= [‘case’] Pattern1 ‘<-’ Expr +``` + +## Migration + +The new syntax is supported in Scala 3.0. However, to enable smooth cross compilation between Scala 2 and Scala 3, the changed behavior and additional type checks are only enabled under the `-source future` setting. They will be enabled by default in version 3.2 of the language. diff --git a/docs/_spec/TODOreference/changed-features/pattern-matching.md b/docs/_spec/TODOreference/changed-features/pattern-matching.md new file mode 100644 index 000000000000..30ae5d9dc104 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/pattern-matching.md @@ -0,0 +1,243 @@ +--- +layout: doc-page +title: "Option-less pattern matching" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/pattern-matching.html +--- + +The implementation of pattern matching in Scala 3 was greatly simplified compared to Scala 2. From a user perspective, this means that Scala 3 generated patterns are a _lot_ easier to debug, as variables all show up in debug modes and positions are correctly preserved. + +Scala 3 supports a superset of Scala 2 [extractors](https://www.scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#extractor-patterns). + +## Extractors + +Extractors are objects that expose a method `unapply` or `unapplySeq`: + +```scala +def unapply[A](x: T)(implicit x: B): U +def unapplySeq[A](x: T)(implicit x: B): U +``` + +Extractors that expose the method `unapply` are called fixed-arity extractors, which +work with patterns of fixed arity. Extractors that expose the method `unapplySeq` are +called variadic extractors, which enables variadic patterns. + +### Fixed-Arity Extractors + +Fixed-arity extractors expose the following signature: + +```scala +def unapply[A](x: T)(implicit x: B): U +``` + +The type `U` conforms to one of the following matches: + +- Boolean match +- Product match + +Or `U` conforms to the type `R`: + +```scala +type R = { + def isEmpty: Boolean + def get: S +} +``` + +and `S` conforms to one of the following matches: + +- single match +- name-based match + +The former form of `unapply` has higher precedence, and _single match_ has higher +precedence over _name-based match_. + +A usage of a fixed-arity extractor is irrefutable if one of the following condition holds: + +- `U = true` +- the extractor is used as a product match +- `U = Some[T]` (for Scala 2 compatibility) +- `U <: R` and `U <: { def isEmpty: false }` + +### Variadic Extractors + +Variadic extractors expose the following signature: + +```scala +def unapplySeq[A](x: T)(implicit x: B): U +``` + +The type `U` conforms to one of the following matches: + +- sequence match +- product-sequence match + +Or `U` conforms to the type `R`: + +```scala +type R = { + def isEmpty: Boolean + def get: S +} +``` + +and `S` conforms to one of the two matches above. + +The former form of `unapplySeq` has higher priority, and _sequence match_ has higher +precedence over _product-sequence match_. + +A usage of a variadic extractor is irrefutable if one of the following conditions holds: + +- the extractor is used directly as a sequence match or product-sequence match +- `U = Some[T]` (for Scala 2 compatibility) +- `U <: R` and `U <: { def isEmpty: false }` + +## Boolean Match + +- `U =:= Boolean` +- Pattern-matching on exactly `0` patterns + +For example: + + + +```scala +object Even: + def unapply(s: String): Boolean = s.size % 2 == 0 + +"even" match + case s @ Even() => println(s"$s has an even number of characters") + case s => println(s"$s has an odd number of characters") + +// even has an even number of characters +``` + +## Product Match + +- `U <: Product` +- `N > 0` is the maximum number of consecutive (parameterless `def` or `val`) `_1: P1` ... `_N: PN` members in `U` +- Pattern-matching on exactly `N` patterns with types `P1, P2, ..., PN` + +For example: + + + +```scala +class FirstChars(s: String) extends Product: + def _1 = s.charAt(0) + def _2 = s.charAt(1) + + // Not used by pattern matching: Product is only used as a marker trait. + def canEqual(that: Any): Boolean = ??? + def productArity: Int = ??? + def productElement(n: Int): Any = ??? + +object FirstChars: + def unapply(s: String): FirstChars = new FirstChars(s) + +"Hi!" match + case FirstChars(char1, char2) => + println(s"First: $char1; Second: $char2") + +// First: H; Second: i +``` + +## Single Match + +- If there is exactly `1` pattern, pattern-matching on `1` pattern with type `U` + + + +```scala +class Nat(val x: Int): + def get: Int = x + def isEmpty = x < 0 + +object Nat: + def unapply(x: Int): Nat = new Nat(x) + +5 match + case Nat(n) => println(s"$n is a natural number") + case _ => () + +// 5 is a natural number +``` + +## Name-based Match + +- `N > 1` is the maximum number of consecutive (parameterless `def` or `val`) `_1: P1 ... _N: PN` members in `U` +- Pattern-matching on exactly `N` patterns with types `P1, P2, ..., PN` + +```scala +object ProdEmpty: + def _1: Int = ??? + def _2: String = ??? + def isEmpty = true + def unapply(s: String): this.type = this + def get = this + +"" match + case ProdEmpty(_, _) => ??? + case _ => () +``` + +## Sequence Match + +- `U <: X`, `T2` and `T3` conform to `T1` + +```scala +type X = { + def lengthCompare(len: Int): Int // or, `def length: Int` + def apply(i: Int): T1 + def drop(n: Int): scala.Seq[T2] + def toSeq: scala.Seq[T3] +} +``` + +- Pattern-matching on _exactly_ `N` simple patterns with types `T1, T1, ..., T1`, where `N` is the runtime size of the sequence, or +- Pattern-matching on `>= N` simple patterns and _a vararg pattern_ (e.g., `xs: _*`) with types `T1, T1, ..., T1, Seq[T1]`, where `N` is the minimum size of the sequence. + + + +```scala +object CharList: + def unapplySeq(s: String): Option[Seq[Char]] = Some(s.toList) + +"example" match + case CharList(c1, c2, c3, c4, _, _, _) => + println(s"$c1,$c2,$c3,$c4") + case _ => + println("Expected *exactly* 7 characters!") + +// e,x,a,m +``` + +## Product-Sequence Match + +- `U <: Product` +- `N > 0` is the maximum number of consecutive (parameterless `def` or `val`) `_1: P1` ... `_N: PN` members in `U` +- `PN` conforms to the signature `X` defined in Seq Pattern +- Pattern-matching on exactly `>= N` patterns, the first `N - 1` patterns have types `P1, P2, ... P(N-1)`, + the type of the remaining patterns are determined as in Seq Pattern. + +```scala +class Foo(val name: String, val children: Int*) +object Foo: + def unapplySeq(f: Foo): Option[(String, Seq[Int])] = + Some((f.name, f.children)) + +def foo(f: Foo) = f match + case Foo(name, x, y, ns*) => ">= two children." + case Foo(name, ns*) => => "< two children." +``` + +There are plans for further simplification, in particular to factor out _product match_ +and _name-based match_ into a single type of extractor. + +## Type testing + +Abstract type testing with `ClassTag` is replaced with `TypeTest` or the alias `Typeable`. + +- pattern `_: X` for an abstract type requires a `TypeTest` in scope +- pattern `x @ X()` for an unapply that takes an abstract type requires a `TypeTest` in scope + +[More details on `TypeTest`](../other-new-features/type-test.md) diff --git a/docs/_spec/TODOreference/changed-features/structural-types-spec.md b/docs/_spec/TODOreference/changed-features/structural-types-spec.md new file mode 100644 index 000000000000..d456932649fb --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/structural-types-spec.md @@ -0,0 +1,153 @@ +--- +layout: doc-page +title: "Programmatic Structural Types - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/structural-types-spec.html +--- + +## Syntax + +``` +SimpleType ::= ... | Refinement +Refinement ::= ‘{’ RefineStatSeq ‘}’ +RefineStatSeq ::= RefineStat {semi RefineStat} +RefineStat ::= ‘val’ VarDcl | ‘def’ DefDcl | ‘type’ {nl} TypeDcl +``` + +## Implementation of Structural Types + +The standard library defines a universal marker trait +[`scala.Selectable`](https://github.com/lampepfl/dotty/blob/main/library/src/scala/Selectable.scala): + +```scala +trait Selectable extends Any +``` + +An implementation of `Selectable` that relies on [Java reflection](https://www.oracle.com/technical-resources/articles/java/javareflection.html) is +available in the standard library: `scala.reflect.Selectable`. Other +implementations can be envisioned for platforms where Java reflection +is not available. + +Implementations of `Selectable` have to make available one or both of +the methods `selectDynamic` and `applyDynamic`. The methods could be members of the `Selectable` implementation or they could be extension methods. + +The `selectDynamic` method takes a field name and returns the value associated with that name in the `Selectable`. +It should have a signature of the form: + +```scala +def selectDynamic(name: String): T +``` + +Often, the return type `T` is `Any`. + +Unlike `scala.Dynamic`, there is no special meaning for an `updateDynamic` method. +However, we reserve the right to give it meaning in the future. +Consequently, it is recommended not to define any member called `updateDynamic` in `Selectable`s. + +The `applyDynamic` method is used for selections that are applied to arguments. It takes a method name and possibly `Class`es representing its parameters types as well as the arguments to pass to the function. +Its signature should be of one of the two following forms: + +```scala +def applyDynamic(name: String)(args: Any*): T +def applyDynamic(name: String, ctags: Class[?]*)(args: Any*): T +``` + +Both versions are passed the actual arguments in the `args` parameter. The second version takes in addition a vararg argument of `java.lang.Class`es that identify the method's parameter classes. Such an argument is needed +if `applyDynamic` is implemented using Java reflection, but it could be +useful in other cases as well. `selectDynamic` and `applyDynamic` can also take additional context parameters in using clauses. These are resolved in the normal way at the callsite. + +Given a value `v` of type `C { Rs }`, where `C` is a class reference +and `Rs` are structural refinement declarations, and given `v.a` of type `U`, we consider three distinct cases: + +- If `U` is a value type, we map `v.a` to: + ```scala + v.selectDynamic("a").asInstanceOf[U] + ``` + +- If `U` is a method type `(T11, ..., T1n)...(TN1, ..., TNn): R` and it is not a dependent method type, we map `v.a(a11, ..., a1n)...(aN1, ..., aNn)` to: + ```scala + v.applyDynamic("a")(a11, ..., a1n, ..., aN1, ..., aNn) + .asInstanceOf[R] + ``` + If this call resolves to an `applyDynamic` method of the second form that takes a `Class[?]*` argument, we further rewrite this call to + ```scala + v.applyDynamic("a", c11, ..., c1n, ..., cN1, ... cNn)( + a11, ..., a1n, ..., aN1, ..., aNn) + .asInstanceOf[R] + ``` + where each `c_ij` is the literal `java.lang.Class[?]` of the type of the formal parameter `Tij`, i.e., `classOf[Tij]`. + +- If `U` is neither a value nor a method type, or a dependent method + type, an error is emitted. + +Note that `v`'s static type does not necessarily have to conform to `Selectable`, nor does it need to have `selectDynamic` and `applyDynamic` as members. It suffices that there is an implicit +conversion that can turn `v` into a `Selectable`, and the selection methods could also be available as +[extension methods](../contextual/extension-methods.md). + +## Limitations of Structural Types + +- Dependent methods cannot be called via structural call. + +- Refinements may not introduce overloads: If a refinement specifies the signature + of a method `m`, and `m` is also defined in the parent type of the refinement, then + the new signature must properly override the existing one. + +- Subtyping of structural refinements must preserve erased parameter types: Assume + we want to prove `S <: T { def m(x: A): B }`. Then, as usual, `S` must have a member method `m` that can take an argument of type `A`. Furthermore, if `m` is not a member of `T` (i.e. the refinement is structural), an additional condition applies. In this case, the member _definition_ `m` of `S` will have a parameter + with type `A'` say. The additional condition is that the erasure of `A'` and `A` is the same. Here is an example: + + ```scala + class Sink[A] { def put(x: A): Unit = {} } + val a = Sink[String]() + val b: { def put(x: String): Unit } = a // error + b.put("abc") // looks for a method with a `String` parameter + ``` + The second to last line is not well-typed, + since the erasure of the parameter type of `put` in class `Sink` is `Object`, + but the erasure of `put`'s parameter in the type of `b` is `String`. + This additional condition is necessary, since we will have to resort + to some (as yet unknown) form of reflection to call a structural member + like `put` in the type of `b` above. The condition ensures that the statically + known parameter types of the refinement correspond up to erasure to the + parameter types of the selected call target at runtime. + + Most reflection dispatch algorithms need to know exact erased parameter types. For instance, if the example above would typecheck, the call + `b.put("abc")` on the last line would look for a method `put` in the runtime type of `b` that takes a `String` parameter. But the `put` method is the one from class `Sink`, which takes an `Object` parameter. Hence the call would fail at runtime with a `NoSuchMethodException`. + + One might hope for a "more intelligent" reflexive dispatch algorithm that does not require exact parameter type matching. Unfortunately, this can always run into ambiguities, as long as overloading is a possibility. For instance, continuing the example above, we might introduce a new subclass `Sink1` of `Sink` and change the definition of `a` as follows: + + ```scala + class Sink1[A] extends Sink[A] { def put(x: "123") = ??? } + val a: Sink[String] = Sink1[String]() + ``` + + Now there are two `put` methods in the runtime type of `b` with erased parameter + types `Object` and `String`, respectively. Yet dynamic dispatch still needs to go + to the first `put` method, even though the second looks like a better match. + + For the cases where we can in fact implement reflection without knowing precise parameter types (for instance if static overloading is replaced by dynamically dispatched multi-methods), there is an escape hatch. For types that extend `scala.Selectable.WithoutPreciseParameterTypes` the signature check is omitted. Example: + + ```scala + trait MultiMethodSelectable extends Selectable.WithoutPreciseParameterTypes: + // Assume this version of `applyDynamic` can be implemented without knowing + // precise parameter types `paramTypes`: + def applyDynamic(name: String, paramTypes: Class[_]*)(args: Any*): Any = ??? + + class Sink[A] extends MultiMethodSelectable: + def put(x: A): Unit = {} + + val a = new Sink[String] + val b: MultiMethodSelectable { def put(x: String): Unit } = a // OK + ``` +## Differences with Scala 2 Structural Types + +- Scala 2 supports structural types by means of Java reflection. Unlike + Scala 3, structural calls do not rely on a mechanism such as + `Selectable`, and reflection cannot be avoided. +- In Scala 2, refinements can introduce overloads. +- In Scala 2, mutable `var`s are allowed in refinements. In Scala 3, + they are no longer allowed. +- Scala 2 does not impose the "same-erasure" restriction on subtyping of structural types. It allows some calls to fail at runtime instead. + +## Context + +For more information, see [Rethink Structural Types](https://github.com/lampepfl/dotty/issues/1886). diff --git a/docs/_spec/TODOreference/changed-features/structural-types.md b/docs/_spec/TODOreference/changed-features/structural-types.md new file mode 100644 index 000000000000..37e583332cf1 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/structural-types.md @@ -0,0 +1,191 @@ +--- +layout: doc-page +title: "Programmatic Structural Types" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/structural-types.html +--- + +## Motivation + +Some usecases, such as modelling database access, are more awkward in +statically typed languages than in dynamically typed languages: With +dynamically typed languages, it's quite natural to model a row as a +record or object, and to select entries with simple dot notation (e.g. +`row.columnName`). + +Achieving the same experience in statically typed +language requires defining a class for every possible row arising from +database manipulation (including rows arising from joins and +projections) and setting up a scheme to map between a row and the +class representing it. + +This requires a large amount of boilerplate, which leads developers to +trade the advantages of static typing for simpler schemes where colum +names are represented as strings and passed to other operators (e.g. +`row.select("columnName")`). This approach forgoes the advantages of +static typing, and is still not as natural as the dynamically typed +version. + +Structural types help in situations where we would like to support +simple dot notation in dynamic contexts without losing the advantages +of static typing. They allow developers to use dot notation and +configure how fields and methods should be resolved. + +## Example + +Here's an example of a structural type `Person`: + +```scala + class Record(elems: (String, Any)*) extends Selectable: + private val fields = elems.toMap + def selectDynamic(name: String): Any = fields(name) + + type Person = Record { val name: String; val age: Int } + ``` + +The type `Person` adds a _refinement_ to its parent type `Record` that defines the two fields `name` and `age`. We say the refinement is _structural_ since `name` and `age` are not defined in the parent type. But they exist nevertheless as members of class `Person`. For instance, the following +program would print "Emma is 42 years old.": + +```scala + val person = Record("name" -> "Emma", "age" -> 42).asInstanceOf[Person] + println(s"${person.name} is ${person.age} years old.") +``` + +The parent type `Record` in this example is a generic class that can represent arbitrary records in its `elems` argument. This argument is a +sequence of pairs of labels of type `String` and values of type `Any`. +When we create a `Person` as a `Record` we have to assert with a typecast +that the record defines the right fields of the right types. `Record` +itself is too weakly typed so the compiler cannot know this without +help from the user. In practice, the connection between a structural type +and its underlying generic representation would most likely be done by +a database layer, and therefore would not be a concern of the end user. + +`Record` extends the marker trait [`scala.Selectable`](https://scala-lang.org/api/3.x/scala/Selectable.html) and defines +a method `selectDynamic`, which maps a field name to its value. +Selecting a structural type member is done by calling this method. +The `person.name` and `person.age` selections are translated by +the Scala compiler to: + +```scala + person.selectDynamic("name").asInstanceOf[String] + person.selectDynamic("age").asInstanceOf[Int] +``` + +Besides `selectDynamic`, a `Selectable` class sometimes also defines a method `applyDynamic`. This can then be used to translate function calls of structural members. So, if `a` is an instance of `Selectable`, a structural call like `a.f(b, c)` would translate to + +```scala + a.applyDynamic("f")(b, c) +``` + +## Using Java Reflection + +Structural types can also be accessed using [Java reflection](https://www.oracle.com/technical-resources/articles/java/javareflection.html). Example: + +```scala + type Closeable = { def close(): Unit } + + class FileInputStream: + def close(): Unit + + class Channel: + def close(): Unit +``` + +Here, we define a structural type `Closeable` that defines a `close` method. There are various classes that have `close` methods, we just list [`FileInputStream`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/FileInputStream.html#%3Cinit%3E(java.io.File)) and [`Channel`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/channels/Channel.html) as two examples. It would be easiest if the two classes shared a common interface that factors out the `close` method. But such factorings are often not possible if different libraries are combined in one application. Yet, we can still have methods that work on +all classes with a `close` method by using the `Closeable` type. For instance, + +```scala + import scala.reflect.Selectable.reflectiveSelectable + + def autoClose(f: Closeable)(op: Closeable => Unit): Unit = + try op(f) finally f.close() +``` + +The call `f.close()` has to use Java reflection to identify and call the `close` method in the receiver `f`. This needs to be enabled by an import +of `reflectiveSelectable` shown above. What happens "under the hood" is then the following: + + - The import makes available an implicit conversion that turns any type into a + `Selectable`. `f` is wrapped in this conversion. + + - The compiler then transforms the `close` call on the wrapped `f` + to an `applyDynamic` call. The end result is: + + ```scala + reflectiveSelectable(f).applyDynamic("close")() + ``` + - The implementation of `applyDynamic` in `reflectiveSelectable`'s result +uses Java reflection to find and call a method `close` with zero parameters in the value referenced by `f` at runtime. + +Structural calls like this tend to be much slower than normal method calls. The mandatory import of `reflectiveSelectable` serves as a signpost that something inefficient is going on. + +**Note:** In Scala 2, Java reflection is the only mechanism available for structural types and it is automatically enabled without needing the +`reflectiveSelectable` conversion. However, to warn against inefficient +dispatch, Scala 2 requires a language import `import scala.language.reflectiveCalls`. + +Before resorting to structural calls with Java reflection one should consider alternatives. For instance, sometimes a more modular _and_ efficient architecture can be obtained using type classes. + +## Extensibility + +New instances of `Selectable` can be defined to support means of +access other than Java reflection, which would enable usages such as +the database access example given at the beginning of this document. + +## Local Selectable Instances + +Local and anonymous classes that extend `Selectable` get more refined types +than other classes. Here is an example: + +```scala +trait Vehicle extends reflect.Selectable: + val wheels: Int + +val i3 = new Vehicle: // i3: Vehicle { val range: Int } + val wheels = 4 + val range = 240 + +i3.range +``` + +The type of `i3` in this example is `Vehicle { val range: Int }`. Hence, +`i3.range` is well-formed. Since the base class `Vehicle` does not define a `range` field or method, we need structural dispatch to access the `range` field of the anonymous class that initializes `id3`. Structural dispatch +is implemented by the base trait [`reflect.Selectable`](https://scala-lang.org/api/3.x/scala/reflect/Selectable.html) of `Vehicle`, which defines the necessary `selectDynamic` member. + +`Vehicle` could also extend some other subclass of [`scala.Selectable`](https://scala-lang.org/api/3.x/scala/Selectable.html) that implements `selectDynamic` and `applyDynamic` differently. But if it does not extend a `Selectable` at all, the code would no longer typecheck: + +```scala +trait Vehicle: + val wheels: Int + +val i3 = new Vehicle: // i3: Vehicle + val wheels = 4 + val range = 240 + +i3.range // error: range is not a member of `Vehicle` +``` + +The difference is that the type of an anonymous class that does not extend `Selectable` is just formed from the parent type(s) of the class, without +adding any refinements. Hence, `i3` now has just type `Vehicle` and the selection `i3.range` gives a "member not found" error. + +Note that in Scala 2 all local and anonymous classes could produce values with refined types. But +members defined by such refinements could be selected only with the language import +[`reflectiveCalls`](https://scala-lang.org/api/3.x/scala/languageFeature$$reflectiveCalls$.html). + +## Relation with `scala.Dynamic` + +There are clearly some connections with [`scala.Dynamic`](https://scala-lang.org/api/3.x/scala/Dynamic.html) here, since +both select members programmatically. But there are also some +differences. + +- Fully dynamic selection is not typesafe, but structural selection + is, as long as the correspondence of the structural type with the + underlying value is as stated. + +- [`Dynamic`](https://scala-lang.org/api/3.x/scala/Dynamic.html) is just a marker trait, which gives more leeway where and + how to define reflective access operations. By contrast + `Selectable` is a trait which declares the access operations. + +- Two access operations, `selectDynamic` and `applyDynamic` are shared + between both approaches. In `Selectable`, `applyDynamic` also may also take + [`java.lang.Class`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html) arguments indicating the method's formal parameter types. + [`Dynamic`](https://scala-lang.org/api/3.x/scala/Dynamic.html) comes with `updateDynamic`. + +[More details](structural-types-spec.md) diff --git a/docs/_spec/TODOreference/changed-features/type-checking.md b/docs/_spec/TODOreference/changed-features/type-checking.md new file mode 100644 index 000000000000..6f59b1a1c1c6 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/type-checking.md @@ -0,0 +1,7 @@ +--- +layout: doc-page +title: "Changes in Type Checking" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/type-checking.html +--- + +*** **TO BE FILLED IN** *** diff --git a/docs/_spec/TODOreference/changed-features/type-inference.md b/docs/_spec/TODOreference/changed-features/type-inference.md new file mode 100644 index 000000000000..00d0e959f5ed --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/type-inference.md @@ -0,0 +1,10 @@ +--- +layout: doc-page +title: "Changes in Type Inference" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/type-inference.html +--- + +For more information, see the two presentations + +* [Scala 3, Type inference and You!](https://www.youtube.com/watch?v=lMvOykNQ4zs) by Guillaume Martres (September 2019) +* [GADTs in Dotty](https://www.youtube.com/watch?v=VV9lPg3fNl8) by Aleksander Boruch-Gruszecki (July 2019). diff --git a/docs/_spec/TODOreference/changed-features/vararg-splices.md b/docs/_spec/TODOreference/changed-features/vararg-splices.md new file mode 100644 index 000000000000..43c4acc5f880 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/vararg-splices.md @@ -0,0 +1,40 @@ +--- +layout: doc-page +title: "Vararg Splices" +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/vararg-splices.html +--- + +The syntax of vararg splices in patterns and function arguments has changed. The new syntax uses a postfix `*`, analogously to how a vararg parameter is declared. + +```scala +val arr = Array(0, 1, 2, 3) +val lst = List(arr*) // vararg splice argument +lst match + case List(0, 1, xs*) => println(xs) // binds xs to Seq(2, 3) + case List(1, _*) => // wildcard pattern +``` + +The old syntax for splice arguments will be phased out. + +```scala +/*!*/ val lst = List(arr: _*) // syntax error + lst match + case List(0, 1, xs @ _*) // ok, equivalent to `xs*` +``` + +## Syntax + +``` +ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ + | ‘(’ [Patterns ‘,’] Pattern2 ‘*’ ‘)’ + +ParArgumentExprs ::= ‘(’ [‘using’] ExprsInParens ‘)’ + | ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ ‘)’ +``` + +## Compatibility considerations + +To enable cross compilation between Scala 2 and Scala 3, the compiler will +accept both the old and the new syntax. Under the `-source future` setting, an error +will be emitted when the old syntax is encountered. An automatic rewrite from old +to new syntax is offered under `-source future-migration`. diff --git a/docs/_spec/TODOreference/changed-features/wildcards.md b/docs/_spec/TODOreference/changed-features/wildcards.md new file mode 100644 index 000000000000..0d3e13c3d7e0 --- /dev/null +++ b/docs/_spec/TODOreference/changed-features/wildcards.md @@ -0,0 +1,50 @@ +--- +layout: doc-page +title: Wildcard Arguments in Types +nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/wildcards.html +--- + +The syntax of wildcard arguments in types has changed from `_` to `?`. Example: +```scala +List[?] +Map[? <: AnyRef, ? >: Null] +``` + +## Motivation + +We would like to use the underscore syntax `_` to stand for an anonymous type parameter, aligning it with its meaning in +value parameter lists. So, just as `f(_)` is a shorthand for the lambda `x => f(x)`, in the future `C[_]` will be a shorthand +for the type lambda `[X] =>> C[X]`. This makes higher-kinded types easier to use. It also removes the wart that, used as a type +parameter, `F[_]` means `F` is a type constructor whereas used as a type, `F[_]` means it is a wildcard (i.e. existential) type. +In the future, `F[_]` will mean the same thing, no matter where it is used. + +We pick `?` as a replacement syntax for wildcard types, since it aligns with +[Java's syntax](https://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html). + +## Migration Strategy + +The migration to the new scheme is complicated, in particular since the [kind projector](https://github.com/typelevel/kind-projector) +compiler plugin still uses the reverse convention, with `?` meaning parameter placeholder instead of wildcard. Fortunately, kind projector has added `*` as an alternative syntax for `?`. + +A step-by-step migration is made possible with the following measures: + + 1. In Scala 3.0, both `_` and `?` are legal names for wildcards. + 2. In Scala 3.1, `_` is deprecated in favor of `?` as a name for a wildcard. A `-rewrite` option is + available to rewrite one to the other. + 3. In Scala 3.2, the meaning of `_` changes from wildcard to placeholder for type parameter. + 4. The Scala 3.1 behavior is already available today under the `-source future` setting. + +To smooth the transition for codebases that use kind-projector, we adopt the following measures under the command line +option `-Ykind-projector`: + + 1. In Scala 3.0, `*` is available as a type parameter placeholder. + 2. In Scala 3.2, `*` is deprecated in favor of `_`. A `-rewrite` option is + available to rewrite one to the other. + 3. In Scala 3.3, `*` is removed again, and all type parameter placeholders will be expressed with `_`. + +These rules make it possible to cross build between Scala 2 using the kind projector plugin and Scala 3.0 - 3.2 using the compiler option `-Ykind-projector`. + +There is also a migration path for users that want a one-time transition to syntax with `_` as a type parameter placeholder. +With option `-Ykind-projector:underscores` Scala 3 will regard `_` as a type parameter placeholder, leaving `?` as the only syntax for wildcards. + +To cross-compile with old Scala 2 sources, while using `_` a placeholder, you must use options `-Xsource:3 -P:kind-projector:underscore-placeholders` together with a recent version of kind-projector (`0.13` and higher) and most recent versions of Scala 2 (`2.13.5` and higher and `2.12.14` and higher) diff --git a/docs/_spec/TODOreference/contextual/by-name-context-parameters.md b/docs/_spec/TODOreference/contextual/by-name-context-parameters.md new file mode 100644 index 000000000000..3004bfb2c4c2 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/by-name-context-parameters.md @@ -0,0 +1,65 @@ +--- +layout: doc-page +title: "By-Name Context Parameters" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/by-name-context-parameters.html +--- + +Context parameters can be declared by-name to avoid a divergent inferred expansion. Example: + +```scala +trait Codec[T]: + def write(x: T): Unit + +given intCodec: Codec[Int] = ??? + +given optionCodec[T](using ev: => Codec[T]): Codec[Option[T]] with + def write(xo: Option[T]) = xo match + case Some(x) => ev.write(x) + case None => + +val s = summon[Codec[Option[Int]]] + +s.write(Some(33)) +s.write(None) +``` +As is the case for a normal by-name parameter, the argument for the context parameter `ev` +is evaluated on demand. In the example above, if the option value `x` is `None`, it is +not evaluated at all. + +The synthesized argument for a context parameter is backed by a local val +if this is necessary to prevent an otherwise diverging expansion. + +The precise steps for synthesizing an argument for a by-name context parameter of type `=> T` are as follows. + + 1. Create a new given of type `T`: + + ```scala + given lv: T = ??? + ``` + + where `lv` is an arbitrary fresh name. + + 1. This given is not immediately available as candidate for argument inference (making it immediately available could result in a loop in the synthesized computation). But it becomes available in all nested contexts that look again for an argument to a by-name context parameter. + + 1. If this search succeeds with expression `E`, and `E` contains references to `lv`, replace `E` by + + ```scala + { given lv: T = E; lv } + ``` + + Otherwise, return `E` unchanged. + +In the example above, the definition of `s` would be expanded as follows. + +```scala +val s = summon[Test.Codec[Option[Int]]]( + optionCodec[Int](using intCodec) +) +``` + +No local given instance was generated because the synthesized argument is not recursive. + +## Reference + +For more information, see [Issue #1998](https://github.com/lampepfl/dotty/issues/1998) +and the associated [Scala SIP](https://docs.scala-lang.org/sips/byname-implicits.html). diff --git a/docs/_spec/TODOreference/contextual/context-bounds.md b/docs/_spec/TODOreference/contextual/context-bounds.md new file mode 100644 index 000000000000..42479d6802b3 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/context-bounds.md @@ -0,0 +1,53 @@ +--- +layout: doc-page +title: "Context Bounds" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/context-bounds.html +--- + +A context bound is a shorthand for expressing the common pattern of a context parameter that depends on a type parameter. Using a context bound, the `maximum` function of the last section can be written like this: + +```scala +def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max) +``` + +A bound like `: Ord` on a type parameter `T` of a method or class indicates a context parameter `using Ord[T]`. The context parameter(s) generated from context bounds +are added as follows: + + - If the method parameters end in an implicit parameter list or using clause, + context parameters are added in front of that list. + - Otherwise they are added as a separate parameter clause at the end. + +Example: + +```scala +def f[T: C1 : C2, U: C3](x: T)(using y: U, z: V): R +``` + +would expand to + +```scala +def f[T, U](x: T)(using _: C1[T], _: C2[T], _: C3[U], y: U, z: V): R +``` + +Context bounds can be combined with subtype bounds. If both are present, subtype bounds come first, e.g. + +```scala +def g[T <: B : C](x: T): R = ... +``` + +## Migration + +To ease migration, context bounds in Dotty map in Scala 3.0 to old-style implicit parameters +for which arguments can be passed either with a `(using ...)` clause or with a normal application. From Scala 3.1 on, they will map to context parameters instead, as is described above. + +If the source version is `future-migration`, any pairing of an evidence +context parameter stemming from a context bound with a normal argument will give a migration +warning. The warning indicates that a `(using ...)` clause is needed instead. The rewrite can be +done automatically under `-rewrite`. + +## Syntax + +``` +TypeParamBounds ::= [SubtypeBounds] {ContextBound} +ContextBound ::= ‘:’ Type +``` diff --git a/docs/_spec/TODOreference/contextual/context-functions-spec.md b/docs/_spec/TODOreference/contextual/context-functions-spec.md new file mode 100644 index 000000000000..109513e9da86 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/context-functions-spec.md @@ -0,0 +1,79 @@ +--- +layout: doc-page +title: "Context Functions - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/context-functions-spec.html +--- + +## Syntax + +``` +Type ::= ... + | FunArgTypes ‘?=>’ Type +Expr ::= ... + | FunParams ‘?=>’ Expr +``` + +Context function types associate to the right, e.g. +`S ?=> T ?=> U` is the same as `S ?=> (T ?=> U)`. + +## Implementation + +Context function types are shorthands for class types that define `apply` +methods with context parameters. Specifically, the `N`-ary function type + +`T1, ..., TN ?=> R` is a shorthand for the class type +`ContextFunctionN[T1, ..., TN, R]`. Such class types are assumed to have the following definitions, for any value of `N >= 1`: + +```scala +package scala +trait ContextFunctionN[-T1, ..., -TN, +R]: + def apply(using x1: T1, ..., xN: TN): R +``` + +Context function types erase to normal function types, so these classes are +generated on the fly for typechecking, but not realized in actual code. + +Context function literals `(x1: T1, ..., xn: Tn) ?=> e` map +context parameters `xi` of types `Ti` to the result of evaluating the expression `e`. +The scope of each context parameter `xi` is `e`. The parameters must have pairwise distinct names. + +If the expected type of the context function literal is of the form +`scala.ContextFunctionN[S1, ..., Sn, R]`, the expected type of `e` is `R` and +the type `Ti` of any of the parameters `xi` can be omitted, in which case `Ti += Si` is assumed. If the expected type of the context function literal is +some other type, all context parameter types must be explicitly given, and the expected type of `e` is undefined. +The type of the context function literal is `scala.ContextFunctionN[S1, ...,Sn, T]`, where `T` is the widened +type of `e`. `T` must be equivalent to a type which does not refer to any of +the context parameters `xi`. + +The context function literal is evaluated as the instance creation expression + +```scala +new scala.ContextFunctionN[T1, ..., Tn, T]: + def apply(using x1: T1, ..., xn: Tn): T = e +``` + +A context parameter may also be a wildcard represented by an underscore `_`. In that case, a fresh name for the parameter is chosen arbitrarily. + +**Note:** The closing paragraph of the +[Anonymous Functions section](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#anonymous-functions) +of Scala 2.13 is subsumed by context function types and should be removed. + +Context function literals `(x1: T1, ..., xn: Tn) ?=> e` are +automatically created for any expression `e` whose expected type is +`scala.ContextFunctionN[T1, ..., Tn, R]`, unless `e` is +itself a context function literal. This is analogous to the automatic +insertion of [`scala.Function0`](https://scala-lang.org/api/3.x/scala/Function0.html) around expressions in by-name argument position. + +Context function types generalize to `N > 22` in the same way that function types do, see [the corresponding +documentation](../dropped-features/limit22.md). + +## Examples + +See the section on Expressiveness from [Simplicitly: foundations and +applications of implicit function +types](https://dl.acm.org/citation.cfm?id=3158130). + +## Type Checking + +After desugaring no additional typing rules are required for context function types. diff --git a/docs/_spec/TODOreference/contextual/context-functions.md b/docs/_spec/TODOreference/contextual/context-functions.md new file mode 100644 index 000000000000..0ad3c8757782 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/context-functions.md @@ -0,0 +1,154 @@ +--- +layout: doc-page +title: "Context Functions" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/context-functions.html +--- + +_Context functions_ are functions with (only) context parameters. +Their types are _context function types_. Here is an example of a context function type: + +```scala +type Executable[T] = ExecutionContext ?=> T +``` +Context functions are written using `?=>` as the "arrow" sign. +They are applied to synthesized arguments, in +the same way methods with context parameters are applied. For instance: +```scala + given ec: ExecutionContext = ... + + def f(x: Int): ExecutionContext ?=> Int = ... + + // could be written as follows with the type alias from above + // def f(x: Int): Executable[Int] = ... + + f(2)(using ec) // explicit argument + f(2) // argument is inferred +``` +Conversely, if the expected type of an expression `E` is a context function type +`(T_1, ..., T_n) ?=> U` and `E` is not already an +context function literal, `E` is converted to a context function literal by rewriting it to +```scala + (x_1: T1, ..., x_n: Tn) ?=> E +``` +where the names `x_1`, ..., `x_n` are arbitrary. This expansion is performed +before the expression `E` is typechecked, which means that `x_1`, ..., `x_n` +are available as givens in `E`. + +Like their types, context function literals are written using `?=>` as the arrow between parameters and results. They differ from normal function literals in that their types are context function types. + +For example, continuing with the previous definitions, +```scala + def g(arg: Executable[Int]) = ... + + g(22) // is expanded to g((ev: ExecutionContext) ?=> 22) + + g(f(2)) // is expanded to g((ev: ExecutionContext) ?=> f(2)(using ev)) + + g((ctx: ExecutionContext) ?=> f(3)) // is expanded to g((ctx: ExecutionContext) ?=> f(3)(using ctx)) + g((ctx: ExecutionContext) ?=> f(3)(using ctx)) // is left as it is +``` + +## Example: Builder Pattern + +Context function types have considerable expressive power. For +instance, here is how they can support the "builder pattern", where +the aim is to construct tables like this: +```scala + table { + row { + cell("top left") + cell("top right") + } + row { + cell("bottom left") + cell("bottom right") + } + } +``` +The idea is to define classes for `Table` and `Row` that allow the +addition of elements via `add`: +```scala + class Table: + val rows = new ArrayBuffer[Row] + def add(r: Row): Unit = rows += r + override def toString = rows.mkString("Table(", ", ", ")") + + class Row: + val cells = new ArrayBuffer[Cell] + def add(c: Cell): Unit = cells += c + override def toString = cells.mkString("Row(", ", ", ")") + + case class Cell(elem: String) +``` +Then, the `table`, `row` and `cell` constructor methods can be defined +with context function types as parameters to avoid the plumbing boilerplate +that would otherwise be necessary. +```scala + def table(init: Table ?=> Unit) = + given t: Table = Table() + init + t + + def row(init: Row ?=> Unit)(using t: Table) = + given r: Row = Row() + init + t.add(r) + + def cell(str: String)(using r: Row) = + r.add(new Cell(str)) +``` +With that setup, the table construction code above compiles and expands to: +```scala + table { ($t: Table) ?=> + + row { ($r: Row) ?=> + cell("top left")(using $r) + cell("top right")(using $r) + }(using $t) + + row { ($r: Row) ?=> + cell("bottom left")(using $r) + cell("bottom right")(using $r) + }(using $t) + } +``` +## Example: Postconditions + +As a larger example, here is a way to define constructs for checking arbitrary postconditions using an extension method `ensuring` so that the checked result can be referred to simply by `result`. The example combines opaque type aliases, context function types, and extension methods to provide a zero-overhead abstraction. + +```scala +object PostConditions: + opaque type WrappedResult[T] = T + + def result[T](using r: WrappedResult[T]): T = r + + extension [T](x: T) + def ensuring(condition: WrappedResult[T] ?=> Boolean): T = + assert(condition(using x)) + x +end PostConditions +import PostConditions.{ensuring, result} + +val s = List(1, 2, 3).sum.ensuring(result == 6) +``` +**Explanations**: We use a context function type `WrappedResult[T] ?=> Boolean` +as the type of the condition of `ensuring`. An argument to `ensuring` such as +`(result == 6)` will therefore have a given of type `WrappedResult[T]` in +scope to pass along to the `result` method. `WrappedResult` is a fresh type, to make sure +that we do not get unwanted givens in scope (this is good practice in all cases +where context parameters are involved). Since `WrappedResult` is an opaque type alias, its +values need not be boxed, and since `ensuring` is added as an extension method, its argument +does not need boxing either. Hence, the implementation of `ensuring` is close in efficiency to the best possible code one could write by hand: + +```scala +val s = + val result = List(1, 2, 3).sum + assert(result == 6) + result +``` +## Reference + +For more information, see the [blog article](https://www.scala-lang.org/blog/2016/12/07/implicit-function-types.html), +(which uses a different syntax that has been superseded). + +[More details](./context-functions-spec.md) diff --git a/docs/_spec/TODOreference/contextual/contextual.md b/docs/_spec/TODOreference/contextual/contextual.md new file mode 100644 index 000000000000..fda63397f8f9 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/contextual.md @@ -0,0 +1,83 @@ +--- +layout: index +title: "Contextual Abstractions" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual.html +--- + +## Critique of the Status Quo + +Scala's implicits are its most distinguished feature. They are _the_ fundamental way to abstract over context. They represent a unified paradigm with a great variety of use cases, among them: implementing type classes, establishing context, dependency injection, expressing capabilities, computing new types and proving relationships between them. + +Following Haskell, Scala was the second popular language to have some form of implicits. Other languages have followed suit. E.g [Rust's traits](https://doc.rust-lang.org/rust-by-example/trait.html) or [Swift's protocol extensions](https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#ID521). Design proposals are also on the table for Kotlin as [compile time dependency resolution](https://github.com/Kotlin/KEEP/blob/e863b25f8b3f2e9b9aaac361c6ee52be31453ee0/proposals/compile-time-dependency-resolution.md), for C# as [Shapes and Extensions](https://github.com/dotnet/csharplang/issues/164) +or for F# as [Traits](https://github.com/MattWindsor91/visualfsharp/blob/hackathon-vs/examples/fsconcepts.md). Implicits are also a common feature of theorem provers such as [Coq](https://coq.inria.fr/refman/language/extensions/implicit-arguments.html) or [Agda](https://agda.readthedocs.io/en/latest/language/implicit-arguments.html). + +Even though these designs use widely different terminology, they are all variants of the core idea of _term inference_. Given a type, the compiler synthesizes a "canonical" term that has that type. Scala embodies the idea in a purer form than most other languages: An implicit parameter directly leads to an inferred argument term that could also be written down explicitly. By contrast, type class based designs are less direct since they hide term inference behind some form of type classification and do not offer the option of writing the inferred quantities (typically, dictionaries) explicitly. + +Given that term inference is where the industry is heading, and given that Scala has it in a very pure form, how come implicits are not more popular? In fact, it's fair to say that implicits are at the same time Scala's most distinguished and most controversial feature. I believe this is due to a number of aspects that together make implicits harder to learn than necessary and also make it harder to prevent abuses. + +Particular criticisms are: + +1. Being very powerful, implicits are easily over-used and mis-used. This observation holds in almost all cases when we talk about _implicit conversions_, which, even though conceptually different, share the same syntax with other implicit definitions. For instance, regarding the two definitions + + ```scala + implicit def i1(implicit x: T): C[T] = ... + implicit def i2(x: T): C[T] = ... + ``` + + the first of these is a conditional implicit _value_, the second an implicit _conversion_. Conditional implicit values are a cornerstone for expressing type classes, whereas most applications of implicit conversions have turned out to be of dubious value. The problem is that many newcomers to the language start with defining implicit conversions since they are easy to understand and seem powerful and convenient. Scala 3 will put under a language flag both definitions and applications of "undisciplined" implicit conversions between types defined elsewhere. This is a useful step to push back against overuse of implicit conversions. But the problem remains that syntactically, conversions and values just look too similar for comfort. + +2. Another widespread abuse is over-reliance on implicit imports. This often leads to inscrutable type errors that go away with the right import incantation, leaving a feeling of frustration. Conversely, it is hard to see what implicits a program uses since implicits can hide anywhere in a long list of imports. + +3. The syntax of implicit definitions is too minimal. It consists of a single modifier, `implicit`, that can be attached to a large number of language constructs. A problem with this for newcomers is that it conveys mechanism instead of intent. For instance, a type class instance is an implicit object or val if unconditional and an implicit def with implicit parameters referring to some class if conditional. This describes precisely what the implicit definitions translate to -- just drop the `implicit` modifier, and that's it! But the cues that define intent are rather indirect and can be easily misread, as demonstrated by the definitions of `i1` and `i2` above. + +4. The syntax of implicit parameters also has shortcomings. While implicit _parameters_ are designated specifically, arguments are not. Passing an argument to an implicit parameter looks like a regular application `f(arg)`. This is problematic because it means there can be confusion regarding what parameter gets instantiated in a call. For instance, in + + ```scala + def currentMap(implicit ctx: Context): Map[String, Int] + ``` + + one cannot write `currentMap("abc")` since the string `"abc"` is taken as explicit argument to the implicit `ctx` parameter. One has to write `currentMap.apply("abc")` instead, which is awkward and irregular. For the same reason, a method definition can only have one implicit parameter section and it must always come last. This restriction not only reduces orthogonality, but also prevents some useful program constructs, such as a method with a regular parameter whose type depends on an implicit value. Finally, it's also a bit annoying that implicit parameters must have a name, even though in many cases that name is never referenced. + +5. Implicits pose challenges for tooling. The set of available implicits depends on context, so command completion has to take context into account. This is feasible in an IDE but tools like [Scaladoc](https://docs.scala-lang.org/overviews/scaladoc/overview.html) that are based on static web pages can only provide an approximation. Another problem is that failed implicit searches often give very unspecific error messages, in particular if some deeply recursive implicit search has failed. Note that the Scala 3 compiler has already made a lot of progress in the error diagnostics area. If a recursive search fails some levels down, it shows what was constructed and what is missing. Also, it suggests imports that can bring missing implicits in scope. + +None of the shortcomings is fatal, after all implicits are very widely used, and many libraries and applications rely on them. But together, they make code using implicits a lot more cumbersome and less clear than it could be. + +Historically, many of these shortcomings come from the way implicits were gradually "discovered" in Scala. Scala originally had only implicit conversions with the intended use case of "extending" a class or trait after it was defined, i.e. what is expressed by implicit classes in later versions of Scala. Implicit parameters and instance definitions came later in 2006 and we picked similar syntax since it seemed convenient. For the same reason, no effort was made to distinguish implicit imports or arguments from normal ones. + +Existing Scala programmers by and large have gotten used to the status quo and see little need for change. But for newcomers this status quo presents a big hurdle. I believe if we want to overcome that hurdle, we should take a step back and allow ourselves to consider a radically new design. + +## The New Design + +The following pages introduce a redesign of contextual abstractions in Scala. They introduce four fundamental changes: + +1. [Given Instances](./givens.md) are a new way to define basic terms that can be synthesized. They replace implicit definitions. The core principle of the proposal is that, rather than mixing the `implicit` modifier with a large number of features, we have a single way to define terms that can be synthesized for types. + +2. [Using Clauses](./using-clauses.md) are a new syntax for implicit _parameters_ and their _arguments_. It unambiguously aligns parameters and arguments, solving a number of language warts. It also allows us to have several `using` clauses in a definition. + +3. ["Given" Imports](./given-imports.md) are a new class of import selectors that specifically import + givens and nothing else. + +4. [Implicit Conversions](./conversions.md) are now expressed as given instances of a standard `Conversion` class. All other forms of implicit conversions will be phased out. + +This section also contains pages describing other language features that are related to context abstraction. These are: + +- [Context Bounds](./context-bounds.md), which carry over unchanged. +- [Extension Methods](./extension-methods.md) replace implicit classes in a way that integrates better with type classes. +- [Implementing Type Classes](./type-classes.md) demonstrates how some common type classes can be implemented using the new constructs. +- [Type Class Derivation](./derivation.md) introduces constructs to automatically derive type class instances for ADTs. +- [Multiversal Equality](./multiversal-equality.md) introduces a special type class to support type safe equality. +- [Context Functions](./context-functions.md) provide a way to abstract over context parameters. +- [By-Name Context Parameters](./by-name-context-parameters.md) are an essential tool to define recursive synthesized values without looping. +- [Relationship with Scala 2 Implicits](./relationship-implicits.md) discusses the relationship between old-style implicits and new-style givens and how to migrate from one to the other. + +Overall, the new design achieves a better separation of term inference from the rest of the language: There is a single way to define givens instead of a multitude of forms all taking an `implicit` modifier. There is a single way to introduce implicit parameters and arguments instead of conflating implicit with normal arguments. There is a separate way to import givens that does not allow them to hide in a sea of normal imports. And there is a single way to define an implicit conversion which is clearly marked as such and does not require special syntax. + +This design thus avoids feature interactions and makes the language more consistent and orthogonal. It will make implicits easier to learn and harder to abuse. It will greatly improve the clarity of the 95% of Scala programs that use implicits. It has thus the potential to fulfil the promise of term inference in a principled way that is also accessible and friendly. + +Could we achieve the same goals by tweaking existing implicits? After having tried for a long time, I believe now that this is impossible. + +- First, some of the problems are clearly syntactic and require different syntax to solve them. +- Second, there is the problem how to migrate. We cannot change the rules in mid-flight. At some stage of language evolution we need to accommodate both the new and the old rules. With a syntax change, this is easy: Introduce the new syntax with new rules, support the old syntax for a while to facilitate cross compilation, deprecate and phase out the old syntax at some later time. Keeping the same syntax does not offer this path, and in fact does not seem to offer any viable path for evolution +- Third, even if we would somehow succeed with migration, we still have the problem + how to teach this. We cannot make existing tutorials go away. Almost all existing tutorials start with implicit conversions, which will go away; they use normal imports, which will go away, and they explain calls to methods with implicit parameters by expanding them to plain applications, which will also go away. This means that we'd have + to add modifications and qualifications to all existing literature and courseware, likely causing more confusion with beginners instead of less. By contrast, with a new syntax there is a clear criterion: Any book or courseware that mentions `implicit` is outdated and should be updated. diff --git a/docs/_spec/TODOreference/contextual/conversions.md b/docs/_spec/TODOreference/contextual/conversions.md new file mode 100644 index 000000000000..1ce8d42074e7 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/conversions.md @@ -0,0 +1,76 @@ +--- +layout: doc-page +title: "Implicit Conversions" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/conversions.html +--- + +Implicit conversions are defined by given instances of the `scala.Conversion` class. +This class is defined in package `scala` as follows: +```scala +abstract class Conversion[-T, +U] extends (T => U): + def apply (x: T): U +``` +For example, here is an implicit conversion from `String` to `Token`: +```scala +given Conversion[String, Token] with + def apply(str: String): Token = new KeyWord(str) +``` +Using an alias this can be expressed more concisely as: +```scala +given Conversion[String, Token] = new KeyWord(_) +``` +An implicit conversion is applied automatically by the compiler in three situations: + +1. If an expression `e` has type `T`, and `T` does not conform to the expression's expected type `S`. +2. In a selection `e.m` with `e` of type `T`, but `T` defines no member `m`. +3. In an application `e.m(args)` with `e` of type `T`, if `T` does define + some member(s) named `m`, but none of these members can be applied to the arguments `args`. + +In the first case, the compiler looks for a given `scala.Conversion` instance that maps +an argument of type `T` to type `S`. In the second and third +case, it looks for a given `scala.Conversion` instance that maps an argument of type `T` +to a type that defines a member `m` which can be applied to `args` if present. +If such an instance `C` is found, the expression `e` is replaced by `C.apply(e)`. + +## Examples + +1. The `Predef` package contains "auto-boxing" conversions that map +primitive number types to subclasses of `java.lang.Number`. For instance, the +conversion from `Int` to `java.lang.Integer` can be defined as follows: + ```scala + given int2Integer: Conversion[Int, java.lang.Integer] = + java.lang.Integer.valueOf(_) + ``` + +2. The "magnet" pattern is sometimes used to express many variants of a method. Instead of defining overloaded versions of the method, one can also let the method take one or more arguments of specially defined "magnet" types, into which various argument types can be converted. Example: + ```scala + object Completions: + + // The argument "magnet" type + enum CompletionArg: + case Error(s: String) + case Response(f: Future[HttpResponse]) + case Status(code: Future[StatusCode]) + + object CompletionArg: + + // conversions defining the possible arguments to pass to `complete` + // these always come with CompletionArg + // They can be invoked explicitly, e.g. + // + // CompletionArg.fromStatusCode(statusCode) + + given fromString : Conversion[String, CompletionArg] = Error(_) + given fromFuture : Conversion[Future[HttpResponse], CompletionArg] = Response(_) + given fromStatusCode: Conversion[Future[StatusCode], CompletionArg] = Status(_) + end CompletionArg + import CompletionArg.* + + def complete[T](arg: CompletionArg) = arg match + case Error(s) => ... + case Response(f) => ... + case Status(code) => ... + + end Completions + ``` +This setup is more complicated than simple overloading of `complete`, but it can still be useful if normal overloading is not available (as in the case above, since we cannot have two overloaded methods that take `Future[...]` arguments), or if normal overloading would lead to a combinatorial explosion of variants. diff --git a/docs/_spec/TODOreference/contextual/derivation-macro.md b/docs/_spec/TODOreference/contextual/derivation-macro.md new file mode 100644 index 000000000000..be7565616913 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/derivation-macro.md @@ -0,0 +1,205 @@ +--- +layout: doc-page +title: "How to write a type class `derived` method using macros" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/derivation-macro.html +--- + +In the main [derivation](./derivation.md) documentation page, we explained the +details behind `Mirror`s and type class derivation. Here we demonstrate how to +implement a type class `derived` method using macros only. We follow the same +example of deriving `Eq` instances and for simplicity we support a `Product` +type e.g., a case class `Person`. The low-level method we will use to implement +the `derived` method exploits quotes, splices of both expressions and types and +the `scala.quoted.Expr.summon` method which is the equivalent of +`summonFrom`. The former is suitable for use in a quote context, used within +macros. + +As in the original code, the type class definition is the same: + +```scala +trait Eq[T]: + def eqv(x: T, y: T): Boolean +``` + +we need to implement a method `Eq.derived` on the companion object of `Eq` that +produces a quoted instance for `Eq[T]`. Here is a possible signature, + +```scala +given derived[T: Type](using Quotes): Expr[Eq[T]] +``` + +and for comparison reasons we give the same signature we had with `inline`: + +```scala +inline given derived[T](using Mirror.Of[T]): Eq[T] = ??? +``` + +Note, that since a type is used in a subsequent stage it will need to be lifted +to a `Type` by using the corresponding context bound. Also, note that we can +summon the quoted `Mirror` inside the body of the `derived` thus we can omit it +from the signature. The body of the `derived` method is shown below: + + +```scala +given derived[T: Type](using Quotes): Expr[Eq[T]] = + import quotes.reflect.* + + val ev: Expr[Mirror.Of[T]] = Expr.summon[Mirror.Of[T]].get + + ev match + case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }} => + val elemInstances = summonAll[elementTypes] + def eqProductBody(x: Expr[Product], y: Expr[Product])(using Quotes): Expr[Boolean] = { + elemInstances.zipWithIndex.foldLeft(Expr(true)) { + case (acc, ('{ $elem: Eq[t] }, index)) => + val indexExpr = Expr(index) + val e1 = '{ $x.productElement($indexExpr).asInstanceOf[t] } + val e2 = '{ $y.productElement($indexExpr).asInstanceOf[t] } + '{ $acc && $elem.eqv($e1, $e2) } + } + } + '{ eqProduct((x: T, y: T) => ${eqProductBody('x.asExprOf[Product], 'y.asExprOf[Product])}) } + + // case for Mirror.ProductOf[T] + // ... +``` + +Note, that in the `inline` case we can merely write +`summonAll[m.MirroredElemTypes]` inside the inline method but here, since +`Expr.summon` is required, we can extract the element types in a macro fashion. +Being inside a macro, our first reaction would be to write the code below. Since +the path inside the type argument is not stable this cannot be used: + +```scala +'{ + summonAll[$m.MirroredElemTypes] +} +``` + +Instead we extract the tuple-type for element types using pattern matching over +quotes and more specifically of the refined type: + +```scala + case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }} => ... +``` + +Shown below is the implementation of `summonAll` as a macro. We assume that +given instances for our primitive types exist. + +```scala +def summonAll[T: Type](using Quotes): List[Expr[Eq[_]]] = + Type.of[T] match + case '[String *: tpes] => '{ summon[Eq[String]] } :: summonAll[tpes] + case '[Int *: tpes] => '{ summon[Eq[Int]] } :: summonAll[tpes] + case '[tpe *: tpes] => derived[tpe] :: summonAll[tpes] + case '[EmptyTuple] => Nil +``` + +One additional difference with the body of `derived` here as opposed to the one +with `inline` is that with macros we need to synthesize the body of the code during the +macro-expansion time. That is the rationale behind the `eqProductBody` function. +Assuming that we calculate the equality of two `Person`s defined with a case +class that holds a name of type [`String`](https://scala-lang.org/api/3.x/scala/Predef$.html#String-0) +and an age of type `Int`, the equality check we want to generate is the following: + +```scala + true + && Eq[String].eqv(x.productElement(0),y.productElement(0)) + && Eq[Int].eqv(x.productElement(1), y.productElement(1)) +``` + +## Calling the derived method inside the macro + +Following the rules in [Macros](../metaprogramming/metaprogramming.md) we create two methods. +One that hosts the top-level splice `eqv` and one that is the implementation. +Alternatively and what is shown below is that we can call the `eqv` method +directly. The `eqGen` can trigger the derivation. + +```scala +extension [T](inline x: T) + inline def === (inline y: T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) + +inline given eqGen[T]: Eq[T] = ${ Eq.derived[T] } +``` + +Note, that we use inline method syntax and we can compare instance such as +`Sm(Person("Test", 23)) === Sm(Person("Test", 24))` for e.g., the following two +types: + +```scala +case class Person(name: String, age: Int) + +enum Opt[+T]: + case Sm(t: T) + case Nn +``` + +The full code is shown below: + +```scala +import scala.deriving.* +import scala.quoted.* + + +trait Eq[T]: + def eqv(x: T, y: T): Boolean + +object Eq: + given Eq[String] with + def eqv(x: String, y: String) = x == y + + given Eq[Int] with + def eqv(x: Int, y: Int) = x == y + + def eqProduct[T](body: (T, T) => Boolean): Eq[T] = + new Eq[T]: + def eqv(x: T, y: T): Boolean = body(x, y) + + def eqSum[T](body: (T, T) => Boolean): Eq[T] = + new Eq[T]: + def eqv(x: T, y: T): Boolean = body(x, y) + + def summonAll[T: Type](using Quotes): List[Expr[Eq[_]]] = + Type.of[T] match + case '[String *: tpes] => '{ summon[Eq[String]] } :: summonAll[tpes] + case '[Int *: tpes] => '{ summon[Eq[Int]] } :: summonAll[tpes] + case '[tpe *: tpes] => derived[tpe] :: summonAll[tpes] + case '[EmptyTuple] => Nil + + given derived[T: Type](using q: Quotes): Expr[Eq[T]] = + import quotes.reflect.* + + val ev: Expr[Mirror.Of[T]] = Expr.summon[Mirror.Of[T]].get + + ev match + case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }} => + val elemInstances = summonAll[elementTypes] + val eqProductBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => + elemInstances.zipWithIndex.foldLeft(Expr(true: Boolean)) { + case (acc, (elem, index)) => + val e1 = '{$x.asInstanceOf[Product].productElement(${Expr(index)})} + val e2 = '{$y.asInstanceOf[Product].productElement(${Expr(index)})} + + '{ $acc && $elem.asInstanceOf[Eq[Any]].eqv($e1, $e2) } + } + '{ eqProduct((x: T, y: T) => ${eqProductBody('x, 'y)}) } + + case '{ $m: Mirror.SumOf[T] { type MirroredElemTypes = elementTypes }} => + val elemInstances = summonAll[elementTypes] + val eqSumBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => + val ordx = '{ $m.ordinal($x) } + val ordy = '{ $m.ordinal($y) } + + val elements = Expr.ofList(elemInstances) + '{ $ordx == $ordy && $elements($ordx).asInstanceOf[Eq[Any]].eqv($x, $y) } + + '{ eqSum((x: T, y: T) => ${eqSumBody('x, 'y)}) } + end derived +end Eq + +object Macro3: + extension [T](inline x: T) + inline def === (inline y: T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) + + inline given eqGen[T]: Eq[T] = ${ Eq.derived[T] } +``` diff --git a/docs/_spec/TODOreference/contextual/derivation.md b/docs/_spec/TODOreference/contextual/derivation.md new file mode 100644 index 000000000000..bad47dcb0096 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/derivation.md @@ -0,0 +1,425 @@ +--- +layout: doc-page +title: "Type Class Derivation" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/derivation.html +--- + +Type class derivation is a way to automatically generate given instances for type classes which satisfy some simple +conditions. A type class in this sense is any trait or class with a type parameter determining the type being operated +on. Common examples are `Eq`, `Ordering`, or `Show`. For example, given the following `Tree` algebraic data type +(ADT), + +```scala +enum Tree[T] derives Eq, Ordering, Show: + case Branch(left: Tree[T], right: Tree[T]) + case Leaf(elem: T) +``` + +The `derives` clause generates the following given instances for the `Eq`, `Ordering` and `Show` type classes in the +companion object of `Tree`, + +```scala +given [T: Eq] : Eq[Tree[T]] = Eq.derived +given [T: Ordering] : Ordering[Tree[T]] = Ordering.derived +given [T: Show] : Show[Tree[T]] = Show.derived +``` + +We say that `Tree` is the _deriving type_ and that the `Eq`, `Ordering` and `Show` instances are _derived instances_. + +## Types supporting `derives` clauses + +All data types can have a `derives` clause. This document focuses primarily on data types which also have a given instance +of the `Mirror` type class available. + +`Mirror` type class instances provide information at the type level about the components and labelling of the type. +They also provide minimal term level infrastructure to allow higher level libraries to provide comprehensive +derivation support. + +Instances of the `Mirror` type class are generated automatically by the compiler +unconditionally for: +- enums and enum cases, +- case objects. + +Instances for `Mirror` are also generated conditionally for: +- case classes where the constructor is visible at the callsite (always true if the companion is not a case object) +- sealed classes and sealed traits where: + - there exists at least one child case, + - each child case is reachable from the parent's definition, + - if the sealed trait/class has no companion, then each child case is reachable from the callsite through the prefix of the type being mirrored, + - and where the compiler can generate a `Mirror` type class instance for each child case. + + +The `Mirror` type class definition is as follows: + +```scala +sealed trait Mirror: + + /** the type being mirrored */ + type MirroredType + + /** the type of the elements of the mirrored type */ + type MirroredElemTypes + + /** The mirrored *-type */ + type MirroredMonoType + + /** The name of the type */ + type MirroredLabel <: String + + /** The names of the elements of the type */ + type MirroredElemLabels <: Tuple + +object Mirror: + + /** The Mirror for a product type */ + trait Product extends Mirror: + + /** Create a new instance of type `T` with elements + * taken from product `p`. + */ + def fromProduct(p: scala.Product): MirroredMonoType + + trait Sum extends Mirror: + + /** The ordinal number of the case class of `x`. + * For enums, `ordinal(x) == x.ordinal` + */ + def ordinal(x: MirroredMonoType): Int + +end Mirror +``` + +Product types (i.e. case classes and objects, and enum cases) have mirrors which are subtypes of `Mirror.Product`. Sum +types (i.e. sealed class or traits with product children, and enums) have mirrors which are subtypes of `Mirror.Sum`. + +For the `Tree` ADT from above the following `Mirror` instances will be automatically provided by the compiler, + +```scala +// Mirror for Tree +new Mirror.Sum: + type MirroredType = Tree + type MirroredElemTypes[T] = (Branch[T], Leaf[T]) + type MirroredMonoType = Tree[_] + type MirroredLabel = "Tree" + type MirroredElemLabels = ("Branch", "Leaf") + + def ordinal(x: MirroredMonoType): Int = x match + case _: Branch[_] => 0 + case _: Leaf[_] => 1 + +// Mirror for Branch +new Mirror.Product: + type MirroredType = Branch + type MirroredElemTypes[T] = (Tree[T], Tree[T]) + type MirroredMonoType = Branch[_] + type MirroredLabel = "Branch" + type MirroredElemLabels = ("left", "right") + + def fromProduct(p: Product): MirroredMonoType = + new Branch(...) + +// Mirror for Leaf +new Mirror.Product: + type MirroredType = Leaf + type MirroredElemTypes[T] = Tuple1[T] + type MirroredMonoType = Leaf[_] + type MirroredLabel = "Leaf" + type MirroredElemLabels = Tuple1["elem"] + + def fromProduct(p: Product): MirroredMonoType = + new Leaf(...) +``` + +If a Mirror cannot be generated automatically for a given type, an error will appear explaining why it is neither a supported +sum type nor a product type. For example, if `A` is a trait that is not sealed, + +``` +No given instance of type deriving.Mirror.Of[A] was found for parameter x of method summon in object Predef. Failed to synthesize an instance of type deriving.Mirror.Of[A]: + * trait A is not a generic product because it is not a case class + * trait A is not a generic sum because it is not a sealed trait +``` + + +Note the following properties of `Mirror` types, + ++ Properties are encoded using types rather than terms. This means that they have no runtime footprint unless used and + also that they are a compile time feature for use with Scala 3's metaprogramming facilities. ++ There is no restriction against the mirrored type being a local or inner class. ++ The kinds of `MirroredType` and `MirroredElemTypes` match the kind of the data type the mirror is an instance for. + This allows `Mirror`s to support ADTs of all kinds. ++ There is no distinct representation type for sums or products (ie. there is no `HList` or `Coproduct` type as in + Scala 2 versions of Shapeless). Instead the collection of child types of a data type is represented by an ordinary, + possibly parameterized, tuple type. Scala 3's metaprogramming facilities can be used to work with these tuple types + as-is, and higher level libraries can be built on top of them. ++ For both product and sum types, the elements of `MirroredElemTypes` are arranged in definition order (i.e. `Branch[T]` + precedes `Leaf[T]` in `MirroredElemTypes` for `Tree` because `Branch` is defined before `Leaf` in the source file). + This means that `Mirror.Sum` differs in this respect from Shapeless's generic representation for ADTs in Scala 2, + where the constructors are ordered alphabetically by name. ++ The methods `ordinal` and `fromProduct` are defined in terms of `MirroredMonoType` which is the type of kind-`*` + which is obtained from `MirroredType` by wildcarding its type parameters. + +## Type classes supporting automatic deriving + +A trait or class can appear in a `derives` clause if its companion object defines a method named `derived`. The +signature and implementation of a `derived` method for a type class `TC[_]` are arbitrary but it is typically of the +following form, + +```scala +import scala.deriving.Mirror + +inline def derived[T](using Mirror.Of[T]): TC[T] = ... +``` + +That is, the `derived` method takes a context parameter of (some subtype of) type `Mirror` which defines the shape of +the deriving type `T`, and computes the type class implementation according to that shape. This is all that the +provider of an ADT with a `derives` clause has to know about the derivation of a type class instance. + +Note that `derived` methods may have context `Mirror` parameters indirectly (e.g. by having a context argument which in turn +has a context `Mirror` parameter, or not at all (e.g. they might use some completely different user-provided mechanism, for +instance using Scala 3 macros or runtime reflection). We expect that (direct or indirect) `Mirror` based implementations +will be the most common and that is what this document emphasises. + +Type class authors will most likely use higher level derivation or generic programming libraries to implement +`derived` methods. An example of how a `derived` method might be implemented using _only_ the low level facilities +described above and Scala 3's general metaprogramming features is provided below. It is not anticipated that type class +authors would normally implement a `derived` method in this way, however this walkthrough can be taken as a guide for +authors of the higher level derivation libraries that we expect typical type class authors will use (for a fully +worked out example of such a library, see [Shapeless 3](https://github.com/milessabin/shapeless/tree/shapeless-3)). + +## How to write a type class `derived` method using low level mechanisms + +The low-level method we will use to implement a type class `derived` method in this example exploits three new +type-level constructs in Scala 3: inline methods, inline matches, and implicit searches via `summonInline` or `summonFrom`. Given this definition of the +`Eq` type class, + +```scala +trait Eq[T]: + def eqv(x: T, y: T): Boolean +``` + +we need to implement a method `Eq.derived` on the companion object of `Eq` that produces a given instance for `Eq[T]` given +a `Mirror[T]`. Here is a possible implementation, + +```scala +import scala.deriving.Mirror + +inline given derived[T](using m: Mirror.Of[T]): Eq[T] = + val elemInstances = summonAll[m.MirroredElemTypes] // (1) + inline m match // (2) + case s: Mirror.SumOf[T] => eqSum(s, elemInstances) + case p: Mirror.ProductOf[T] => eqProduct(p, elemInstances) +``` + +Note that `derived` is defined as an `inline` given. This means that the method will be expanded at +call sites (for instance the compiler generated instance definitions in the companion objects of ADTs which have a +`derived Eq` clause), and also that it can be used recursively if necessary, to compute instances for children. + +The body of this method (1) first materializes the `Eq` instances for all the child types of type the instance is +being derived for. This is either all the branches of a sum type or all the fields of a product type. The +implementation of `summonAll` is `inline` and uses Scala 3's `summonInline` construct to collect the instances as a +`List`, + +```scala +inline def summonAll[T <: Tuple]: List[Eq[_]] = + inline erasedValue[T] match + case _: EmptyTuple => Nil + case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts] +``` + +with the instances for children in hand the `derived` method uses an `inline match` to dispatch to methods which can +construct instances for either sums or products (2). Note that because `derived` is `inline` the match will be +resolved at compile-time and only the left-hand side of the matching case will be inlined into the generated code with +types refined as revealed by the match. + +In the sum case, `eqSum`, we use the runtime `ordinal` values of the arguments to `eqv` to first check if the two +values are of the same subtype of the ADT (3) and then, if they are, to further test for equality based on the `Eq` +instance for the appropriate ADT subtype using the auxiliary method `check` (4). + +```scala +import scala.deriving.Mirror + +def eqSum[T](s: Mirror.SumOf[T], elems: List[Eq[_]]): Eq[T] = + new Eq[T]: + def eqv(x: T, y: T): Boolean = + val ordx = s.ordinal(x) // (3) + (s.ordinal(y) == ordx) && check(elems(ordx))(x, y) // (4) +``` + +In the product case, `eqProduct` we test the runtime values of the arguments to `eqv` for equality as products based +on the `Eq` instances for the fields of the data type (5), + +```scala +import scala.deriving.Mirror + +def eqProduct[T](p: Mirror.ProductOf[T], elems: List[Eq[_]]): Eq[T] = + new Eq[T]: + def eqv(x: T, y: T): Boolean = + iterator(x).zip(iterator(y)).zip(elems.iterator).forall { // (5) + case ((x, y), elem) => check(elem)(x, y) + } +``` + +Pulling this all together we have the following complete implementation, + +```scala +import scala.deriving.* +import scala.compiletime.{erasedValue, summonInline} + +inline def summonAll[T <: Tuple]: List[Eq[_]] = + inline erasedValue[T] match + case _: EmptyTuple => Nil + case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts] + +trait Eq[T]: + def eqv(x: T, y: T): Boolean + +object Eq: + given Eq[Int] with + def eqv(x: Int, y: Int) = x == y + + def check(elem: Eq[_])(x: Any, y: Any): Boolean = + elem.asInstanceOf[Eq[Any]].eqv(x, y) + + def iterator[T](p: T) = p.asInstanceOf[Product].productIterator + + def eqSum[T](s: Mirror.SumOf[T], elems: => List[Eq[_]]): Eq[T] = + new Eq[T]: + def eqv(x: T, y: T): Boolean = + val ordx = s.ordinal(x) + (s.ordinal(y) == ordx) && check(elems(ordx))(x, y) + + def eqProduct[T](p: Mirror.ProductOf[T], elems: => List[Eq[_]]): Eq[T] = + new Eq[T]: + def eqv(x: T, y: T): Boolean = + iterator(x).zip(iterator(y)).zip(elems.iterator).forall { + case ((x, y), elem) => check(elem)(x, y) + } + + inline given derived[T](using m: Mirror.Of[T]): Eq[T] = + lazy val elemInstances = summonAll[m.MirroredElemTypes] + inline m match + case s: Mirror.SumOf[T] => eqSum(s, elemInstances) + case p: Mirror.ProductOf[T] => eqProduct(p, elemInstances) +end Eq +``` + +we can test this relative to a simple ADT like so, + +```scala +enum Opt[+T] derives Eq: + case Sm(t: T) + case Nn + +@main def test(): Unit = + import Opt.* + val eqoi = summon[Eq[Opt[Int]]] + assert(eqoi.eqv(Sm(23), Sm(23))) + assert(!eqoi.eqv(Sm(23), Sm(13))) + assert(!eqoi.eqv(Sm(23), Nn)) +``` + +In this case the code that is generated by the inline expansion for the derived `Eq` instance for `Opt` looks like the +following, after a little polishing, + +```scala +given derived$Eq[T](using eqT: Eq[T]): Eq[Opt[T]] = + eqSum( + summon[Mirror[Opt[T]]], + List( + eqProduct(summon[Mirror[Sm[T]]], List(summon[Eq[T]])), + eqProduct(summon[Mirror[Nn.type]], Nil) + ) + ) +``` + +Alternative approaches can be taken to the way that `derived` methods can be defined. For example, more aggressively +inlined variants using Scala 3 macros, whilst being more involved for type class authors to write than the example +above, can produce code for type classes like `Eq` which eliminate all the abstraction artefacts (eg. the `Lists` of +child instances in the above) and generate code which is indistinguishable from what a programmer might write by hand. +As a third example, using a higher level library such as Shapeless the type class author could define an equivalent +`derived` method as, + +```scala +given eqSum[A](using inst: => K0.CoproductInstances[Eq, A]): Eq[A] with + def eqv(x: A, y: A): Boolean = inst.fold2(x, y)(false)( + [t] => (eqt: Eq[t], t0: t, t1: t) => eqt.eqv(t0, t1) + ) + +given eqProduct[A](using inst: K0.ProductInstances[Eq, A]): Eq[A] with + def eqv(x: A, y: A): Boolean = inst.foldLeft2(x, y)(true: Boolean)( + [t] => (acc: Boolean, eqt: Eq[t], t0: t, t1: t) => + Complete(!eqt.eqv(t0, t1))(false)(true) + ) + +inline def derived[A](using gen: K0.Generic[A]): Eq[A] = + gen.derive(eqProduct, eqSum) +``` + +The framework described here enables all three of these approaches without mandating any of them. + +For a brief discussion on how to use macros to write a type class `derived` +method please read more at [How to write a type class `derived` method using macros](./derivation-macro.md). + +## Deriving instances elsewhere + +Sometimes one would like to derive a type class instance for an ADT after the ADT is defined, without being able to +change the code of the ADT itself. To do this, simply define an instance using the `derived` method of the type class +as right-hand side. E.g, to implement `Ordering` for `Option` define, + +```scala +given [T: Ordering]: Ordering[Option[T]] = Ordering.derived +``` + +Assuming the `Ordering.derived` method has a context parameter of type `Mirror[T]` it will be satisfied by the +compiler generated `Mirror` instance for `Option` and the derivation of the instance will be expanded on the right +hand side of this definition in the same way as an instance defined in ADT companion objects. + +## Syntax + +``` +Template ::= InheritClauses [TemplateBody] +EnumDef ::= id ClassConstr InheritClauses EnumBody +InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] +ConstrApps ::= ConstrApp {‘with’ ConstrApp} + | ConstrApp {‘,’ ConstrApp} +``` + +**Note:** To align `extends` clauses and `derives` clauses, Scala 3 also allows multiple +extended types to be separated by commas. So the following is now legal: + +```scala +class A extends B, C { ... } +``` + +It is equivalent to the old form + +```scala +class A extends B with C { ... } +``` + +## Discussion + +This type class derivation framework is intentionally very small and low-level. There are essentially two pieces of +infrastructure in compiler-generated `Mirror` instances, + ++ type members encoding properties of the mirrored types. ++ a minimal value level mechanism for working generically with terms of the mirrored types. + +The `Mirror` infrastructure can be seen as an extension of the existing `Product` infrastructure for case classes: +typically `Mirror` types will be implemented by the ADTs companion object, hence the type members and the `ordinal` or +`fromProduct` methods will be members of that object. The primary motivation for this design decision, and the +decision to encode properties via types rather than terms was to keep the bytecode and runtime footprint of the +feature small enough to make it possible to provide `Mirror` instances _unconditionally_. + +Whilst `Mirrors` encode properties precisely via type members, the value level `ordinal` and `fromProduct` are +somewhat weakly typed (because they are defined in terms of `MirroredMonoType`) just like the members of `Product`. +This means that code for generic type classes has to ensure that type exploration and value selection proceed in +lockstep and it has to assert this conformance in some places using casts. If generic type classes are correctly +written these casts will never fail. + +As mentioned, however, the compiler-provided mechanism is intentionally very low level and it is anticipated that +higher level type class derivation and generic programming libraries will build on this and Scala 3's other +metaprogramming facilities to hide these low-level details from type class authors and general users. Type class +derivation in the style of both Shapeless and Magnolia are possible (a prototype of Shapeless 3, which combines +aspects of both Shapeless 2 and Magnolia has been developed alongside this language feature) as is a more aggressively +inlined style, supported by Scala 3's new quote/splice macro and inlining facilities. diff --git a/docs/_spec/TODOreference/contextual/extension-methods.md b/docs/_spec/TODOreference/contextual/extension-methods.md new file mode 100644 index 000000000000..d23cadf513d7 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/extension-methods.md @@ -0,0 +1,306 @@ +--- +layout: doc-page +title: "Extension Methods" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/extension-methods.html +--- + +Extension methods allow one to add methods to a type after the type is defined. Example: + +```scala +case class Circle(x: Double, y: Double, radius: Double) + +extension (c: Circle) + def circumference: Double = c.radius * math.Pi * 2 +``` + +Like regular methods, extension methods can be invoked with infix `.`: + +```scala +val circle = Circle(0, 0, 1) +circle.circumference +``` + +## Translation of Extension Methods + +An extension method translates to a specially labelled method that takes the leading parameter section as its first argument list. The label, expressed +as `` here, is compiler-internal. So, the definition of `circumference` above translates to the following method, and can also be invoked as such: + +``` + def circumference(c: Circle): Double = c.radius * math.Pi * 2 + +assert(circle.circumference == circumference(circle)) +``` + +## Operators + +The extension method syntax can also be used to define operators. Examples: + +```scala +extension (x: String) + def < (y: String): Boolean = ... +extension (x: Elem) + def +: (xs: Seq[Elem]): Seq[Elem] = ... +extension (x: Number) + infix def min (y: Number): Number = ... + +"ab" < "c" +1 +: List(2, 3) +x min 3 +``` + +The three definitions above translate to + +``` + def < (x: String)(y: String): Boolean = ... + def +: (xs: Seq[Elem])(x: Elem): Seq[Elem] = ... + infix def min(x: Number)(y: Number): Number = ... +``` + +Note the swap of the two parameters `x` and `xs` when translating +the right-associative operator `+:` to an extension method. This is analogous +to the implementation of right binding operators as normal methods. The Scala +compiler preprocesses an infix operation `x +: xs` to `xs.+:(x)`, so the extension +method ends up being applied to the sequence as first argument (in other words, the +two swaps cancel each other out). See [here for details](./right-associative-extension-methods.md). + +## Generic Extensions + +It is also possible to extend generic types by adding type parameters to an extension. For instance: + +```scala +extension [T](xs: List[T]) + def second = xs.tail.head + +extension [T: Numeric](x: T) + def + (y: T): T = summon[Numeric[T]].plus(x, y) +``` + +Type parameters on extensions can also be combined with type parameters on the methods +themselves: + +```scala +extension [T](xs: List[T]) + def sumBy[U: Numeric](f: T => U): U = ... +``` + +Type arguments matching method type parameters are passed as usual: + +```scala +List("a", "bb", "ccc").sumBy[Int](_.length) +``` + +By contrast, type arguments matching type parameters following `extension` can be passed +only if the method is referenced as a non-extension method: + +```scala +sumBy[String](List("a", "bb", "ccc"))(_.length) +``` + +Or, when passing both type arguments: + +```scala +sumBy[String](List("a", "bb", "ccc"))[Int](_.length) +``` + +Extensions can also take using clauses. For instance, the `+` extension above could equivalently be written with a using clause: + +```scala +extension [T](x: T)(using n: Numeric[T]) + def + (y: T): T = n.plus(x, y) +``` + +## Collective Extensions + +Sometimes, one wants to define several extension methods that share the same +left-hand parameter type. In this case one can "pull out" the common parameters into +a single extension and enclose all methods in braces or an indented region. +Example: + +```scala +extension (ss: Seq[String]) + + def longestStrings: Seq[String] = + val maxLength = ss.map(_.length).max + ss.filter(_.length == maxLength) + + def longestString: String = longestStrings.head +``` + +The same can be written with braces as follows (note that indented regions can still be used inside braces): + +```scala +extension (ss: Seq[String]) { + + def longestStrings: Seq[String] = { + val maxLength = ss.map(_.length).max + ss.filter(_.length == maxLength) + } + + def longestString: String = longestStrings.head +} +``` + +Note the right-hand side of `longestString`: it calls `longestStrings` directly, implicitly +assuming the common extended value `ss` as receiver. + +Collective extensions like these are a shorthand for individual extensions +where each method is defined separately. For instance, the first extension above expands to: + +```scala +extension (ss: Seq[String]) + def longestStrings: Seq[String] = + val maxLength = ss.map(_.length).max + ss.filter(_.length == maxLength) + +extension (ss: Seq[String]) + def longestString: String = ss.longestStrings.head +``` + +Collective extensions also can take type parameters and have using clauses. Example: + +```scala +extension [T](xs: List[T])(using Ordering[T]) + def smallest(n: Int): List[T] = xs.sorted.take(n) + def smallestIndices(n: Int): List[Int] = + val limit = smallest(n).max + xs.zipWithIndex.collect { case (x, i) if x <= limit => i } +``` + +## Translation of Calls to Extension Methods + +To convert a reference to an extension method, the compiler has to know about the extension +method. We say in this case that the extension method is _applicable_ at the point of reference. +There are four possible ways for an extension method to be applicable: + + 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference. + 2. The extension method is a member of some given + instance that is visible at the point of the reference. + 3. The reference is of the form `r.m` and the extension method + is defined in the implicit scope of the type of `r`. + 4. The reference is of the form `r.m` and the extension method + is defined in some given instance in the implicit scope of the type of `r`. + +Here is an example for the first rule: + +```scala +trait IntOps: + extension (i: Int) def isZero: Boolean = i == 0 + + extension (i: Int) def safeMod(x: Int): Option[Int] = + // extension method defined in same scope IntOps + if x.isZero then None + else Some(i % x) + +object IntOpsEx extends IntOps: + extension (i: Int) def safeDiv(x: Int): Option[Int] = + // extension method brought into scope via inheritance from IntOps + if x.isZero then None + else Some(i / x) + +trait SafeDiv: + import IntOpsEx.* // brings safeDiv and safeMod into scope + + extension (i: Int) def divide(d: Int): Option[(Int, Int)] = + // extension methods imported and thus in scope + (i.safeDiv(d), i.safeMod(d)) match + case (Some(d), Some(r)) => Some((d, r)) + case _ => None +``` + +By the second rule, an extension method can be made available by defining a given instance containing it, like this: + +```scala +given ops1: IntOps() // brings safeMod into scope + +1.safeMod(2) +``` + +By the third and fourth rule, an extension method is available if it is in the implicit scope of the receiver type or in a given instance in that scope. Example: + +```scala +class List[T]: + ... +object List: + ... + extension [T](xs: List[List[T]]) + def flatten: List[T] = xs.foldLeft(List.empty[T])(_ ++ _) + + given [T: Ordering]: Ordering[List[T]] with + extension (xs: List[T]) + def < (ys: List[T]): Boolean = ... +end List + +// extension method available since it is in the implicit scope +// of List[List[Int]] +List(List(1, 2), List(3, 4)).flatten + +// extension method available since it is in the given Ordering[List[T]], +// which is itself in the implicit scope of List[Int] +List(1, 2) < List(3) +``` + +The precise rules for resolving a selection to an extension method are as follows. + +Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional, and where `T` is the expected type. +The following two rewritings are tried in order: + + 1. The selection is rewritten to `m[Ts](e)`. + 2. If the first rewriting does not typecheck with expected type `T`, + and there is an extension method `m` in some eligible object `o`, the selection is rewritten to `o.m[Ts](e)`. An object `o` is _eligible_ if + + - `o` forms part of the implicit scope of `T`, or + - `o` is a given instance that is visible at the point of the application, or + - `o` is a given instance in the implicit scope of `T`. + + This second rewriting is attempted at the time where the compiler also tries an implicit conversion + from `T` to a type containing `m`. If there is more than one way of rewriting, an ambiguity error results. + +An extension method can also be referenced using a simple identifier without a preceding expression. If an identifier `g` appears in the body of an extension method `f` and refers to an extension method `g` that is defined in the same collective extension + +```scala +extension (x: T) + def f ... = ... g ... + def g ... +``` + +the identifier is rewritten to `x.g`. This is also the case if `f` and `g` are the same method. Example: + +```scala +extension (s: String) + def position(ch: Char, n: Int): Int = + if n < s.length && s(n) != ch then position(ch, n + 1) + else n +``` + +The recursive call `position(ch, n + 1)` expands to `s.position(ch, n + 1)` in this case. The whole extension method rewrites to + +```scala +def position(s: String)(ch: Char, n: Int): Int = + if n < s.length && s(n) != ch then position(s)(ch, n + 1) + else n +``` + +## Syntax + +Here are the syntax changes for extension methods and collective extensions relative +to the [current syntax](../syntax.md). + +``` +BlockStat ::= ... | Extension +TemplateStat ::= ... | Extension +TopStat ::= ... | Extension +Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} + ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods +ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> +ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef +``` + +In the above the notation `<<< ts >>>` in the production rule `ExtMethods` is defined as follows : + +``` +<<< ts >>> ::= ‘{’ ts ‘}’ | indent ts outdent +``` + +`extension` is a soft keyword. It is recognized as a keyword only if it appears +at the start of a statement and is followed by `[` or `(`. In all other cases +it is treated as an identifier. diff --git a/docs/_spec/TODOreference/contextual/given-imports.md b/docs/_spec/TODOreference/contextual/given-imports.md new file mode 100644 index 000000000000..6a55368979b1 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/given-imports.md @@ -0,0 +1,117 @@ +--- +layout: doc-page +title: "Importing Givens" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/given-imports.html +--- + +A special form of import wildcard selector is used to import given instances. Example: + +```scala +object A: + class TC + given tc: TC = ??? + def f(using TC) = ??? + +object B: + import A.* + import A.given + ... +``` + +In the code above, the `import A.*` clause in object `B` imports all members +of `A` _except_ the given instance `tc`. Conversely, the second import `import A.given` will import _only_ that given instance. +The two import clauses can also be merged into one: + +```scala +object B: + import A.{given, *} + ... +``` + +Generally, a normal wildcard selector `*` brings all definitions other than givens or extensions into scope +whereas a `given` selector brings all givens (including those resulting from extensions) into scope. + +There are two main benefits arising from these rules: + +- It is made clearer where givens in scope are coming from. + In particular, it is not possible to hide imported givens in a long list of regular wildcard imports. +- It enables importing all givens + without importing anything else. This is particularly important since givens + can be anonymous, so the usual recourse of using named imports is not + practical. + +## Importing By Type + +Since givens can be anonymous it is not always practical to import them by their name, and wildcard imports are typically used instead. By-type imports provide a more specific alternative to wildcard imports, which makes it clearer what is imported. Example: + +```scala +import A.given TC +``` + +This imports any given in `A` that has a type which conforms to `TC`. Importing givens of several types `T1,...,Tn` +is expressed by multiple `given` selectors. + +```scala +import A.{given T1, ..., given Tn} +``` + +Importing all given instances of a parameterized type is expressed by wildcard arguments. +For instance, assuming the object + +```scala +object Instances: + given intOrd: Ordering[Int] = ... + given listOrd[T: Ordering]: Ordering[List[T]] = ... + given ec: ExecutionContext = ... + given im: Monoid[Int] = ... +``` + +the import clause + +```scala +import Instances.{given Ordering[?], given ExecutionContext} +``` + +would import the `intOrd`, `listOrd`, and `ec` instances but leave out the `im` instance, since it fits none of the specified bounds. + +By-type imports can be mixed with by-name imports. If both are present in an import clause, by-type imports come last. For instance, the import clause + +```scala +import Instances.{im, given Ordering[?]} +``` + +would import `im`, `intOrd`, and `listOrd` but leave out `ec`. + +## Migration + +The rules for imports stated above have the consequence that a library +would have to migrate in lockstep with all its users from old style implicits and +normal imports to givens and given imports. + +The following modifications avoid this hurdle to migration. + + 1. A `given` import selector also brings old style implicits into scope. So, in Scala 3.0 + an old-style implicit definition can be brought into scope either by a `*` or a `given` wildcard selector. + + 2. In Scala 3.1, old-style implicits accessed through a `*` wildcard import will give a deprecation warning. + + 3. In some version after 3.1, old-style implicits accessed through a `*` wildcard import will give a compiler error. + +These rules mean that library users can use `given` selectors to access old-style implicits in Scala 3.0, +and will be gently nudged and then forced to do so in later versions. Libraries can then switch to +given instances once their user base has migrated. + +## Syntax + +``` +Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} +Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} +ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec +ImportSpec ::= NamedSelector + | WildcardSelector + | ‘{’ ImportSelectors) ‘}’ +NamedSelector ::= id [‘as’ (id | ‘_’)] +WildCardSelector ::= ‘*' | ‘given’ [InfixType] +ImportSelectors ::= NamedSelector [‘,’ ImportSelectors] + | WildCardSelector {‘,’ WildCardSelector} +``` diff --git a/docs/_spec/TODOreference/contextual/givens.md b/docs/_spec/TODOreference/contextual/givens.md new file mode 100644 index 000000000000..411d50ba63ea --- /dev/null +++ b/docs/_spec/TODOreference/contextual/givens.md @@ -0,0 +1,193 @@ +--- +layout: doc-page +title: "Given Instances" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/givens.html +--- + +Given instances (or, simply, "givens") define "canonical" values of certain types +that serve for synthesizing arguments to [context parameters](./using-clauses.md). Example: + +```scala +trait Ord[T]: + def compare(x: T, y: T): Int + extension (x: T) def < (y: T) = compare(x, y) < 0 + extension (x: T) def > (y: T) = compare(x, y) > 0 + +given intOrd: Ord[Int] with + def compare(x: Int, y: Int) = + if x < y then -1 else if x > y then +1 else 0 + +given listOrd[T](using ord: Ord[T]): Ord[List[T]] with + + def compare(xs: List[T], ys: List[T]): Int = (xs, ys) match + case (Nil, Nil) => 0 + case (Nil, _) => -1 + case (_, Nil) => +1 + case (x :: xs1, y :: ys1) => + val fst = ord.compare(x, y) + if fst != 0 then fst else compare(xs1, ys1) + +``` + +This code defines a trait `Ord` with two given instances. `intOrd` defines +a given for the type `Ord[Int]` whereas `listOrd[T]` defines givens +for `Ord[List[T]]` for all types `T` that come with a given instance for `Ord[T]` +themselves. The `using` clause in `listOrd` defines a condition: There must be a +given of type `Ord[T]` for a given of type `Ord[List[T]]` to exist. +Such conditions are expanded by the compiler to [context parameters](./using-clauses.md). + +## Anonymous Givens + +The name of a given can be left out. So the definitions +of the last section can also be expressed like this: + +```scala +given Ord[Int] with + ... +given [T](using Ord[T]): Ord[List[T]] with + ... +``` + +If the name of a given is missing, the compiler will synthesize a name from +the implemented type(s). + +**Note** The name synthesized by the compiler is chosen to be readable and reasonably concise. For instance, the two instances above would get the names: + +```scala +given_Ord_Int +given_Ord_List +``` + +The precise rules for synthesizing names are found [here](./relationship-implicits.html#anonymous-given-instances). These rules do not guarantee absence of name conflicts between +given instances of types that are "too similar". To avoid conflicts one can +use named instances. + +**Note** To ensure robust binary compatibility, publicly available libraries should prefer named instances. + +## Alias Givens + +An alias can be used to define a given instance that is equal to some expression. Example: + +```scala +given global: ExecutionContext = ForkJoinPool() +``` + +This creates a given `global` of type `ExecutionContext` that resolves to the right +hand side `ForkJoinPool()`. +The first time `global` is accessed, a new `ForkJoinPool` is created, which is then +returned for this and all subsequent accesses to `global`. This operation is thread-safe. + +Alias givens can be anonymous as well, e.g. + +```scala +given Position = enclosingTree.position +given (using config: Config): Factory = MemoizingFactory(config) +``` + +An alias given can have type parameters and context parameters just like any other given, +but it can only implement a single type. + +## Given Macros + +Given aliases can have the `inline` and `transparent` modifiers. +Example: + +```scala +transparent inline given mkAnnotations[A, T]: Annotations[A, T] = ${ + // code producing a value of a subtype of Annotations +} +``` + +Since `mkAnnotations` is `transparent`, the type of an application is the type of its right-hand side, which can be a proper subtype of the declared result type `Annotations[A, T]`. + +Given instances can have the `inline` but not `transparent` modifiers as their type is already known from the signature. +Example: + +```scala +trait Show[T] { + inline def show(x: T): String +} + +inline given Show[Foo] with { + /*transparent*/ inline def show(x: Foo): String = ${ ... } +} + +def app = + // inlines `show` method call and removes the call to `given Show[Foo]` + summon[Show[Foo]].show(foo) +``` +Note that the inline methods within the given instances may be `transparent`. + +The inlining of given instances will not inline/duplicate the implementation of the given, it will just inline the instantiation of that instance. +This is used to help dead code elimination of the given instances that are not used after inlining. + + +## Pattern-Bound Given Instances + +Given instances can also appear in patterns. Example: + +```scala +for given Context <- applicationContexts do + +pair match + case (ctx @ given Context, y) => ... +``` + +In the first fragment above, anonymous given instances for class `Context` are established by enumerating over `applicationContexts`. In the second fragment, a given `Context` +instance named `ctx` is established by matching against the first half of the `pair` selector. + +In each case, a pattern-bound given instance consists of `given` and a type `T`. The pattern matches exactly the same selectors as the type ascription pattern `_: T`. + +## Negated Givens + +Scala 2's somewhat puzzling behavior with respect to ambiguity has been exploited to implement the analogue of a "negated" search in implicit resolution, +where a query Q1 fails if some other query Q2 succeeds and Q1 succeeds if Q2 fails. With the new cleaned up behavior these techniques no longer work. +But the new special type [`scala.util.NotGiven`](https://scala-lang.org/api/3.x/scala/util/NotGiven.html) now implements negation directly. + +For any query type `Q`, [`NotGiven[Q]`](https://scala-lang.org/api/3.x/scala/util/NotGiven.html) succeeds if and only if the implicit +search for `Q` fails, for example: + +```scala +import scala.util.NotGiven + +trait Tagged[A] + +case class Foo[A](value: Boolean) +object Foo: + given fooTagged[A](using Tagged[A]): Foo[A] = Foo(true) + given fooNotTagged[A](using NotGiven[Tagged[A]]): Foo[A] = Foo(false) + +@main def test(): Unit = + given Tagged[Int]() + assert(summon[Foo[Int]].value) // fooTagged is found + assert(!summon[Foo[String]].value) // fooNotTagged is found +``` + +## Given Instance Initialization + +A given instance without type or context parameters is initialized on-demand, the first +time it is accessed. If a given has type or context parameters, a fresh instance +is created for each reference. + +## Syntax + +Here is the syntax for given instances: + +``` +TmplDef ::= ... + | ‘given’ GivenDef +GivenDef ::= [GivenSig] StructuralInstance + | [GivenSig] AnnotType ‘=’ Expr + | [GivenSig] AnnotType +GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ +StructuralInstance ::= ConstrApp {‘with’ ConstrApp} ‘with’ TemplateBody +``` + +A given instance starts with the reserved word `given` and an optional _signature_. The signature +defines a name and/or parameters for the instance. It is followed by `:`. There are three kinds +of given instances: + +- A _structural instance_ contains one or more types or constructor applications, + followed by `with` and a template body that contains member definitions of the instance. +- An _alias instance_ contains a type, followed by `=` and a right-hand side expression. +- An _abstract instance_ contains just the type, which is not followed by anything. diff --git a/docs/_spec/TODOreference/contextual/multiversal-equality.md b/docs/_spec/TODOreference/contextual/multiversal-equality.md new file mode 100644 index 000000000000..e9a81b95f472 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/multiversal-equality.md @@ -0,0 +1,227 @@ +--- +layout: doc-page +title: "Multiversal Equality" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/multiversal-equality.html +--- + +Previously, Scala had universal equality: Two values of any types +could be compared with each other with `==` and `!=`. This came from +the fact that `==` and `!=` are implemented in terms of Java's +`equals` method, which can also compare values of any two reference +types. + +Universal equality is convenient. But it is also dangerous since it +undermines type safety. For instance, let's assume one is left after some refactoring +with an erroneous program where a value `y` has type `S` instead of the correct type `T`. + +```scala +val x = ... // of type T +val y = ... // of type S, but should be T +x == y // typechecks, will always yield false +``` + +If `y` gets compared to other values of type `T`, +the program will still typecheck, since values of all types can be compared with each other. +But it will probably give unexpected results and fail at runtime. + +Multiversal equality is an opt-in way to make universal equality safer. +It uses a binary type class [`scala.CanEqual`](https://github.com/lampepfl/dotty/blob/main/library/src/scala/CanEqual.scala) +to indicate that values of two given types can be compared with each other. +The example above would not typecheck if `S` or `T` was a class +that derives `CanEqual`, e.g. + +```scala +class T derives CanEqual +``` + +Alternatively, one can also provide a `CanEqual` given instance directly, like this: + +```scala +given CanEqual[T, T] = CanEqual.derived +``` + +This definition effectively says that values of type `T` can (only) be +compared to other values of type `T` when using `==` or `!=`. The definition +affects type checking but it has no significance for runtime +behavior, since `==` always maps to `equals` and `!=` always maps to +the negation of `equals`. The right-hand side `CanEqual.derived` of the definition +is a value that has any `CanEqual` instance as its type. Here is the definition of class +`CanEqual` and its companion object: + +```scala +package scala +import annotation.implicitNotFound + +@implicitNotFound("Values of types ${L} and ${R} cannot be compared with == or !=") +sealed trait CanEqual[-L, -R] + +object CanEqual: + object derived extends CanEqual[Any, Any] +``` + +One can have several `CanEqual` given instances for a type. For example, the four +definitions below make values of type `A` and type `B` comparable with +each other, but not comparable to anything else: + +```scala +given CanEqual[A, A] = CanEqual.derived +given CanEqual[B, B] = CanEqual.derived +given CanEqual[A, B] = CanEqual.derived +given CanEqual[B, A] = CanEqual.derived +``` + +The [`scala.CanEqual`](https://github.com/lampepfl/dotty/blob/main/library/src/scala/CanEqual.scala) +object defines a number of `CanEqual` given instances that together +define a rule book for what standard types can be compared (more details below). + +There is also a "fallback" instance named `canEqualAny` that allows comparisons +over all types that do not themselves have a `CanEqual` given. `canEqualAny` is defined as follows: + +```scala +def canEqualAny[L, R]: CanEqual[L, R] = CanEqual.derived +``` + +Even though `canEqualAny` is not declared as `given`, the compiler will still +construct an `canEqualAny` instance as answer to an implicit search for the +type `CanEqual[L, R]`, unless `L` or `R` have `CanEqual` instances +defined on them, or the language feature `strictEquality` is enabled. + +The primary motivation for having `canEqualAny` is backwards compatibility. +If this is of no concern, one can disable `canEqualAny` by enabling the language +feature `strictEquality`. As for all language features this can be either +done with an import + +```scala +import scala.language.strictEquality +``` +or with a command line option `-language:strictEquality`. + +## Deriving CanEqual Instances + +Instead of defining `CanEqual` instances directly, it is often more convenient to derive them. Example: + +```scala +class Box[T](x: T) derives CanEqual +``` + +By the usual rules of [type class derivation](./derivation.md), +this generates the following `CanEqual` instance in the companion object of `Box`: + +```scala +given [T, U](using CanEqual[T, U]): CanEqual[Box[T], Box[U]] = + CanEqual.derived +``` + +That is, two boxes are comparable with `==` or `!=` if their elements are. Examples: + +```scala +new Box(1) == new Box(1L) // ok since there is an instance for `CanEqual[Int, Long]` +new Box(1) == new Box("a") // error: can't compare +new Box(1) == 1 // error: can't compare +``` + +## Precise Rules for Equality Checking + +The precise rules for equality checking are as follows. + +If the `strictEquality` feature is enabled then +a comparison using `x == y` or `x != y` between values `x: T` and `y: U` +is legal if there is a `given` of type `CanEqual[T, U]`. + +In the default case where the `strictEquality` feature is not enabled the comparison is +also legal if + + 1. `T` and `U` are the same, or + 2. one of `T`, `U` is a subtype of the _lifted_ version of the other type, or + 3. neither `T` nor `U` have a _reflexive_ `CanEqual` instance. + +Explanations: + + - _lifting_ a type `S` means replacing all references to abstract types + in covariant positions of `S` by their upper bound, and replacing + all refinement types in covariant positions of `S` by their parent. + - a type `T` has a _reflexive_ `CanEqual` instance if the implicit search for `CanEqual[T, T]` + succeeds. + +## Predefined CanEqual Instances + +The `CanEqual` object defines instances for comparing + - the primitive types `Byte`, `Short`, `Char`, `Int`, `Long`, `Float`, `Double`, `Boolean`, and `Unit`, + - `java.lang.Number`, `java.lang.Boolean`, and `java.lang.Character`, + - `scala.collection.Seq`, and `scala.collection.Set`. + +Instances are defined so that every one of these types has a _reflexive_ `CanEqual` instance, and the following holds: + + - Primitive numeric types can be compared with each other. + - Primitive numeric types can be compared with subtypes of `java.lang.Number` (and _vice versa_). + - `Boolean` can be compared with `java.lang.Boolean` (and _vice versa_). + - `Char` can be compared with `java.lang.Character` (and _vice versa_). + - Two sequences (of arbitrary subtypes of `scala.collection.Seq`) can be compared + with each other if their element types can be compared. The two sequence types + need not be the same. + - Two sets (of arbitrary subtypes of `scala.collection.Set`) can be compared + with each other if their element types can be compared. The two set types + need not be the same. + - Any subtype of `AnyRef` can be compared with `Null` (and _vice versa_). + +## Why Two Type Parameters? + +One particular feature of the `CanEqual` type is that it takes _two_ type parameters, representing the types of the two items to be compared. By contrast, conventional +implementations of an equality type class take only a single type parameter which represents the common type of _both_ operands. +One type parameter is simpler than two, so why go through the additional complication? The reason has to do with the fact that, rather than coming up with a type class where no operation existed before, +we are dealing with a refinement of pre-existing, universal equality. It is best illustrated through an example. + +Say you want to come up with a safe version of the `contains` method on `List[T]`. The original definition of `contains` in the standard library was: +```scala +class List[+T]: + ... + def contains(x: Any): Boolean +``` +That uses universal equality in an unsafe way since it permits arguments of any type to be compared with the list's elements. The "obvious" alternative definition +```scala + def contains(x: T): Boolean +``` +does not work, since it refers to the covariant parameter `T` in a nonvariant context. The only variance-correct way to use the type parameter `T` in `contains` is as a lower bound: +```scala + def contains[U >: T](x: U): Boolean +``` +This generic version of `contains` is the one used in the current (Scala 2.13) version of `List`. +It looks different but it admits exactly the same applications as the `contains(x: Any)` definition we started with. +However, we can make it more useful (i.e. restrictive) by adding a `CanEqual` parameter: +```scala + def contains[U >: T](x: U)(using CanEqual[T, U]): Boolean // (1) +``` +This version of `contains` is equality-safe! More precisely, given +`x: T`, `xs: List[T]` and `y: U`, then `xs.contains(y)` is type-correct if and only if +`x == y` is type-correct. + +Unfortunately, the crucial ability to "lift" equality type checking from simple equality and pattern matching to arbitrary user-defined operations gets lost if we restrict ourselves to an equality class with a single type parameter. Consider the following signature of `contains` with a hypothetical `CanEqual1[T]` type class: +```scala + def contains[U >: T](x: U)(using CanEqual1[U]): Boolean // (2) +``` +This version could be applied just as widely as the original `contains(x: Any)` method, +since the `CanEqual1[Any]` fallback is always available! So we have gained nothing. What got lost in the transition to a single parameter type class was the original rule that `CanEqual[A, B]` is available only if neither `A` nor `B` have a reflexive `CanEqual` instance. That rule simply cannot be expressed if there is a single type parameter for `CanEqual`. + +The situation is different under `-language:strictEquality`. In that case, +the `CanEqual[Any, Any]` or `CanEqual1[Any]` instances would never be available, and the +single and two-parameter versions would indeed coincide for most practical purposes. + +But assuming `-language:strictEquality` immediately and everywhere poses migration problems which might well be unsurmountable. Consider again `contains`, which is in the standard library. Parameterizing it with the `CanEqual` type class as in (1) is an immediate win since it rules out non-sensical applications while still allowing all sensible ones. +So it can be done almost at any time, modulo binary compatibility concerns. +On the other hand, parameterizing `contains` with `CanEqual1` as in (2) would make `contains` +unusable for all types that have not yet declared a `CanEqual1` instance, including all +types coming from Java. This is clearly unacceptable. It would lead to a situation where, +rather than migrating existing libraries to use safe equality, the only upgrade path is to have parallel libraries, with the new version only catering to types deriving `CanEqual1` and the old version dealing with everything else. Such a split of the ecosystem would be very problematic, which means the cure is likely to be worse than the disease. + +For these reasons, it looks like a two-parameter type class is the only way forward because it can take the existing ecosystem where it is and migrate it towards a future where more and more code uses safe equality. + +In applications where `-language:strictEquality` is the default one could also introduce a one-parameter type alias such as +```scala +type Eq[-T] = CanEqual[T, T] +``` +Operations needing safe equality could then use this alias instead of the two-parameter `CanEqual` class. But it would only +work under `-language:strictEquality`, since otherwise the universal `Eq[Any]` instance would be available everywhere. + + +More on multiversal equality is found in a [blog post](http://www.scala-lang.org/blog/2016/05/06/multiversal-equality.html) +and a [GitHub issue](https://github.com/lampepfl/dotty/issues/1247). diff --git a/docs/_spec/TODOreference/contextual/relationship-implicits.md b/docs/_spec/TODOreference/contextual/relationship-implicits.md new file mode 100644 index 000000000000..fce07f51151a --- /dev/null +++ b/docs/_spec/TODOreference/contextual/relationship-implicits.md @@ -0,0 +1,206 @@ +--- +layout: doc-page +title: "Relationship with Scala 2 Implicits" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/relationship-implicits.html +--- + +Many, but not all, of the new contextual abstraction features in Scala 3 can be mapped to Scala 2's implicits. This page gives a rundown on the relationships between new and old features. + +## Simulating Scala 3 Contextual Abstraction Concepts with Scala 2 Implicits + +### Given Instances + +Given instances can be mapped to combinations of implicit objects, classes and implicit methods. + + 1. Given instances without parameters are mapped to implicit objects. For instance, + + ```scala + given intOrd: Ord[Int] with { ... } + ``` + + maps to + + ```scala + implicit object intOrd extends Ord[Int] { ... } + ``` + + 2. Parameterized givens are mapped to combinations of classes and implicit methods. For instance, + + ```scala + given listOrd[T](using ord: Ord[T]): Ord[List[T]] with { ... } + ``` + + maps to + + ```scala + class listOrd[T](implicit ord: Ord[T]) extends Ord[List[T]] { ... } + final implicit def listOrd[T](implicit ord: Ord[T]): listOrd[T] = + new listOrd[T] + ``` + + 3. Alias givens map to implicit methods or implicit lazy vals. If an alias has neither type nor context parameters, + it is treated as a lazy val, unless the right-hand side is a simple reference, in which case we can use a forwarder to + that reference without caching it. + +Examples: + +```scala +given global: ExecutionContext = new ForkJoinContext() + +val ctx: Context +given Context = ctx +``` + +would map to + +```scala +final implicit lazy val global: ExecutionContext = new ForkJoinContext() +final implicit def given_Context = ctx +``` + +### Anonymous Given Instances + +Anonymous given instances get compiler synthesized names, which are generated in a reproducible way from the implemented type(s). For example, if the names of the `IntOrd` and `ListOrd` givens above were left out, the following names would be synthesized instead: + +```scala +given given_Ord_Int: Ord[Int] with { ... } +given given_Ord_List[T](using ord: Ord[T]): Ord[List[T]] with { ... } +``` + +The synthesized type names are formed from + +1. the prefix `given_`, +2. the simple name(s) of the implemented type(s), leaving out any prefixes, +3. the simple name(s) of the top-level argument type constructors to these types. + +Tuples are treated as transparent, i.e. a type `F[(X, Y)]` would get the synthesized name +`F_X_Y`. Directly implemented function types `A => B` are represented as `A_to_B`. Function types used as arguments to other type constructors are represented as `Function`. + +### Using Clauses + +Using clauses correspond largely to Scala 2's implicit parameter clauses. E.g. + +```scala +def max[T](x: T, y: T)(using ord: Ord[T]): T +``` + +would be written + +```scala +def max[T](x: T, y: T)(implicit ord: Ord[T]): T +``` + +in Scala 2. The main difference concerns applications of such parameters. +Explicit arguments to parameters of using clauses _must_ be written using `(using ...)`, +mirroring the definition syntax. E.g, `max(2, 3)(using IntOrd)`. +Scala 2 uses normal applications `max(2, 3)(IntOrd)` instead. The Scala 2 syntax has some inherent ambiguities and restrictions which are overcome by the new syntax. For instance, multiple implicit parameter lists are not available in the old syntax, even though they can be simulated using auxiliary objects in the "Aux" pattern. + +The `summon` method corresponds to `implicitly` in Scala 2. +It is precisely the same as the `the` method in [Shapeless](https://github.com/milessabin/shapeless). +The difference between `summon` (or `the`) and `implicitly` is +that `summon` can return a more precise type than the type that was +asked for. + +### Context Bounds + +Context bounds are the same in both language versions. They expand to the respective forms of implicit parameters. + +**Note:** To ease migration, context bounds in Scala 3 map for a limited time to old-style implicit parameters for which arguments can be passed either in a using clause or +in a normal argument list. Once old-style implicits are deprecated, context bounds +will map to using clauses instead. + +### Extension Methods + +Extension methods have no direct counterpart in Scala 2, but they can be simulated with implicit classes. For instance, the extension method + +```scala +extension (c: Circle) + def circumference: Double = c.radius * math.Pi * 2 +``` + +could be simulated to some degree by + +```scala +implicit class CircleDecorator(c: Circle) extends AnyVal { + def circumference: Double = c.radius * math.Pi * 2 +} +``` + +Abstract extension methods in traits that are implemented in given instances have no direct counterpart in Scala 2. The only way to simulate these is to make implicit classes available through imports. The Simulacrum macro library can automate this process in some cases. + +### Type Class Derivation + +Type class derivation has no direct counterpart in the Scala 2 language. Comparable functionality can be achieved by macro-based libraries such as [Shapeless](https://github.com/milessabin/shapeless), [Magnolia](https://propensive.com/opensource/magnolia), or [scalaz-deriving](https://github.com/scalaz/scalaz-deriving). + +### Context Function Types + +Context function types have no analogue in Scala 2. + +### Implicit By-Name Parameters + +Implicit by-name parameters are not supported in Scala 2, but can be emulated to some degree by the `Lazy` type in Shapeless. + +## Simulating Scala 2 Implicits in Scala 3 + +### Implicit Conversions + +Implicit conversion methods in Scala 2 can be expressed as given instances of the `scala.Conversion` class in Scala 3. For instance, instead of + +```scala +implicit def stringToToken(str: String): Token = new Keyword(str) +``` + +one can write + +```scala +given stringToToken: Conversion[String, Token] with + def apply(str: String): Token = KeyWord(str) +``` + +or + +```scala +given stringToToken: Conversion[String, Token] = KeyWord(_) +``` + +### Implicit Classes + +Implicit classes in Scala 2 are often used to define extension methods, which are directly supported in Scala 3. Other uses of implicit classes can be simulated by a pair of a regular class and a given `Conversion` instance. + +### Implicit Values + +Implicit `val` definitions in Scala 2 can be expressed in Scala 3 using a regular `val` definition and an alias given. +For instance, Scala 2's + +```scala +lazy implicit val pos: Position = tree.sourcePos +``` + +can be expressed in Scala 3 as + +```scala +lazy val pos: Position = tree.sourcePos +given Position = pos +``` + +### Abstract Implicits + +An abstract implicit `val` or `def` in Scala 2 can be expressed in Scala 3 using a regular abstract definition and an alias given. For instance, Scala 2's + +```scala +implicit def symDecorator: SymDecorator +``` + +can be expressed in Scala 3 as + +```scala +def symDecorator: SymDecorator +given SymDecorator = symDecorator +``` + +## Implementation Status and Timeline + +The Scala 3 implementation implements both Scala 2's implicits and the new abstractions. In fact, support for Scala 2's implicits is an essential part of the common language subset between 2.13 and Scala 3. +Migration to the new abstractions will be supported by making automatic rewritings available. + +Depending on adoption patterns, old style implicits might start to be deprecated in a version following Scala 3.0. diff --git a/docs/_spec/TODOreference/contextual/right-associative-extension-methods.md b/docs/_spec/TODOreference/contextual/right-associative-extension-methods.md new file mode 100644 index 000000000000..068123df8cd2 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/right-associative-extension-methods.md @@ -0,0 +1,52 @@ +--- +layout: doc-page +title: "Right-Associative Extension Methods: Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/right-associative-extension-methods.html +--- + +The most general form of leading parameters of an extension method is as follows: + + - A possibly empty list of using clauses `leadingUsing` + - A single parameter `extensionParam` + - A possibly empty list of using clauses `trailingUsing` + +This is then followed by `def`, the method name, and possibly further parameters +`otherParams`. An example is: + +```scala + extension (using a: A, b: B)(using c: C) // <-- leadingUsing + (x: X) // <-- extensionParam + (using d: D) // <-- trailingUsing + def +:: (y: Y)(using e: E)(z: Z) // <-- otherParams +``` + +An extension method is treated as a right-associative operator +(as in [SLS §6.12.3](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#infix-operations)) +if it has a name ending in `:` and is immediately followed by a +single parameter. In the example above, that parameter is `(y: Y)`. + +The Scala compiler pre-processes a right-associative infix operation such as `x +: xs` +to `xs.+:(x)` if `x` is a pure expression or a call-by-name parameter and to `val y = x; xs.+:(y)` otherwise. This is necessary since a regular right-associative infix method +is defined in the class of its right operand. To make up for this swap, +the expansion of right-associative extension methods performs an analogous parameter swap. More precisely, if `otherParams` consists of a single parameter +`rightParam` followed by `remaining`, the total parameter sequence +of the extension method's expansion is: + +``` + leadingUsing rightParam trailingUsing extensionParam remaining +``` + +For instance, the `+::` method above would become + +```scala + def +:: (using a: A, b: B)(using c: C) + (y: Y) + (using d: D) + (x: X) + (using e: E)(z: Z) +``` + +This expansion has to be kept in mind when writing right-associative extension +methods with inter-parameter dependencies. + +An overall simpler design could be obtained if right-associative operators could _only_ be defined as extension methods, and would be disallowed as normal methods. In that case neither arguments nor parameters would have to be swapped. Future versions of Scala should strive to achieve this simplification. diff --git a/docs/_spec/TODOreference/contextual/type-classes.md b/docs/_spec/TODOreference/contextual/type-classes.md new file mode 100644 index 000000000000..9fc0d2eec864 --- /dev/null +++ b/docs/_spec/TODOreference/contextual/type-classes.md @@ -0,0 +1,282 @@ +--- +layout: doc-page +title: "Implementing Type classes" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/type-classes.html +--- + +A _type class_ is an abstract, parameterized type that lets you add new behavior to any closed data type without using sub-typing. This can be useful in multiple use-cases, for example: + +* expressing how a type you don't own (from the standard or 3rd-party library) conforms to such behavior +* expressing such a behavior for multiple types without involving sub-typing relationships (one `extends` another) between those types (see: [ad hoc polymorphism](https://en.wikipedia.org/wiki/Ad_hoc_polymorphism) for instance) + +Therefore in Scala 3, _type classes_ are just _traits_ with one or more parameters whose implementations are not defined through the `extends` keyword, but by **given instances**. +Here are some examples of common type classes: + +## Semigroups and monoids + +Here's the `Monoid` type class definition: + +```scala +trait SemiGroup[T]: + extension (x: T) def combine (y: T): T + +trait Monoid[T] extends SemiGroup[T]: + def unit: T +``` + +An implementation of this `Monoid` type class for the type `String` can be the following: + +```scala +given Monoid[String] with + extension (x: String) def combine (y: String): String = x.concat(y) + def unit: String = "" +``` + +Whereas for the type `Int` one could write the following: + +```scala +given Monoid[Int] with + extension (x: Int) def combine (y: Int): Int = x + y + def unit: Int = 0 +``` + +This monoid can now be used as _context bound_ in the following `combineAll` method: + +```scala +def combineAll[T: Monoid](xs: List[T]): T = + xs.foldLeft(summon[Monoid[T]].unit)(_.combine(_)) +``` + +To get rid of the `summon[...]` we can define a `Monoid` object as follows: + +```scala +object Monoid: + def apply[T](using m: Monoid[T]) = m +``` + +Which would allow to re-write the `combineAll` method this way: + +```scala +def combineAll[T: Monoid](xs: List[T]): T = + xs.foldLeft(Monoid[T].unit)(_.combine(_)) +``` + +## Functors + +A `Functor` for a type provides the ability for its values to be "mapped over", i.e. apply a function that transforms inside a value while remembering its shape. For example, to modify every element of a collection without dropping or adding elements. +We can represent all types that can be "mapped over" with `F`. It's a type constructor: the type of its values becomes concrete when provided a type argument. +Therefore we write it `F[_]`, hinting that the type `F` takes another type as argument. +The definition of a generic `Functor` would thus be written as: + +```scala +trait Functor[F[_]]: + def map[A, B](x: F[A], f: A => B): F[B] +``` + +Which could read as follows: "A `Functor` for the type constructor `F[_]` represents the ability to transform `F[A]` to `F[B]` through the application of function `f` with type `A => B`". We call the `Functor` definition here a _type class_. +This way, we could define an instance of `Functor` for the `List` type: + +```scala +given Functor[List] with + def map[A, B](x: List[A], f: A => B): List[B] = + x.map(f) // List already has a `map` method +``` + +With this `given` instance in scope, everywhere a `Functor` is expected, the compiler will accept a `List` to be used. + +For instance, we may write such a testing method: + +```scala +def assertTransformation[F[_]: Functor, A, B](expected: F[B], original: F[A], mapping: A => B): Unit = + assert(expected == summon[Functor[F]].map(original, mapping)) +``` + +And use it this way, for example: + +```scala +assertTransformation(List("a1", "b1"), List("a", "b"), elt => s"${elt}1") +``` + +That's a first step, but in practice we probably would like the `map` function to be a method directly accessible on the type `F`. So that we can call `map` directly on instances of `F`, and get rid of the `summon[Functor[F]]` part. +As in the previous example of Monoids, [`extension` methods](extension-methods.md) help achieving that. Let's re-define the `Functor` type class with extension methods. + +```scala +trait Functor[F[_]]: + extension [A](x: F[A]) + def map[B](f: A => B): F[B] +``` + +The instance of `Functor` for `List` now becomes: + +```scala +given Functor[List] with + extension [A](xs: List[A]) + def map[B](f: A => B): List[B] = + xs.map(f) // List already has a `map` method + +``` + +It simplifies the `assertTransformation` method: + +```scala +def assertTransformation[F[_]: Functor, A, B](expected: F[B], original: F[A], mapping: A => B): Unit = + assert(expected == original.map(mapping)) +``` + +The `map` method is now directly used on `original`. It is available as an extension method +since `original`'s type is `F[A]` and a given instance for `Functor[F[A]]` which defines `map` +is in scope. + +## Monads + +Applying `map` in `Functor[List]` to a mapping function of type `A => B` results in a `List[B]`. So applying it to a mapping function of type `A => List[B]` results in a `List[List[B]]`. To avoid managing lists of lists, we may want to "flatten" the values in a single list. + +That's where `Monad` comes in. A `Monad` for type `F[_]` is a `Functor[F]` with two more operations: + +* `flatMap`, which turns an `F[A]` into an `F[B]` when given a function of type `A => F[B]`, +* `pure`, which creates an `F[A]` from a single value `A`. + +Here is the translation of this definition in Scala 3: + +```scala +trait Monad[F[_]] extends Functor[F]: + + /** The unit value for a monad */ + def pure[A](x: A): F[A] + + extension [A](x: F[A]) + /** The fundamental composition operation */ + def flatMap[B](f: A => F[B]): F[B] + + /** The `map` operation can now be defined in terms of `flatMap` */ + def map[B](f: A => B) = x.flatMap(f.andThen(pure)) + +end Monad +``` + +### List + +A `List` can be turned into a monad via this `given` instance: + +```scala +given listMonad: Monad[List] with + def pure[A](x: A): List[A] = + List(x) + extension [A](xs: List[A]) + def flatMap[B](f: A => List[B]): List[B] = + xs.flatMap(f) // rely on the existing `flatMap` method of `List` +``` + +Since `Monad` is a subtype of `Functor`, `List` is also a functor. The Functor's `map` +operation is already provided by the `Monad` trait, so the instance does not need to define +it explicitly. + +### Option + +`Option` is an other type having the same kind of behaviour: + +```scala +given optionMonad: Monad[Option] with + def pure[A](x: A): Option[A] = + Option(x) + extension [A](xo: Option[A]) + def flatMap[B](f: A => Option[B]): Option[B] = xo match + case Some(x) => f(x) + case None => None +``` + +### Reader + +Another example of a `Monad` is the _Reader_ Monad, which acts on functions instead of +data types like `List` or `Option`. It can be used to combine multiple functions +that all need the same parameter. For instance multiple functions needing access to some configuration, context, environment variables, etc. + +Let's define a `Config` type, and two functions using it: + +```scala +trait Config +// ... +def compute(i: Int)(config: Config): String = ??? +def show(str: String)(config: Config): Unit = ??? +``` + +We may want to combine `compute` and `show` into a single function, accepting a `Config` as parameter, and showing the result of the computation, and we'd like to use +a monad to avoid passing the parameter explicitly multiple times. So postulating +the right `flatMap` operation, we could write: + +```scala +def computeAndShow(i: Int): Config => Unit = compute(i).flatMap(show) +``` + +instead of + +```scala +show(compute(i)(config))(config) +``` + +Let's define this m then. First, we are going to define a type named `ConfigDependent` representing a function that when passed a `Config` produces a `Result`. + +```scala +type ConfigDependent[Result] = Config => Result +``` + +The monad instance will look like this: + +```scala +given configDependentMonad: Monad[ConfigDependent] with + + def pure[A](x: A): ConfigDependent[A] = + config => x + + extension [A](x: ConfigDependent[A]) + def flatMap[B](f: A => ConfigDependent[B]): ConfigDependent[B] = + config => f(x(config))(config) + +end configDependentMonad +``` + +The type `ConfigDependent` can be written using [type lambdas](../new-types/type-lambdas.md): + +```scala +type ConfigDependent = [Result] =>> Config => Result +``` + +Using this syntax would turn the previous `configDependentMonad` into: + +```scala +given configDependentMonad: Monad[[Result] =>> Config => Result] with + + def pure[A](x: A): Config => A = + config => x + + extension [A](x: Config => A) + def flatMap[B](f: A => Config => B): Config => B = + config => f(x(config))(config) + +end configDependentMonad +``` + +It is likely that we would like to use this pattern with other kinds of environments than our `Config` trait. The Reader monad allows us to abstract away `Config` as a type _parameter_, named `Ctx` in the following definition: + +```scala +given readerMonad[Ctx]: Monad[[X] =>> Ctx => X] with + + def pure[A](x: A): Ctx => A = + ctx => x + + extension [A](x: Ctx => A) + def flatMap[B](f: A => Ctx => B): Ctx => B = + ctx => f(x(ctx))(ctx) + +end readerMonad +``` + +## Summary + +The definition of a _type class_ is expressed with a parameterised type with abstract members, such as a `trait`. +The main difference between subtype polymorphism and ad-hoc polymorphism with _type classes_ is how the definition of the _type class_ is implemented, in relation to the type it acts upon. +In the case of a _type class_, its implementation for a concrete type is expressed through a `given` instance definition, which is supplied as an implicit argument alongside the value it acts upon. With subtype polymorphism, the implementation is mixed into the parents of a class, and only a single term is required to perform a polymorphic operation. The type class solution +takes more effort to set up, but is more extensible: Adding a new interface to a +class requires changing the source code of that class. But contrast, instances for type classes can be defined anywhere. + +To conclude, we have seen that traits and given instances, combined with other constructs like extension methods, context bounds and type lambdas allow a concise and natural expression of _type classes_. diff --git a/docs/_spec/TODOreference/contextual/using-clauses.md b/docs/_spec/TODOreference/contextual/using-clauses.md new file mode 100644 index 000000000000..9187e1916e7d --- /dev/null +++ b/docs/_spec/TODOreference/contextual/using-clauses.md @@ -0,0 +1,153 @@ +--- +layout: doc-page +title: "Using Clauses" +nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/using-clauses.html +--- + +Functional programming tends to express most dependencies as simple function parameterization. +This is clean and powerful, but it sometimes leads to functions that take many parameters where the same value is passed over and over again in long call chains to many +functions. Context parameters can help here since they enable the compiler to synthesize +repetitive arguments instead of the programmer having to write them explicitly. + +For example, with the [given instances](./givens.md) defined previously, +a `max` function that works for any arguments for which an ordering exists can be defined as follows: + +```scala +def max[T](x: T, y: T)(using ord: Ord[T]): T = + if ord.compare(x, y) < 0 then y else x +``` + +Here, `ord` is a _context parameter_ introduced with a `using` clause. +The `max` function can be applied as follows: + +```scala +max(2, 3)(using intOrd) +``` + +The `(using intOrd)` part passes `intOrd` as an argument for the `ord` parameter. But the point of context parameters is that this argument can also be left out (and it usually is). So the following applications are equally valid: + +```scala +max(2, 3) +max(List(1, 2, 3), Nil) +``` + +## Anonymous Context Parameters + +In many situations, the name of a context parameter need not be +mentioned explicitly at all, since it is used only in synthesized arguments for +other context parameters. In that case one can avoid defining a parameter name +and just provide its type. Example: + +```scala +def maximum[T](xs: List[T])(using Ord[T]): T = + xs.reduceLeft(max) +``` + +`maximum` takes a context parameter of type `Ord[T]` only to pass it on as an +inferred argument to `max`. The name of the parameter is left out. + +Generally, context parameters may be defined either as a full parameter list `(p_1: T_1, ..., p_n: T_n)` or just as a sequence of types `T_1, ..., T_n`. Vararg parameters are not supported in `using` clauses. + +## Class Context Parameters + +If a class context parameter is made a member by adding a `val` or `var` modifier, +then that member is available as a given instance. + +Compare the following examples, where the attempt to supply an explicit `given` member induces an ambiguity: + +```scala +class GivenIntBox(using val givenInt: Int): + def n = summon[Int] + +class GivenIntBox2(using givenInt: Int): + given Int = givenInt + //def n = summon[Int] // ambiguous +``` + +The `given` member is importable as explained in the section on [importing `given`s](./given-imports.md): + +```scala +val b = GivenIntBox(using 23) +import b.given +summon[Int] // 23 + +import b.* +//givenInt // Not found +``` + +## Inferring Complex Arguments + +Here are two other methods that have a context parameter of type `Ord[T]`: + +```scala +def descending[T](using asc: Ord[T]): Ord[T] = new Ord[T]: + def compare(x: T, y: T) = asc.compare(y, x) + +def minimum[T](xs: List[T])(using Ord[T]) = + maximum(xs)(using descending) +``` + +The `minimum` method's right-hand side passes `descending` as an explicit argument to `maximum(xs)`. +With this setup, the following calls are all well-formed, and they all normalize to the last one: + +```scala +minimum(xs) +maximum(xs)(using descending) +maximum(xs)(using descending(using listOrd)) +maximum(xs)(using descending(using listOrd(using intOrd))) +``` + +## Multiple `using` Clauses + +There can be several `using` clauses in a definition and `using` clauses can be freely mixed with normal parameter clauses. Example: + +```scala +def f(u: Universe)(using ctx: u.Context)(using s: ctx.Symbol, k: ctx.Kind) = ... +``` + +Multiple `using` clauses are matched left-to-right in applications. Example: + +```scala +object global extends Universe { type Context = ... } +given ctx : global.Context with { type Symbol = ...; type Kind = ... } +given sym : ctx.Symbol +given kind: ctx.Kind + +``` +Then the following calls are all valid (and normalize to the last one) + +```scala +f(global) +f(global)(using ctx) +f(global)(using ctx)(using sym, kind) +``` + +But `f(global)(using sym, kind)` would give a type error. + + +## Summoning Instances + +The method `summon` in `Predef` returns the given of a specific type. For example, +the given instance for `Ord[List[Int]]` is produced by + +```scala +summon[Ord[List[Int]]] // reduces to listOrd(using intOrd) +``` + +The `summon` method is simply defined as the (non-widening) identity function over a context parameter. + +```scala +def summon[T](using x: T): x.type = x +``` + +## Syntax + +Here is the new syntax of parameters and arguments seen as a delta from the [standard context free syntax of Scala 3](../syntax.md). `using` is a soft keyword, recognized only at the start of a parameter or argument list. It can be used as a normal identifier everywhere else. + +``` +ClsParamClause ::= ... | UsingClsParamClause +DefParamClauses ::= ... | UsingParamClause +UsingClsParamClause ::= ‘(’ ‘using’ (ClsParams | Types) ‘)’ +UsingParamClause ::= ‘(’ ‘using’ (DefParams | Types) ‘)’ +ParArgumentExprs ::= ... | ‘(’ ‘using’ ExprsInParens ‘)’ +``` diff --git a/docs/_spec/TODOreference/dropped-features/auto-apply.md b/docs/_spec/TODOreference/dropped-features/auto-apply.md new file mode 100644 index 000000000000..eadfe2f429ea --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/auto-apply.md @@ -0,0 +1,96 @@ +--- +layout: doc-page +title: "Dropped: Auto-Application" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/auto-apply.html +--- + +Previously an empty argument list `()` was implicitly inserted when +calling a nullary method without arguments. Example: + +```scala +def next(): T = ... +next // is expanded to next() +``` + +In Scala 3, this idiom is an error. + +```scala +next +^ +missing arguments for method next +``` + +In Scala 3, the application syntax has to follow exactly the parameter +syntax. Excluded from this rule are methods that are defined in Java +or that override methods defined in Java. The reason for being more +lenient with such methods is that otherwise everyone would have to +write + +```scala +xs.toString().length() +``` + +instead of + +```scala +xs.toString.length +``` + +The latter is idiomatic Scala because it conforms to the _uniform +access principle_. This principle states that one should be able to +change an object member from a field to a non-side-effecting method +and back without affecting clients that access the +member. Consequently, Scala encourages to define such "property" +methods without a `()` parameter list whereas side-effecting methods +should be defined with it. Methods defined in Java cannot make this +distinction; for them a `()` is always mandatory. So Scala fixes the +problem on the client side, by allowing the parameterless references. +But where Scala allows that freedom for all method references, Scala 3 +restricts it to references of external methods that are not defined +themselves in Scala 3. + +For reasons of backwards compatibility, Scala 3 for the moment also +auto-inserts `()` for nullary methods that are defined in Scala 2, or +that override a method defined in Scala 2. It turns out that, because +the correspondence between definition and call was not enforced in +Scala so far, there are quite a few method definitions in Scala 2 +libraries that use `()` in an inconsistent way. For instance, we +find in `scala.math.Numeric` + +```scala +def toInt(): Int +``` + +whereas `toInt` is written without parameters everywhere +else. Enforcing strict parameter correspondence for references to +such methods would project the inconsistencies to client code, which +is undesirable. So Scala 3 opts for more leniency when type-checking +references to such methods until most core libraries in Scala 2 have +been cleaned up. + +Stricter conformance rules also apply to overriding of nullary +methods. It is no longer allowed to override a parameterless method +by a nullary method or _vice versa_. Instead, both methods must agree +exactly in their parameter lists. + +```scala +class A: + def next(): Int + +class B extends A: + def next: Int // overriding error: incompatible type +``` + +Methods overriding Java or Scala 2 methods are again exempted from this +requirement. + +## Migrating code + +Existing Scala code with inconsistent parameters can still be compiled +in Scala 3 under `-source 3.0-migration`. When paired with the `-rewrite` +option, the code will be automatically rewritten to conform to Scala 3's +stricter checking. + +## Reference + +For more information, see [Issue #2570](https://github.com/lampepfl/dotty/issues/2570) and [PR #2716](https://github.com/lampepfl/dotty/pull/2716). diff --git a/docs/_spec/TODOreference/dropped-features/class-shadowing-spec.md b/docs/_spec/TODOreference/dropped-features/class-shadowing-spec.md new file mode 100644 index 000000000000..6c81087408c6 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/class-shadowing-spec.md @@ -0,0 +1,26 @@ +--- +layout: doc-page +title: "Dropped: Class Shadowing - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/class-shadowing-spec.html +--- + +Spec diff: in section [5.1.4 Overriding](https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html#Overriding), add *M' must not be a class*. + +> Why do we want to make this change to the language? + +Class shadowing is irregular compared to other types of overrides. Indeed, inner classes are not actually overridden but simply shadowed. + + +> How much existing code is going to be affected? + +From all the code compiled so far with Scala 3 the only instance of this I could find is in the stdlib. Looking at [this commit](https://github.com/lampepfl/scala/commit/68f13bf39979b631ed211ec1751934306ceb5d6c#diff-7aa508b70e055b47c823764e3e5646b8) it seems like the usage of class shadowing was accidental. + + +> How exactly is existing code going to be affected? + +Code that relies on overridden inner classes will stop compiling. + + +> Is this change going to be migratable automatically? + +No. diff --git a/docs/_spec/TODOreference/dropped-features/class-shadowing.md b/docs/_spec/TODOreference/dropped-features/class-shadowing.md new file mode 100644 index 000000000000..8bfdb1eb196c --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/class-shadowing.md @@ -0,0 +1,33 @@ +--- +layout: doc-page +title: "Dropped: Class Shadowing" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/class-shadowing.html +--- + +Scala 2 so far allowed patterns like this: + +```scala +class Base { + class Ops { ... } +} + +class Sub extends Base { + class Ops { ... } +} +``` + +Scala 3 rejects this with the error message: + +```scala +6 | class Ops { } + | ^ + |class Ops cannot have the same name as class Ops in class Base + | -- class definitions cannot be overridden +``` + +The issue is that the two `Ops` classes _look_ like one overrides the +other, but classes in Scala 2 cannot be overridden. To keep things clean +(and its internal operations consistent) the Scala 3 compiler forces you +to rename the inner classes so that their names are different. + +[More details](./class-shadowing-spec.md) diff --git a/docs/_spec/TODOreference/dropped-features/delayed-init.md b/docs/_spec/TODOreference/dropped-features/delayed-init.md new file mode 100644 index 000000000000..5d4f614ce951 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/delayed-init.md @@ -0,0 +1,32 @@ +--- +layout: doc-page +title: "Dropped: DelayedInit" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/delayed-init.html +--- + +The special handling of the [`DelayedInit`](https://scala-lang.org/api/3.x/scala/DelayedInit.html) +trait is no longer supported. + +One consequence is that the [`App`](https://scala-lang.org/api/3.x/scala/App.html) class, +which used [`DelayedInit`](https://scala-lang.org/api/3.x/scala/DelayedInit.html) is +now partially broken. You can still use `App` as a simple way to set up a main program. Example: + +```scala +object HelloWorld extends App { + println("Hello, world!") +} +``` + +However, the code is now run in the initializer of the object, which on +some JVM's means that it will only be interpreted. So, better not use it +for benchmarking! Also, if you want to access the command line arguments, +you need to use an explicit `main` method for that. + +```scala +object Hello: + def main(args: Array[String]) = + println(s"Hello, ${args(0)}") +``` + +On the other hand, Scala 3 offers a convenient alternative to such "program" objects +with [`@main` methods](../changed-features/main-functions.md). diff --git a/docs/_spec/TODOreference/dropped-features/do-while.md b/docs/_spec/TODOreference/dropped-features/do-while.md new file mode 100644 index 000000000000..08a730b8b5a7 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/do-while.md @@ -0,0 +1,41 @@ +--- +layout: doc-page +title: "Dropped: Do-While" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/do-while.html +--- + +The syntax construct +```scala +do while +``` +is no longer supported. Instead, it is recommended to use the equivalent `while` loop +below: +```scala +while ({ ; }) () +``` +For instance, instead of +```scala +do + i += 1 +while (f(i) == 0) +``` +one writes +```scala +while + i += 1 + f(i) == 0 +do () +``` +The idea to use a block as the condition of a while also gives a solution +to the "loop-and-a-half" problem. Here is another example: +```scala +while + val x: Int = iterator.next + x >= 0 +do print(".") +``` + +## Why Drop The Construct? + + - `do-while` is used relatively rarely and it can be expressed faithfully using just `while`. So there seems to be little point in having it as a separate syntax construct. + - Under the [new syntax rules](../other-new-features/control-syntax.md) `do` is used as a statement continuation, which would clash with its meaning as a statement introduction. diff --git a/docs/_spec/TODOreference/dropped-features/dropped-features.md b/docs/_spec/TODOreference/dropped-features/dropped-features.md new file mode 100644 index 000000000000..f6a13d9fa5da --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/dropped-features.md @@ -0,0 +1,7 @@ +--- +layout: index +title: "Dropped Features" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features.html +--- + +The following pages document the features of Scala 2 that have been dropped in Scala 3. diff --git a/docs/_spec/TODOreference/dropped-features/early-initializers.md b/docs/_spec/TODOreference/dropped-features/early-initializers.md new file mode 100644 index 000000000000..6f7c59c4f031 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/early-initializers.md @@ -0,0 +1,16 @@ +--- +layout: doc-page +title: "Dropped: Early Initializers" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/early-initializers.html +--- + +Early initializers of the form + +```scala +class C extends { ... } with SuperClass ... +``` + +have been dropped. They were rarely used, and mostly to compensate for the lack of +[trait parameters](../other-new-features/trait-parameters.md), which are now directly supported in Scala 3. + +For more information, see [SLS §5.1.6](https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html#early-definitions). diff --git a/docs/_spec/TODOreference/dropped-features/existential-types.md b/docs/_spec/TODOreference/dropped-features/existential-types.md new file mode 100644 index 000000000000..6ef815152cd0 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/existential-types.md @@ -0,0 +1,35 @@ +--- +layout: doc-page +title: "Dropped: Existential Types" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/existential-types.html +--- + +Existential types using `forSome` (as in +[SLS §3.2.12](https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#existential-types)) +have been dropped. The reasons for dropping them are: + + - Existential types violate a type soundness principle on which DOT + and Scala 3 are constructed. That principle says that every + prefix (`p`, respectvely `S`) of a type selection `p.T` or `S#T` + must either come from a value constructed at runtime or refer to a + type that is known to have only good bounds. + + - Existential types create many difficult feature interactions + with other Scala constructs. + + - Existential types largely overlap with path-dependent types, + so the gain of having them is relatively minor. + +Existential types that can be expressed using only wildcards (but not +`forSome`) are still supported, but are treated as refined types. +For instance, the type +```scala +Map[_ <: AnyRef, Int] +``` +is treated as the type `Map`, where the first type parameter +is upper-bounded by `AnyRef` and the second type parameter is an alias +of `Int`. + +When reading class files compiled with Scala 2, Scala 3 will do a best +effort to approximate existential types with its own types. It will +issue a warning that a precise emulation is not possible. diff --git a/docs/_spec/TODOreference/dropped-features/limit22.md b/docs/_spec/TODOreference/dropped-features/limit22.md new file mode 100644 index 000000000000..e72aeadbe2ca --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/limit22.md @@ -0,0 +1,17 @@ +--- +layout: doc-page +title: "Dropped: Limit 22" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/limit22.html +--- + +The limits of 22 for the maximal number of parameters of function types and the +maximal number of fields in tuple types have been dropped. + +* Functions can now have an arbitrary number of parameters. Functions beyond + [`scala.Function22`](https://www.scala-lang.org/api/current/scala/Function22.html) are erased to a new trait [`scala.runtime.FunctionXXL`](https://scala-lang.org/api/3.x/scala/runtime/FunctionXXL.html). + +* Tuples can also have an arbitrary number of fields. Tuples beyond [`scala.Tuple22`](https://www.scala-lang.org/api/current/scala/Tuple22.html) + are erased to a new class [`scala.runtime.TupleXXL`](https://scala-lang.org/api/3.x/scala/runtime/TupleXXL.html) (which extends the trait [`scala.Product`](https://scala-lang.org/api/3.x/scala/Product.md)). Furthermore, they support generic + operation such as concatenation and indexing. + +Both of these are implemented using arrays. diff --git a/docs/_spec/TODOreference/dropped-features/macros.md b/docs/_spec/TODOreference/dropped-features/macros.md new file mode 100644 index 000000000000..7ffe9043d0cd --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/macros.md @@ -0,0 +1,16 @@ +--- +layout: doc-page +title: "Dropped: Scala 2 Macros" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/macros.html +--- + +The previous, experimental macro system has been dropped. + +Instead, there is a cleaner, more restricted system based on two complementary concepts: `inline` and `'{ ... }`/`${ ... }` code generation. +`'{ ... }` delays the compilation of the code and produces an object containing the code, dually `${ ... }` evaluates an expression which produces code and inserts it in the surrounding `${ ... }`. +In this setting, a definition marked as inlined containing a `${ ... }` is a macro, the code inside the `${ ... }` is executed at compile-time and produces code in the form of `'{ ... }`. +Additionally, the contents of code can be inspected and created with a more complex reflection API as an extension of `'{ ... }`/`${ ... }` framework. + +* `inline` has been [implemented](../metaprogramming/inline.md) in Scala 3. +* Quotes `'{ ... }` and splices `${ ... }` has been [implemented](../metaprogramming/macros.md) in Scala 3. +* [TASTy reflect](../metaprogramming/reflection.md) provides more complex tree based APIs to inspect or create quoted code. diff --git a/docs/_spec/TODOreference/dropped-features/nonlocal-returns.md b/docs/_spec/TODOreference/dropped-features/nonlocal-returns.md new file mode 100644 index 000000000000..17b86f77ee56 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/nonlocal-returns.md @@ -0,0 +1,29 @@ +--- +layout: doc-page +title: "Deprecated: Nonlocal Returns" + +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/nonlocal-returns.html +--- + +Returning from nested anonymous functions has been deprecated, and will produce a warning from version `3.2`. + +Nonlocal returns are implemented by throwing and catching `scala.runtime.NonLocalReturnException`-s. This is rarely what is intended by the programmer. It can be problematic because of the hidden performance cost of throwing and catching exceptions. Furthermore, it is a leaky implementation: a catch-all exception handler can intercept a `NonLocalReturnException`. + +A drop-in library replacement is provided in [`scala.util.control.NonLocalReturns`](https://scala-lang.org/api/3.x/scala/util/control/NonLocalReturns$.html). Example: + +```scala +import scala.util.control.NonLocalReturns.* + +extension [T](xs: List[T]) + def has(elem: T): Boolean = returning { + for x <- xs do + if x == elem then throwReturn(true) + false + } + +@main def test(): Unit = + val xs = List(1, 2, 3, 4, 5) + assert(xs.has(2) == xs.contains(2)) +``` + +Note: compiler produces deprecation error on nonlocal returns only with `-source:future` option. diff --git a/docs/_spec/TODOreference/dropped-features/package-objects.md b/docs/_spec/TODOreference/dropped-features/package-objects.md new file mode 100644 index 000000000000..d8149e460bf5 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/package-objects.md @@ -0,0 +1,48 @@ +--- +layout: doc-page +title: "Dropped: Package Objects" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/package-objects.html +--- + +Package objects +```scala +package object p { + val a = ... + def b = ... +} +``` +will be dropped. They are still available in Scala 3.0 and 3.1, but will be deprecated and removed afterwards. + +Package objects are no longer needed since all kinds of definitions can now be written at the top-level. Example: +```scala +package p +type Labelled[T] = (String, T) +val a: Labelled[Int] = ("count", 1) +def b = a._2 + +case class C() + +extension (x: C) def pair(y: C) = (x, y) +``` +There may be several source files in a package containing such top-level definitions, and source files can freely mix top-level value, method, and type definitions with classes and objects. + +The compiler generates synthetic objects that wrap top-level definitions falling into one of the following categories: + + - all pattern, value, method, and type definitions, + - implicit classes and objects, + - companion objects of opaque type aliases. + +If a source file `src.scala` contains such top-level definitions, they will be put in a synthetic object named `src$package`. The wrapping is transparent, however. The definitions in `src` can still be accessed as members of the enclosing package. The synthetic object will be placed last in the file, +after any other package clauses, imports, or object and class definitions. + +**Note:** This means that +1. The name of a source file containing wrapped top-level definitions is relevant for binary compatibility. If the name changes, so does the name of the generated object and its class. + +2. A top-level main method `def main(args: Array[String]): Unit = ...` is wrapped as any other method. If it appears +in a source file `src.scala`, it could be invoked from the command line using a command like `scala src$package`. Since the +"program name" is mangled it is recommended to always put `main` methods in explicitly named objects. + +3. The notion of `private` is independent of whether a definition is wrapped or not. A `private` top-level definition is always visible from everywhere in the enclosing package. + +4. If several top-level definitions are overloaded variants with the same name, +they must all come from the same source file. diff --git a/docs/_spec/TODOreference/dropped-features/procedure-syntax.md b/docs/_spec/TODOreference/dropped-features/procedure-syntax.md new file mode 100644 index 000000000000..de76fbb32af2 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/procedure-syntax.md @@ -0,0 +1,19 @@ +--- +layout: doc-page +title: "Dropped: Procedure Syntax" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/procedure-syntax.html +--- + +Procedure syntax +```scala +def f() { ... } +``` +has been dropped. You need to write one of the following instead: +```scala +def f() = { ... } +def f(): Unit = { ... } +``` +Scala 3 accepts the old syntax under the `-source:3.0-migration` option. +If the `-migration` option is set, it can even rewrite old syntax to new. +The [Scalafix](https://scalacenter.github.io/scalafix/) tool also +can rewrite procedure syntax to make it Scala 3 compatible. diff --git a/docs/_spec/TODOreference/dropped-features/symlits.md b/docs/_spec/TODOreference/dropped-features/symlits.md new file mode 100644 index 000000000000..d3c0180b16e6 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/symlits.md @@ -0,0 +1,24 @@ +--- +layout: doc-page +title: "Dropped: Symbol Literals" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/symlits.html +--- + +Symbol literals are no longer supported. + +The [`scala.Symbol`](https://scala-lang.org/api/3.x/scala/Symbol.html) class still exists, so a literal translation of the symbol literal `'xyz` is `Symbol("xyz")`. However, it is recommended to use a plain string literal `"xyz"` instead. (The `Symbol` class will be deprecated and removed in the future). Example: + + +``` +scalac Test.scala +-- Error: Test.scala:1:25 ------------------------------------------------------------------------------------------------ + +1 |@main def test = println('abc) + | ^ + | symbol literal 'abc is no longer supported, + | use a string literal "abc" or an application Symbol("abc") instead, + | or enclose in braces '{abc} if you want a quoted expression. + | For now, you can also `import language.deprecated.symbolLiterals` to accept + | the idiom, but this possibility might no longer be available in the future. +1 error found +``` diff --git a/docs/_spec/TODOreference/dropped-features/this-qualifier.md b/docs/_spec/TODOreference/dropped-features/this-qualifier.md new file mode 100644 index 000000000000..4fcadff8fae3 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/this-qualifier.md @@ -0,0 +1,33 @@ +--- +layout: doc-page +title: "Dropped: private[this] and protected[this]" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/this-qualifier.html +--- + +The `private[this]` and `protected[this]` access modifiers are deprecated and will be phased out. + +Previously, these modifiers were needed for + + - avoiding the generation of getters and setters + - excluding code under a `private[this]` from variance checks. (Scala 2 also excludes `protected[this]` but this was found to be unsound and was therefore removed). + - avoiding the generation of fields, if a `private[this] val` is not accessed + by a class method. + +The compiler now infers for `private` members the fact that they are only accessed via `this`. Such members are treated as if they had been declared `private[this]`. `protected[this]` is dropped without a replacement. + +This change can in some cases change the semantics of a Scala program, since a +`private` val is no longer guaranteed to generate a field. The field +is omitted if + + - the `val` is only accessed via `this`, and + - the `val` is not accessed from a method in the current class. + +This can cause problems if a program tries to access the missing private field via reflection. The recommended fix is to declare the field instead to be qualified private with the enclosing class as qualifier. Example: +```scala + class C(x: Int): + private[C] val field = x + 1 + // [C] needed if `field` is to be accessed through reflection + val retained = field * field +``` + + diff --git a/docs/_spec/TODOreference/dropped-features/type-projection.md b/docs/_spec/TODOreference/dropped-features/type-projection.md new file mode 100644 index 000000000000..08b5ffb34eca --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/type-projection.md @@ -0,0 +1,18 @@ +--- +layout: doc-page +title: "Dropped: General Type Projection" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/type-projection.html +--- + +Scala so far allowed general type projection `T#A` where `T` is an arbitrary type +and `A` names a type member of `T`. + +Scala 3 disallows this if `T` is an abstract type (class types and type aliases +are fine). This change was made because unrestricted type projection +is [unsound](https://github.com/lampepfl/dotty/issues/1050). + +This restriction rules out the [type-level encoding of a combinator +calculus](https://michid.wordpress.com/2010/01/29/scala-type-level-encoding-of-the-ski-calculus/). + +To rewrite code using type projections on abstract types, consider using +path-dependent types or implicit parameters. diff --git a/docs/_spec/TODOreference/dropped-features/weak-conformance-spec.md b/docs/_spec/TODOreference/dropped-features/weak-conformance-spec.md new file mode 100644 index 000000000000..07625dcfe885 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/weak-conformance-spec.md @@ -0,0 +1,54 @@ +--- +layout: doc-page +title: "Dropped: Weak Conformance - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/weak-conformance-spec.html +--- + +To simplify the underlying type theory, Scala 3 drops the notion of +[*weak conformance*](https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#weak-conformance) +altogether. Instead, it provides more flexibility when +assigning a type to a constant expression. The new rule is: + + - *If* a list of expressions `Es` appears as one of + + - the elements of a vararg parameter, or + - the alternatives of an if-then-else or match expression, or + - the body and catch results of a try expression, + +- *and* all expressions have primitive numeric types, but they do not + all have the same type, + +- *then* the following is attempted: + + - the expressions `Es` are partitioned into `Int` constants on the + one hand, and all other expressions on the other hand, + - if all the other expressions have the same numeric type `T` + (which can be one of `Byte`, `Short`, `Char`, `Int`, `Long`, `Float`, + `Double`), possibly after widening, and if none of the `Int` + literals would incur a loss of precision when converted to `T`, + then they are thus converted (the other expressions are left + unchanged regardless), + - otherwise, the expressions `Es` are used unchanged. + + A loss of precision occurs for + - an `Int -> Float` conversion of a constant + `c` if `c.toFloat.toInt != c` + - an `Int -> Byte` conversion of a constant + `c` if `c.toByte.toInt != c`, + - an `Int -> Short` conversion of a constant + `c` if `c.toShort.toInt != c`. + +## Examples + +```scala +inline val b = 33 +def f(): Int = b + 1 +Array(b, 33, 5.5) : Array[Double] // b is an inline val +Array(f(), 33, 5.5) : Array[AnyVal] // f() is not a constant +Array(5, 11L) : Array[Long] +Array(5, 11L, 5.5) : Array[AnyVal] // Long and Double found +Array(1.0f, 2) : Array[Float] +Array(1.0f, 1234567890): Array[AnyVal] // loss of precision +Array(b, 33, 'a') : Array[Char] +Array(5.toByte, 11) : Array[Byte] +``` diff --git a/docs/_spec/TODOreference/dropped-features/weak-conformance.md b/docs/_spec/TODOreference/dropped-features/weak-conformance.md new file mode 100644 index 000000000000..b1478326b2c9 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/weak-conformance.md @@ -0,0 +1,47 @@ +--- +layout: doc-page +title: "Dropped: Weak Conformance" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/weak-conformance.html +--- + +In some situations, Scala used a _weak conformance_ relation when +testing type compatibility or computing the least upper bound of a set +of types. The principal motivation behind weak conformance was to +make an expression like this have type `List[Double]`: + +```scala +List(1.0, math.sqrt(3.0), 0, -3.3) // : List[Double] +``` + +It's "obvious" that this should be a `List[Double]`. However, without +some special provision, the least upper bound of the lists's element +types `(Double, Double, Int, Double)` would be `AnyVal`, hence the list +expression would be given type `List[AnyVal]`. + +A less obvious example is the following one, which was also typed as a +`List[Double]`, using the weak conformance relation. + +```scala +val n: Int = 3 +val c: Char = 'X' +val d: Double = math.sqrt(3.0) +List(n, c, d) // used to be: List[Double], now: List[AnyVal] +``` + +Here, it is less clear why the type should be widened to +`List[Double]`, a `List[AnyVal]` seems to be an equally valid -- and +more principled -- choice. + +Weak conformance applies to all "numeric" types (including `Char`), and +independently of whether the expressions are literals or not. However, +in hindsight, the only intended use case is for *integer literals* to +be adapted to the type of the other expressions. Other types of numerics +have an explicit type annotation embedded in their syntax (`f`, `d`, +`.`, `L` or `'` for `Char`s) which ensures that their author really +meant them to have that specific type). + +Therefore, Scala 3 drops the general notion of weak conformance, and +instead keeps one rule: `Int` literals are adapted to other numeric +types if necessary. + +[More details](weak-conformance-spec.md) diff --git a/docs/_spec/TODOreference/dropped-features/wildcard-init.md b/docs/_spec/TODOreference/dropped-features/wildcard-init.md new file mode 100644 index 000000000000..e42854079cf9 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/wildcard-init.md @@ -0,0 +1,23 @@ +--- +layout: doc-page +title: "Dropped: Wildcard Initializer" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/wildcard-init.html +--- + +The syntax + +```scala + var x: A = _ +``` + +that was used to indicate an uninitialized field, has been dropped. +At its place there is a special value `uninitialized` in the `scala.compiletime` package. +To get an uninitialized field, you now write + +```scala +import scala.compiletime.uninitialized + +var x: A = uninitialized +``` + +To enable cross-compilation, `_` is still supported, but it will be dropped in a future 3.x version. diff --git a/docs/_spec/TODOreference/dropped-features/xml.md b/docs/_spec/TODOreference/dropped-features/xml.md new file mode 100644 index 000000000000..458a347a66c4 --- /dev/null +++ b/docs/_spec/TODOreference/dropped-features/xml.md @@ -0,0 +1,39 @@ +--- +layout: doc-page +title: "Dropped: XML Literals" +nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/xml.html +--- + +XML Literals are still supported, but will be dropped in the near future, to +be replaced with [XML string interpolation](https://github.com/lampepfl/xml-interpolator): + +```scala +import dotty.xml.interpolator.* + +case class Person(name: String) { override def toString = name } + +@main def test: Unit = + val bill = Person("Bill") + val john = Person("John") + val mike = Person("Mike") + val todoList = List( + (bill, john, "Meeting", "Room 203, 11:00am"), + (john, mike, "Holiday", "March 22-24") + ) + // XML literals (to be dropped) + val mails1 = for (from, to, heading, body) <- todoList yield + + {from}{to} + {heading}{body} + + println(mails1) + // XML string interpolation + val mails2 = for (from, to, heading, body) <- todoList yield xml""" + + ${from}${to} + ${heading}${body} + """ + println(mails2) +``` + +For more information, see the semester project [XML String Interpolator for Dotty](https://infoscience.epfl.ch/record/267527) by Yassin Kammoun (2019). diff --git a/docs/_spec/TODOreference/enums/adts.md b/docs/_spec/TODOreference/enums/adts.md new file mode 100644 index 000000000000..3ab8c9f3b45b --- /dev/null +++ b/docs/_spec/TODOreference/enums/adts.md @@ -0,0 +1,173 @@ +--- +layout: doc-page +title: "Algebraic Data Types" +nightlyOf: https://docs.scala-lang.org/scala3/reference/enums/adts.html +--- + +The [`enum` concept](./enums.md) is general enough to also support algebraic data +types (ADTs) and their generalized version (GADTs). Here is an example +how an `Option` type can be represented as an ADT: + +```scala +enum Option[+T]: + case Some(x: T) + case None +``` + +This example introduces an `Option` enum with a covariant type +parameter `T` consisting of two cases, `Some` and `None`. `Some` is +parameterized with a value parameter `x`. It is a shorthand for writing a +case class that extends `Option`. Since `None` is not parameterized, it +is treated as a normal enum value. + +The `extends` clauses that were omitted in the example above can also +be given explicitly: + +```scala +enum Option[+T]: + case Some(x: T) extends Option[T] + case None extends Option[Nothing] +``` + +Note that the parent type of the `None` value is inferred as +`Option[Nothing]`. Generally, all covariant type parameters of the enum +class are minimized in a compiler-generated `extends` clause whereas all +contravariant type parameters are maximized. If `Option` was non-variant, +you would need to give the extends clause of `None` explicitly. + +As for normal enum values, the cases of an `enum` are all defined in +the `enum`s companion object. So it's `Option.Some` and `Option.None` +unless the definitions are "pulled out" with an import: + +```scala +scala> Option.Some("hello") +val res1: t2.Option[String] = Some(hello) + +scala> Option.None +val res2: t2.Option[Nothing] = None +``` + +Note that the type of the expressions above is always `Option`. Generally, the type of a enum case constructor application will be widened to the underlying enum type, unless a more specific type is expected. This is a subtle difference with respect to normal case classes. The classes making up the cases do exist, and can be unveiled, either by constructing them directly with a `new`, or by explicitly providing an expected type. + +```scala +scala> new Option.Some(2) +val res3: Option.Some[Int] = Some(2) +scala> val x: Option.Some[Int] = Option.Some(3) +val res4: Option.Some[Int] = Some(3) +``` + +As all other enums, ADTs can define methods. For instance, here is `Option` again, with an +`isDefined` method and an `Option(...)` constructor in its companion object. + +```scala +enum Option[+T]: + case Some(x: T) + case None + + def isDefined: Boolean = this match + case None => false + case _ => true + +object Option: + + def apply[T >: Null](x: T): Option[T] = + if x == null then None else Some(x) + +end Option +``` + +Enumerations and ADTs have been presented as two different +concepts. But since they share the same syntactic construct, they can +be seen simply as two ends of a spectrum and it is perfectly possible +to construct hybrids. For instance, the code below gives an +implementation of `Color` either with three enum values or with a +parameterized case that takes an RGB value. + +```scala +enum Color(val rgb: Int): + case Red extends Color(0xFF0000) + case Green extends Color(0x00FF00) + case Blue extends Color(0x0000FF) + case Mix(mix: Int) extends Color(mix) +``` + +## Parameter Variance of Enums + +By default, parameterized cases of enums with type parameters will copy the type parameters of their parent, along +with any variance notations. As usual, it is important to use type parameters carefully when they are variant, as shown +below: + +The following `View` enum has a contravariant type parameter `T` and a single case `Refl`, representing a function +mapping a type `T` to itself: + +```scala +enum View[-T]: + case Refl(f: T => T) +``` + +The definition of `Refl` is incorrect, as it uses contravariant type `T` in the covariant result position of a +function type, leading to the following error: + +```scala +-- Error: View.scala:2:12 -------- +2 | case Refl(f: T => T) + | ^^^^^^^^^ + |contravariant type T occurs in covariant position in type T => T of value f + |enum case Refl requires explicit declaration of type T to resolve this issue. +``` + +Because `Refl` does not declare explicit parameters, it looks to the compiler like the following: + +```scala +enum View[-T]: + case Refl[/*synthetic*/-T1](f: T1 => T1) extends View[T1] +``` + +The compiler has inferred for `Refl` the contravariant type parameter `T1`, following `T` in `View`. +We can now clearly see that `Refl` needs to declare its own non-variant type parameter to correctly type `f`, +and can remedy the error by the following change to `Refl`: + +```diff +enum View[-T]: +- case Refl(f: T => T) ++ case Refl[R](f: R => R) extends View[R] +``` + +Above, type `R` is chosen as the parameter for `Refl` to highlight that it has a different meaning to +type `T` in `View`, but any name will do. + +After some further changes, a more complete implementation of `View` can be given as follows and be used +as the function type `T => U`: + +```scala +enum View[-T, +U] extends (T => U): + case Refl[R](f: R => R) extends View[R, R] + + final def apply(t: T): U = this match + case refl: Refl[r] => refl.f(t) +``` + +## Syntax of Enums + +Changes to the syntax fall in two categories: enum definitions and cases inside enums. +The changes are specified below as deltas with respect to the Scala syntax given [here](../syntax.md) + + 1. Enum definitions are defined as follows: + + ``` + TmplDef ::= `enum' EnumDef + EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumBody + EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’ + EnumStat ::= TemplateStat + | {Annotation [nl]} {Modifier} EnumCase + ``` + + 2. Cases of enums are defined as follows: + + ``` + EnumCase ::= `case' (id ClassConstr [`extends' ConstrApps]] | ids) + ``` + +## Reference + +For more information, see [Issue #1970](https://github.com/lampepfl/dotty/issues/1970). diff --git a/docs/_spec/TODOreference/enums/desugarEnums.md b/docs/_spec/TODOreference/enums/desugarEnums.md new file mode 100644 index 000000000000..477653d670bb --- /dev/null +++ b/docs/_spec/TODOreference/enums/desugarEnums.md @@ -0,0 +1,215 @@ +--- +layout: doc-page +title: "Translation of Enums and ADTs" +nightlyOf: https://docs.scala-lang.org/scala3/reference/enums/desugarEnums.html +--- + +The compiler expands enums and their cases to code that only uses +Scala's other language features. As such, enums in Scala are +convenient _syntactic sugar_, but they are not essential to understand +Scala's core. + +We now explain the expansion of enums in detail. First, +some terminology and notational conventions: + + - We use `E` as a name of an enum, and `C` as a name of a case that appears in `E`. + - We use `<...>` for syntactic constructs that in some circumstances might be empty. For instance, + `` represents one or more parameter lists `(...)` or nothing at all. + + - Enum cases fall into three categories: + + - _Class cases_ are those cases that are parameterized, either with a type parameter section `[...]` or with one or more (possibly empty) parameter sections `(...)`. + - _Simple cases_ are cases of a non-generic enum that have neither parameters nor an extends clause or body. That is, they consist of a name only. + - _Value cases_ are all cases that do not have a parameter section but that do have a (possibly generated) `extends` clause and/or a body. + + Simple cases and value cases are collectively called _singleton cases_. + +The desugaring rules imply that class cases are mapped to case classes, and singleton cases are mapped to `val` definitions. + +There are nine desugaring rules. Rule (1) desugars enum definitions. Rules +(2) and (3) desugar simple cases. Rules (4) to (6) define `extends` clauses for cases that +are missing them. Rules (7) to (9) define how such cases with `extends` clauses +map into `case class`es or `val`s. + +1. An `enum` definition + ```scala + enum E ... { } + ``` + expands to a `sealed abstract` class that extends the `scala.reflect.Enum` trait and + an associated companion object that contains the defined cases, expanded according + to rules (2 - 8). The enum class starts with a compiler-generated import that imports + the names `` of all cases so that they can be used without prefix in the class. + ```scala + sealed abstract class E ... extends with scala.reflect.Enum { + import E.{ } + + } + object E { } + ``` + +2. A simple case consisting of a comma-separated list of enum names + ```scala + case C_1, ..., C_n + ``` + expands to + ```scala + case C_1; ...; case C_n + ``` + Any modifiers or annotations on the original case extend to all expanded + cases. + +3. A simple case + ```scala + case C + ``` + of an enum `E` that does not take type parameters expands to + ```scala + val C = $new(n, "C") + ``` + Here, `$new` is a private method that creates an instance of `E` (see + below). + +4. If `E` is an enum with type parameters + ```scala + V1 T1 >: L1 <: U1 , ... , Vn Tn >: Ln <: Un (n > 0) + ``` + where each of the variances `Vi` is either `'+'` or `'-'`, then a simple case + ```scala + case C + ``` + expands to + ```scala + case C extends E[B1, ..., Bn] + ``` + where `Bi` is `Li` if `Vi = '+'` and `Ui` if `Vi = '-'`. This result is then further + rewritten with rule (8). Simple cases of enums with non-variant type + parameters are not permitted (however value cases with explicit `extends` clause are) + +5. A class case without an extends clause + ```scala + case C + ``` + of an enum `E` that does not take type parameters expands to + ```scala + case C extends E + ``` + This result is then further rewritten with rule (9). + +6. If `E` is an enum with type parameters `Ts`, a class case with neither type parameters nor an extends clause + ```scala + case C + ``` + expands to + ```scala + case C[Ts] extends E[Ts] + ``` + This result is then further rewritten with rule (9). For class cases that have type parameters themselves, an extends clause needs to be given explicitly. + +7. If `E` is an enum with type parameters `Ts`, a class case without type parameters but with an extends clause + ```scala + case C extends + ``` + expands to + ```scala + case C[Ts] extends + ``` + provided at least one of the parameters `Ts` is mentioned in a parameter type in + `` or in a type argument in ``. + +8. A value case + ```scala + case C extends + ``` + expands to a value definition in `E`'s companion object: + ```scala + val C = new { ; def ordinal = n } + ``` + where `n` is the ordinal number of the case in the companion object, + starting from 0. The anonymous class also + implements the abstract `Product` methods that it inherits from `Enum`. + + It is an error if a value case refers to a type parameter of the enclosing `enum` + in a type argument of ``. + +9. A class case + ```scala + case C extends + ``` + expands analogous to a final case class in `E`'s companion object: + ```scala + final case class C extends + ``` + The enum case defines an `ordinal` method of the form + ```scala + def ordinal = n + ``` + where `n` is the ordinal number of the case in the companion object, + starting from 0. + + It is an error if a value case refers to a type parameter of the enclosing `enum` + in a parameter type in `` or in a type argument of ``, unless that parameter is already + a type parameter of the case, i.e. the parameter name is defined in ``. + + The compiler-generated `apply` and `copy` methods of an enum case + ```scala + case C(ps) extends P1, ..., Pn + ``` + are treated specially. A call `C(ts)` of the apply method is ascribed the underlying type + `P1 & ... & Pn` (dropping any [transparent traits](../other-new-features/transparent-traits.md)) + as long as that type is still compatible with the expected type at the point of application. + A call `t.copy(ts)` of `C`'s `copy` method is treated in the same way. + +## Translation of Enums with Singleton Cases + +An enum `E` (possibly generic) that defines one or more singleton cases +will define the following additional synthetic members in its companion object (where `E'` denotes `E` with +any type parameters replaced by wildcards): + + - A method `valueOf(name: String): E'`. It returns the singleton case value whose identifier is `name`. + - A method `values` which returns an `Array[E']` of all singleton case + values defined by `E`, in the order of their definitions. + +If `E` contains at least one simple case, its companion object will define in addition: + + - A private method `$new` which defines a new simple case value with given + ordinal number and name. This method can be thought as being defined as + follows. + + ```scala + private def $new(_$ordinal: Int, $name: String) = + new E with runtime.EnumValue: + def ordinal = _$ordinal + override def productPrefix = $name // if not overridden in `E` + override def toString = $name // if not overridden in `E` + ``` + +The anonymous class also implements the abstract `Product` methods that it inherits from `Enum`. +The `ordinal` method is only generated if the enum does not extend from `java.lang.Enum` (as Scala enums do not extend +`java.lang.Enum`s unless explicitly specified). In case it does, there is no need to generate `ordinal` as +`java.lang.Enum` defines it. Similarly there is no need to override `toString` as that is defined in terms of `name` in +`java.lang.Enum`. Finally, `productPrefix` will call `this.name` when `E` extends `java.lang.Enum`. + +## Scopes for Enum Cases + +A case in an `enum` is treated similarly to a secondary constructor. It can access neither the enclosing `enum` using `this`, nor its value parameters or instance members using simple +identifiers. + +Even though translated enum cases are located in the enum's companion object, referencing +this object or its members via `this` or a simple identifier is also illegal. The compiler typechecks enum cases in the scope of the enclosing companion object but flags any such illegal accesses as errors. + +## Translation of Java-compatible enums + +A Java-compatible enum is an enum that extends `java.lang.Enum`. The translation rules are the same as above, with the reservations defined in this section. + +It is a compile-time error for a Java-compatible enum to have class cases. + +Cases such as `case C` expand to a `@static val` as opposed to a `val`. This allows them to be generated as static fields of the enum type, thus ensuring they are represented the same way as Java enums. + +## Other Rules + +- A normal case class which is not produced from an enum case is not allowed to extend + `scala.reflect.Enum`. This ensures that the only cases of an enum are the ones that are + explicitly declared in it. + +- If an enum case has an `extends` clause, the enum class must be one of the + classes that's extended. diff --git a/docs/_spec/TODOreference/enums/enums-index.md b/docs/_spec/TODOreference/enums/enums-index.md new file mode 100644 index 000000000000..80d703c3e897 --- /dev/null +++ b/docs/_spec/TODOreference/enums/enums-index.md @@ -0,0 +1,7 @@ +--- +layout: index +title: "Enums" +nightlyOf: https://docs.scala-lang.org/scala3/reference/enums/index.html +--- + +This chapter documents enums in Scala 3. diff --git a/docs/_spec/TODOreference/enums/enums.md b/docs/_spec/TODOreference/enums/enums.md new file mode 100644 index 000000000000..65051bdfb39f --- /dev/null +++ b/docs/_spec/TODOreference/enums/enums.md @@ -0,0 +1,222 @@ +--- +layout: doc-page +title: "Enumerations" +nightlyOf: https://docs.scala-lang.org/scala3/reference/enums/enums.html +--- + +An enumeration is used to define a type consisting of a set of named values. + +```scala +enum Color: + case Red, Green, Blue +``` + +This defines a new `sealed` class, `Color`, with three values, `Color.Red`, +`Color.Green`, `Color.Blue`. The color values are members of `Color`s +companion object. + +## Parameterized enums + +Enums can be parameterized. + +```scala +enum Color(val rgb: Int): + case Red extends Color(0xFF0000) + case Green extends Color(0x00FF00) + case Blue extends Color(0x0000FF) +``` + +As the example shows, you can define the parameter value by using an +explicit extends clause. + +## Methods defined for enums + +The values of an enum correspond to unique integers. The integer +associated with an enum value is returned by its `ordinal` method: + +```scala +scala> val red = Color.Red +val red: Color = Red +scala> red.ordinal +val res0: Int = 0 +``` + +The companion object of an enum also defines three utility methods. +The `valueOf` method obtains an enum value +by its name. The `values` method returns all enum values +defined in an enumeration in an `Array`. The `fromOrdinal` +method obtains an enum value from its ordinal (`Int`) value. + +```scala +scala> Color.valueOf("Blue") +val res0: Color = Blue +scala> Color.values +val res1: Array[Color] = Array(Red, Green, Blue) +scala> Color.fromOrdinal(0) +val res2: Color = Red +``` + +## User-defined members of enums + +It is possible to add your own definitions to an enum. Example: + +```scala +enum Planet(mass: Double, radius: Double): + private final val G = 6.67300E-11 + def surfaceGravity = G * mass / (radius * radius) + def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity + + case Mercury extends Planet(3.303e+23, 2.4397e6) + case Venus extends Planet(4.869e+24, 6.0518e6) + case Earth extends Planet(5.976e+24, 6.37814e6) + case Mars extends Planet(6.421e+23, 3.3972e6) + case Jupiter extends Planet(1.9e+27, 7.1492e7) + case Saturn extends Planet(5.688e+26, 6.0268e7) + case Uranus extends Planet(8.686e+25, 2.5559e7) + case Neptune extends Planet(1.024e+26, 2.4746e7) +end Planet +``` + +## User-defined companion object of enums +It is also possible to define an explicit companion object for an enum: + +```scala +object Planet: + def main(args: Array[String]) = + val earthWeight = args(0).toDouble + val mass = earthWeight / Earth.surfaceGravity + for p <- values do + println(s"Your weight on $p is ${p.surfaceWeight(mass)}") +end Planet +``` + +## Restrictions on Enum Cases + +Enum case declarations are similar to secondary constructors: +they are scoped outside of the enum template, despite being declared within it. +This means that enum case declarations cannot access inner members of the +enum class. + +Similarly, enum case declarations may not directly reference members of the enum's companion object, +even if they are imported (directly, or by renaming). For example: + +```scala +import Planet.* +enum Planet(mass: Double, radius: Double): + private final val (mercuryMass, mercuryRadius) = (3.303e+23, 2.4397e6) + + case Mercury extends Planet(mercuryMass, mercuryRadius) // Not found + case Venus extends Planet(venusMass, venusRadius) // illegal reference + case Earth extends Planet(Planet.earthMass, Planet.earthRadius) // ok +object Planet: + private final val (venusMass, venusRadius) = (4.869e+24, 6.0518e6) + private final val (earthMass, earthRadius) = (5.976e+24, 6.37814e6) +end Planet +``` +The fields referenced by `Mercury` are not visible, and the fields referenced by `Venus` may not +be referenced directly (using `import Planet.*`). You must use an indirect reference, +such as demonstrated with `Earth`. + +## Deprecation of Enum Cases + +As a library author, you may want to signal that an enum case is no longer intended for use. However you could still want to gracefully handle the removal of a case from your public API, such as special casing deprecated cases. + +To illustrate, say that the `Planet` enum originally had an additional case: + +```diff + enum Planet(mass: Double, radius: Double): + ... + case Neptune extends Planet(1.024e+26, 2.4746e7) ++ case Pluto extends Planet(1.309e+22, 1.1883e3) + end Planet +``` + +We now want to deprecate the `Pluto` case. First we add the `scala.deprecated` annotation to `Pluto`: + +```diff + enum Planet(mass: Double, radius: Double): + ... + case Neptune extends Planet(1.024e+26, 2.4746e7) +- case Pluto extends Planet(1.309e+22, 1.1883e3) ++ ++ @deprecated("refer to IAU definition of planet") ++ case Pluto extends Planet(1.309e+22, 1.1883e3) + end Planet +``` + +Outside the lexical scopes of `enum Planet` or `object Planet`, references to `Planet.Pluto` will produce a deprecation warning, but within those scopes we can still reference it to implement introspection over the deprecated cases: + +```scala +trait Deprecations[T <: reflect.Enum] { + extension (t: T) def isDeprecatedCase: Boolean +} + +object Planet { + given Deprecations[Planet] with { + extension (p: Planet) + def isDeprecatedCase = p == Pluto + } +} +``` + +We could imagine that a library may use [type class derivation](../contextual/derivation.md) to automatically provide an instance for `Deprecations`. + +## Compatibility with Java Enums + +If you want to use the Scala-defined enums as [Java enums](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html), you can do so by extending +the class `java.lang.Enum`, which is imported by default, as follows: + +```scala +enum Color extends Enum[Color] { case Red, Green, Blue } +``` + +The type parameter comes from the Java enum [definition](https://docs.oracle.com/javase/8/docs/api/index.html?java/lang/Enum.html) and should be the same as the type of the enum. +There is no need to provide constructor arguments (as defined in the Java API docs) to `java.lang.Enum` when extending it – the compiler will generate them automatically. + +After defining `Color` like that, you can use it like you would a Java enum: + +```scala +scala> Color.Red.compareTo(Color.Green) +val res15: Int = -1 +``` + +For a more in-depth example of using Scala 3 enums from Java, see [this test](https://github.com/lampepfl/dotty/tree/main/tests/run/enum-java). In the test, the enums are defined in the `MainScala.scala` file and used from a Java source, `Test.java`. + +## Implementation + +Enums are represented as `sealed` classes that extend the `scala.reflect.Enum` trait. +This trait defines a single public method, `ordinal`: + +```scala +package scala.reflect + +/** A base trait of all Scala enum definitions */ +transparent trait Enum extends Any, Product, Serializable: + + /** A number uniquely identifying a case of an enum */ + def ordinal: Int +``` + +Enum values with `extends` clauses get expanded to anonymous class instances. +For instance, the `Venus` value above would be defined like this: + +```scala +val Venus: Planet = new Planet(4.869E24, 6051800.0): + def ordinal: Int = 1 + override def productPrefix: String = "Venus" + override def toString: String = "Venus" +``` + +Enum values without `extends` clauses all share a single implementation +that can be instantiated using a private method that takes a tag and a name as arguments. +For instance, the first +definition of value `Color.Red` above would expand to: + +```scala +val Red: Color = $new(0, "Red") +``` + +## Reference + +For more information, see [Issue #1970](https://github.com/lampepfl/dotty/issues/1970) and +[PR #4003](https://github.com/lampepfl/dotty/pull/4003). diff --git a/docs/_spec/TODOreference/experimental/canthrow.md b/docs/_spec/TODOreference/experimental/canthrow.md new file mode 100644 index 000000000000..025a0ed1c686 --- /dev/null +++ b/docs/_spec/TODOreference/experimental/canthrow.md @@ -0,0 +1,281 @@ +--- +layout: doc-page +title: "CanThrow Capabilities" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/canthrow.html +--- + +This page describes experimental support for exception checking in Scala 3. It is enabled by the language import +```scala +import language.experimental.saferExceptions +``` +The reason for publishing this extension now is to get feedback on its usability. We are working on more advanced type systems that build on the general ideas put forward in the extension. Those type systems have application areas beyond checked exceptions. Exception checking is a useful starting point since exceptions are familiar to all Scala programmers and their current treatment leaves room for improvement. + +## Why Exceptions? + +Exceptions are an ideal mechanism for error handling in many situations. They serve the intended purpose of propagating error conditions with a minimum of boilerplate. They cause zero overhead for the "happy path", which means they are very efficient as long as errors arise infrequently. Exceptions are also debug friendly, since they produce stack traces that can be inspected at the handler site. So one never has to guess where an erroneous condition originated. + +## Why Not Exceptions? + +However, exceptions in current Scala and many other languages are not reflected in the type system. This means that an essential part of the contract of a function - i.e. what exceptions can it produce? - is not statically checked. Most people acknowledge that this is a problem, but that so far the alternative of checked exceptions was just too painful to be considered. A good example are Java checked exceptions, which do the right thing in principle, but are widely regarded as a mistake since they are so difficult to deal with. So far, none of the successor languages that are modeled after Java or that build on the JVM has copied this feature. See for example Anders Hejlsberg's [statement on why C# does not have checked exceptions](https://www.artima.com/articles/the-trouble-with-checked-exceptions). + +## The Problem With Java's Checked Exceptions + +The main problem with [Java's checked exception model](https://docs.oracle.com/javase/specs/jls/se8/html/jls-11.html#jls-11.2) is its inflexibility, which is due to lack of polymorphism. Consider for instance the `map` function which is declared on `List[A]` like this: +```scala + def map[B](f: A => B): List[B] +``` +In the Java model, function `f` is not allowed to throw a checked exception. So the following call would be invalid: +```scala + xs.map(x => if x < limit then x * x else throw LimitExceeded()) +``` +The only way around this would be to wrap the checked exception `LimitExceeded` in an unchecked [`java.lang.RuntimeException`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/RuntimeException.html) that is caught at the callsite and unwrapped again. Something like this: +```scala + try + xs.map(x => if x < limit then x * x else throw Wrapper(LimitExceeded())) + catch case Wrapper(ex) => throw ex +``` +Ugh! No wonder checked exceptions in Java are not very popular. + +## Monadic Effects + +So the dilemma is that exceptions are easy to use only as long as we forget static type checking. This has caused many people working with Scala to abandon exceptions altogether and to use an error monad like [`Either`](https://scala-lang.org/api/3.x/scala/util/Either.html) instead. This can work in many situations but is not without its downsides either. It makes code a lot more complicated and harder to refactor. It means one is quickly confronted with the problem how to work with several monads. In general, dealing with one monad at a time in Scala is straightforward but dealing with several monads together is much less pleasant since monads don't compose. A great number of techniques have been proposed, implemented, and promoted to deal with this, from monad transformers, to free monads, to tagless final. But none of these techniques is universally liked; each introduces a complicated DSL that's hard to understand for non-experts, introduces runtime overheads, and makes debugging difficult. In the end, quite a few developers prefer to work instead with a single "super-monad" like [`ZIO`](https://zio.dev/version-1.x/datatypes/core/zio) that has error propagation built in alongside other aspects. This one-size fits all approach can work very nicely, even though (or is it because?) it represents an all-encompassing framework. + +However, a programming language is not a framework; it has to cater also for those applications that do not fit the framework's use cases. So there's still a strong motivation for getting exception checking right. + +## From Effects To Capabilities + +Why does `map` work so poorly with Java's checked exception model? It's because +`map`'s signature limits function arguments to not throw checked exceptions. We could try to come up with a more polymorphic formulation of `map`. For instance, it could look like this: +```scala + def map[B, E](f: A => B throws E): List[B] throws E +``` +This assumes a type `A throws E` to indicate computations of type `A` that can throw an exception of type `E`. But in practice the overhead of the additional type parameters makes this approach unappealing as well. Note in particular that we'd have to parameterize _every method_ that takes a function argument that way, so the added overhead of declaring all these exception types looks just like a sort of ceremony we would like to avoid. + +But there is a way to avoid the ceremony. Instead of concentrating on possible _effects_ such as "this code might throw an exception", concentrate on _capabilities_ such as "this code needs the capability to throw an exception". From a standpoint of expressiveness this is quite similar. But capabilities can be expressed as parameters whereas traditionally effects are expressed as some addition to result values. It turns out that this can make a big difference! + +## The `CanThrow` Capability + +In the _effects as capabilities_ model, an effect is expressed as an (implicit) parameter of a certain type. For exceptions we would expect parameters of type +[`CanThrow[E]`](https://scala-lang.org/api/3.x/scala/CanThrow.html) where `E` stands for the exception that can be thrown. Here is the definition of `CanThrow`: +```scala +erased class CanThrow[-E <: Exception] +``` +This shows another experimental Scala feature: [erased definitions](./erased-defs.md). Roughly speaking, values of an erased class do not generate runtime code; they are erased before code generation. This means that all `CanThrow` capabilities are compile-time only artifacts; they do not have a runtime footprint. + +Now, if the compiler sees a `throw Exc()` construct where `Exc` is a checked exception, it will check that there is a capability of type `CanThrow[Exc]` that can be summoned as a given. It's a compile-time error if that's not the case. + +How can the capability be produced? There are several possibilities: + +Most often, the capability is produced by having a using clause `(using CanThrow[Exc])` in some enclosing scope. This roughly corresponds to a [`throws`](https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.6) clause in Java. The analogy is even stronger since alongside [`CanThrow`](https://scala-lang.org/api/3.x/scala/CanThrow.html) there is also the following type alias defined in the [`scala`](https://scala-lang.org/api/3.x/scala.html) package: +```scala +infix type A = Int +``` +```scala +infix type $throws[R, +E <: Exception] = CanThrow[E] ?=> R +``` +That is, [`R $throws E`](https://scala-lang.org/api/3.x/scala/runtime.html#$throws-0) is a context function type that takes an implicit `CanThrow[E]` parameter and that returns a value of type `R`. What's more, the compiler will translate an infix types with `throws` as the operator to `$throws` applications according to the rules +``` + A throws E --> A $throws E + A throws E₁ | ... | Eᵢ --> A $throws E₁ ... $throws Eᵢ +``` +Therefore, a method written like this: +```scala +def m(x: T)(using CanThrow[E]): U +``` +can alternatively be expressed like this: +```scala +def m(x: T): U throws E +``` +Also the capability to throw multiple types of exceptions can be expressed in a few ways as shown in the examples below: +```scala +def m(x: T): U throws E1 | E2 +def m(x: T): U throws E1 throws E2 +def m(x: T)(using CanThrow[E1], CanThrow[E2]): U +def m(x: T)(using CanThrow[E1])(using CanThrow[E2]): U +def m(x: T)(using CanThrow[E1]): U throws E2 +``` + +**Note 1:** A signature like +```scala +def m(x: T)(using CanThrow[E1 | E2]): U +``` +would also allow throwing `E1` or `E2` inside the method's body but might cause problems when someone tried to call this method +from another method declaring its `CanThrow` capabilities like in the earlier examples. +This is because `CanThrow` has a contravariant type parameter so `CanThrow[E1 | E2]` is a subtype of both `CanThrow[E1]` and `CanThrow[E2]`. +Hence the presence of a given instance of `CanThrow[E1 | E2]` in scope satisfies the requirement for `CanThrow[E1]` and `CanThrow[E2]` +but given instances of `CanThrow[E1]` and `CanThrow[E2]` cannot be combined to provide and instance of `CanThrow[E1 | E2]`. + +**Note 2:** One should keep in mind that `|` binds its left and right arguments more tightly than `throws` so `A | B throws E1 | E2` means `(A | B) throws (Ex1 | Ex2)`, not `A | (B throws E1) | E2`. + +The `CanThrow`/`throws` combo essentially propagates the `CanThrow` requirement outwards. But where are these capabilities created in the first place? That's in the `try` expression. Given a `try` like this: + +```scala +try + body +catch + case ex1: Ex1 => handler1 + ... + case exN: ExN => handlerN +``` +the compiler generates an accumulated capability of type `CanThrow[Ex1 | ... | Ex2]` that is available as a given in the scope of `body`. It does this by augmenting the `try` roughly as follows: +```scala +try + erased given CanThrow[Ex1 | ... | ExN] = compiletime.erasedValue + body +catch ... +``` +Note that the right-hand side of the synthesized given is `???` (undefined). This is OK since +this given is erased; it will not be executed at runtime. + +**Note 1:** The [`saferExceptions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$experimental$$saferExceptions$.html) feature is designed to work only with checked exceptions. An exception type is _checked_ if it is a subtype of +`Exception` but not of `RuntimeException`. The signature of `CanThrow` still admits `RuntimeException`s since `RuntimeException` is a proper subtype of its bound, `Exception`. But no capabilities will be generated for `RuntimeException`s. Furthermore, `throws` clauses +also may not refer to `RuntimeException`s. + +**Note 2:** To keep things simple, the compiler will currently only generate capabilities +for catch clauses of the form +```scala + case ex: Ex => +``` +where `ex` is an arbitrary variable name (`_` is also allowed), and `Ex` is an arbitrary +checked exception type. Constructor patterns such as `Ex(...)` or patterns with guards +are not allowed. The compiler will issue an error if one of these is used to catch +a checked exception and `saferExceptions` is enabled. + +## Example + +That's it. Let's see it in action in an example. First, add an import +```scala +import language.experimental.saferExceptions +``` +to enable exception checking. Now, define an exception `LimitExceeded` and +a function `f` like this: +```scala +val limit = 10e9 +class LimitExceeded extends Exception +def f(x: Double): Double = + if x < limit then x * x else throw LimitExceeded() +``` +You'll get this error message: +``` + if x < limit then x * x else throw LimitExceeded() + ^^^^^^^^^^^^^^^^^^^^^ +The capability to throw exception LimitExceeded is missing. +``` +The capability can be provided by one of the following: + + - Adding a using clause `(using CanThrow[LimitExceeded])` to the definition of the enclosing method + - Adding `throws LimitExceeded` clause after the result type of the enclosing method + - Wrapping this piece of code with a `try` block that catches `LimitExceeded` + +The following import might fix the problem: +```scala + import unsafeExceptions.canThrowAny +``` +As the error message implies, you have to declare that `f` needs the capability to throw a `LimitExceeded` exception. The most concise way to do so is to add a `throws` clause: +```scala +def f(x: Double): Double throws LimitExceeded = + if x < limit then x * x else throw LimitExceeded() +``` +Now put a call to `f` in a `try` that catches `LimitExceeded`: +```scala +@main def test(xs: Double*) = + try println(xs.map(f).sum) + catch case ex: LimitExceeded => println("too large") +``` +Run the program with some inputs: +``` +> scala test 1 2 3 +14.0 +> scala test +0.0 +> scala test 1 2 3 100000000000 +too large +``` +Everything typechecks and works as expected. But wait - we have called `map` without any ceremony! How did that work? Here's how the compiler expands the `test` function: +```scala +// compiler-generated code +@main def test(xs: Double*) = + try + erased given ctl: CanThrow[LimitExceeded] = compiletime.erasedValue + println(xs.map(x => f(x)(using ctl)).sum) + catch case ex: LimitExceeded => println("too large") +``` +The `CanThrow[LimitExceeded]` capability is passed in a synthesized `using` clause to `f`, since `f` requires it. Then the resulting closure is passed to `map`. The signature of `map` does not have to account for effects. It takes a closure as always, but that +closure may refer to capabilities in its free variables. This means that `map` is +already effect polymorphic even though we did not change its signature at all. +So the takeaway is that the effects as capabilities model naturally provides for effect polymorphism whereas this is something that other approaches struggle with. + +## Gradual Typing Via Imports + +Another advantage is that the model allows a gradual migration from current unchecked exceptions to safer exceptions. Imagine for a moment that [`experimental.saferExceptions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$experimental$$saferExceptions$.html) is turned on everywhere. There would be lots of code that breaks since functions have not yet been properly annotated with `throws`. But it's easy to create an escape hatch that lets us ignore the breakages for a while: simply add the import +```scala +import scala.unsafeExceptions.canThrowAny +``` +This will provide the [`CanThrow`](https://scala-lang.org/api/3.x/scala/CanThrow.html) capability for any exception, and thereby allow +all throws and all other calls, no matter what the current state of `throws` declarations is. Here's the +definition of [`canThrowAny`](https://scala-lang.org/api/3.x/scala/unsafeExceptions$.html#canThrowAny-0): +```scala +package scala +object unsafeExceptions: + given canThrowAny: CanThrow[Exception] = ??? +``` +Of course, defining a global capability like this amounts to cheating. But the cheating is useful for gradual typing. The import could be used to migrate existing code, or to +enable more fluid explorations of code without regard for complete exception safety. At the end of these migrations or explorations the import should be removed. + +## Scope Of the Extension + +To summarize, the extension for safer exception checking consists of the following elements: + + - It adds to the standard library the class `scala.CanThrow`, the type `scala.$throws`, and the [`scala.unsafeExceptions`](https://scala-lang.org/api/3.x/scala/unsafeExceptions$.html) object, as they were described above. + - It adds some desugaring rules ro rewrite `throws` types to cascaded `$throws` types. + - It augments the type checking of `throw` by _demanding_ a `CanThrow` capability or the thrown exception. + - It augments the type checking of `try` by _providing_ `CanThrow` capabilities for every caught exception. + +That's all. It's quite remarkable that one can do exception checking in this way without any special additions to the type system. We just need regular givens and context functions. Any runtime overhead is eliminated using `erased`. + +## Caveats + +Our capability model allows to declare and check the thrown exceptions of first-order code. But as it stands, it does not give us enough mechanism to enforce the _absence_ of +capabilities for arguments to higher-order functions. Consider a variant `pureMap` +of `map` that should enforce that its argument does not throw exceptions or have any other effects (maybe because wants to reorder computations transparently). Right now +we cannot enforce that since the function argument to `pureMap` can capture arbitrary +capabilities in its free variables without them showing up in its type. One possible way to +address this would be to introduce a pure function type (maybe written `A -> B`). Pure functions are not allowed to close over capabilities. Then `pureMap` could be written +like this: +```scala + def pureMap(f: A -> B): List[B] +``` +Another area where the lack of purity requirements shows up is when capabilities escape from bounded scopes. Consider the following function +```scala +def escaped(xs: Double*): () => Int = + try () => xs.map(f).sum + catch case ex: LimitExceeded => -1 +``` +With the system presented here, this function typechecks, with expansion +```scala +// compiler-generated code +def escaped(xs: Double*): () => Int = + try + given ctl: CanThrow[LimitExceeded] = ??? + () => xs.map(x => f(x)(using ctl)).sum + catch case ex: LimitExceeded => -1 +``` +But if you try to call `escaped` like this +```scala +val g = escaped(1, 2, 1000000000) +g() +``` +the result will be a `LimitExceeded` exception thrown at the second line where `g` is called. What's missing is that `try` should enforce that the capabilities it generates do not escape as free variables in the result of its body. It makes sense to describe such scoped effects as _ephemeral capabilities_ - they have lifetimes that cannot be extended to delayed code in a lambda. + + +## Outlook + +We are working on a new class of type system that supports ephemeral capabilities by tracking the free variables of values. Once that research matures, it will hopefully be possible to augment the Scala language so that we can enforce the missing properties. + +And it would have many other applications besides: Exceptions are a special case of _algebraic effects_, which has been a very active research area over the last 20 years and is finding its way into programming languages (e.g. [Koka](https://koka-lang.github.io/koka/doc/book.html#why-handlers), [Eff](https://www.eff-lang.org/learn/), [Multicore OCaml](https://discuss.ocaml.org/t/multicore-ocaml-september-2021-effect-handlers-will-be-in-ocaml-5-0/8554), [Unison](https://www.unisonweb.org/docs/language-reference/#abilities-and-ability-handlers)). In fact, algebraic effects have been characterized as being equivalent to exceptions with an additional _resume_ operation. The techniques developed here for exceptions can probably be generalized to other classes of algebraic effects. + +But even without these additional mechanisms, exception checking is already useful as it is. It gives a clear path forward to make code that uses exceptions safer, better documented, and easier to refactor. The only loophole arises for scoped capabilities - here we have to verify manually that these capabilities do not escape. Specifically, a `try` always has to be placed in the same computation stage as the throws that it enables. + +Put another way: If the status quo is 0% static checking since 100% is too painful, then an alternative that gives you 95% static checking with great ergonomics looks like a win. And we might still get to 100% in the future. + +For more info, see also our [paper at the ACM Scala Symposium 2021](https://infoscience.epfl.ch/record/290885). diff --git a/docs/_spec/TODOreference/experimental/cc.md b/docs/_spec/TODOreference/experimental/cc.md new file mode 100644 index 000000000000..878bc0a64ed6 --- /dev/null +++ b/docs/_spec/TODOreference/experimental/cc.md @@ -0,0 +1,738 @@ +--- +layout: doc-page +title: "Capture Checking" +--- + +Capture checking is a research project that modifies the Scala type system to track references to capabilities in values. It can be enabled with a `-Ycc` compiler option. +At present, capture checking is still highly experimental and unstable. + +To get an idea what capture checking can do, let's start with a small example: +```scala +def usingLogFile[T](op: FileOutputStream => T): T = + val logFile = FileOutputStream("log") + val result = op(logFile) + logFile.close() + result +``` +The `usingLogFile` method invokes a given operation with a fresh log file as parameter. Once the operation has ended, the log file is closed and the +operation's result is returned. This is a typical _try-with-resources_ pattern, similar to many other such patterns which are often supported by special language constructs in other languages. + +The problem is that `usingLogFile`'s implementation is not entirely safe. One can +undermine it by passing an operation that performs the logging at some later point +after it has terminated. For instance: +```scala +val later = usingLogFile { file => () => file.write(0) } +later() // crash +``` +When `later` is executed it tries to write to a file that is already closed, which +results in an uncaught `IOException`. + +Capture checking gives us the mechanism to prevent such errors _statically_. To +prevent unsafe usages of `usingLogFile`, we can declare it like this: +```scala +def usingLogFile[T](op: ({*} FileOutputStream) => T): T = + // same body as before +``` +The only thing that's changed is that the `FileOutputStream` parameter of `op` is now +tagged with `{*}`. We'll see that this turns the parameter into a _capability_ whose lifetime is tracked. + +If we now try to define the problematic value `later`, we get a static error: +``` + | val later = usingLogFile { f => () => f.write(0) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |The expression's type {*} () -> Unit is not allowed to capture the root capability `*`. + |This usually means that a capability persists longer than its allowed lifetime. +``` +In this case, it was easy to see that the `logFile` capability escapes in the closure passed to `usingLogFile`. But capture checking also works for more complex cases. +For instance, capture checking is able to distinguish between the following safe code: +```scala +val xs = usingLogFile { f => + List(1, 2, 3).map { x => f.write(x); x * x } +} +``` +and the following unsafe one: +```scala +val xs = usingLogFile { f => + LazyList(1, 2, 3).map { x => f.write(x); x * x } +} +``` +An error would be issued in the second case, but not the first one (this assumes a capture-aware +formulation of `LazyList` which we will present later in this page). + +It turns out that capture checking has very broad applications. Besides the various +try-with-resources patterns, it can also be a key part to the solutions of many other long standing problems in programming languages. Among them: + + - How to have a simple and flexible system for checked exceptions. We show later + how capture checking enables a clean and fully safe system for checked exceptions in Scala. + - How to address the problem of effect polymorphism in general. + - How to solve the "what color is your function?" problem of mixing synchronous + and asynchronous computations. + - How to do region-based allocation, safely, + - How to reason about capabilities associated with memory locations. + +The following sections explain in detail how capture checking works in Scala 3. + + +## Overview + +The capture checker extension introduces a new kind of types and it enforces some rules for working with these types. + +Capture checking is enabled by the compiler option `-Ycc`. If the option is not given, the new +type forms can still be written but they are not checked for consistency, because they are +treated simply as certain uninterpreted annotated types. + +## Capabilities and Capturing Types + +Capture checking is done in terms of _capturing types_ of the form +`{c₁, ..., cᵢ} T`. Here `T` is a type, and `{c₁, ..., cᵢ}` is a _capture set_ consisting of references to capabilities `c₁, ..., cᵢ`. + +A _capability_ is syntactically a method- or class-parameter, a local variable, or the `this` of an enclosing class. The type of a capability +must be a capturing type with a non-empty capture set. We also say that +variables that are capabilities are _tracked_. + +In a sense, every +capability gets its authority from some other, more sweeping capability which it captures. The most sweeping capability, from which ultimately all others are derived is written `*`. We call it the _universal capability_. + +Here is an example: +```scala +class FileSystem + +class Logger(fs: {*} FileSystem): + def log(s: String): Unit = ... // Write to a log file, using `fs` + +def test(fs: {*} FileSystem) = + val l: {fs} Logger = Logger(fs) + l.log("hello world!") + val xs: {l} LazyList[Int] = + LazyList.from(1) + .map { i => + l.log(s"computing elem # $i") + i * i + } + xs +``` +Here, the `test` method takes a `FileSystem` as a parameter. `fs` is a capability since its type has a non-empty capture set. The capability is passed to the `Logger` constructor +and retained as a field in class `Logger`. Hence, the local variable `l` has type +`{fs} Logger`: it is a `Logger` which retains the `fs` capability. + +The second variable defined in `test` is `xs`, a lazy list that is obtained from +`LazyList.from(1)` by logging and mapping consecutive numbers. Since the list is lazy, +it needs to retain the reference to the logger `l` for its computations. Hence, the +type of the list is `{l} LazyList[Int]`. On the other hand, since `xs` only logs but does +not do other file operations, it retains the `fs` capability only indirectly. That's why +`fs` does not show up in the capture set of `xs`. + +Capturing types come with a subtype relation where types with "smaller" capture sets are subtypes of types with larger sets (the _subcapturing_ relation is defined in more detail below). If a type `T` does not have a capture set, it is called _pure_, and is a subtype of +any capturing type that adds a capture set to `T`. + +## Function Types + +The usual function type `A => B` now stands for a function that can capture arbitrary capabilities. We call such functions +_impure_. By contrast, the new single arrow function type `A -> B` stands for a function that cannot capture any capabilities, or otherwise said, is _pure_. One can add a capture set in front of an otherwise pure function. +For instance, `{c, d} A -> B` would be a function that can capture capabilities `c` and `d`, but no others. + +The impure function type `A => B` is treated as an alias for `{*} A -> B`. That is, impure functions are functions that can capture anything. + +Function types and captures both associate to the right, so +```scala +{c} A -> {d} B -> C +``` +is the same as +```scala +{c} (A -> {d} (B -> C)) +``` +Contrast with +```scala +({c} A) -> ({d} B) -> C +``` +which is a curried pure function over argument types that can capture `c` and `d`, respectively. + +Analogous conventions apply to context function types. `A ?=> B` is an impure context function, with `A ?-> B` as its pure complement. + +**Note 1:** The identifiers `->` and `?->` are now treated as soft keywords when used as infix type operators. They are +still available as regular identifiers for terms. For instance, the mapping syntax `Map("x" -> 1, "y" -> 2)` is still supported since it only applies to terms. + +**Note 2:** The distinctions between pure vs impure function types do not apply to methods. In fact, since methods are not values they never capture anything directly. References to +capabilities in a method are instead counted in the capture set of the enclosing object. + +## By-Name Parameter Types + +A convention analogous to function types also extends to by-name parameters. In +```scala +def f(x: => Int): Int +``` +the actual argument can refer to arbitrary capabilities. So the following would be OK: +```scala +f(if p(y) then throw Ex() else 1) +``` +On the other hand, if `f` was defined like this +```scala +def f(x: -> Int): Int +``` +the actual argument to `f` could not refer to any capabilities, so the call above would be rejected. +One can also allow specific capabilities like this: +```scala +def f(x: {c}-> Int): Int +``` +Here, the actual argument to `f` is allowed to use the `c` capability but no others. + +**Note**: It is strongly recommended to write the capability set and the arrow `->` without intervening spaces, +as otherwise the notation would look confusingly like a function type. + +## Subtyping and Subcapturing + +Capturing influences subtyping. As usual we write `T₁ <: T₂` to express that the type +`T₁` is a subtype of the type `T₂`, or equivalently, that `T₁` conforms to `T₂`. An +analogous _subcapturing_ relation applies to capture sets. If `C₁` and `C₂` are capture sets, we write `C₁ <: C₂` to express that `C₁` _is covered by_ `C₂`, or, swapping the operands, that `C₂` _covers_ `C₁`. + +Subtyping extends as follows to capturing types: + + - Pure types are subtypes of capturing types. That is, `T <: C T`, for any type `T`, capturing set `C`. + - For capturing types, smaller capturing sets produce subtypes: `C₁ T₁ <: C₂ T₂` if + `C₁ <: C₂` and `T₁ <: T₂`. + +A subcapturing relation `C₁ <: C₂` holds if `C₂` _accounts for_ every element `c` in `C₁`. This means one of the following three conditions must be true: + + - `c ∈ C₂`, + - `c` refers to a parameter of some class `Cls` and `C₂` contains `Cls.this`, + - `c`'s type has capturing set `C` and `C₂` accounts for every element of `C` (that is, `C <: C₂`). + + +**Example 1.** Given +```scala +fs: {*} FileSystem +ct: {*} CanThrow[Exception] +l : {fs} Logger +``` +we have +``` +{l} <: {fs} <: {*} +{fs} <: {fs, ct} <: {*} +{ct} <: {fs, ct} <: {*} +``` +The set consisting of the root capability `{*}` covers every other capture set. This is +a consequence of the fact that, ultimately, every capability is created from `*`. + +**Example 2.** Consider again the FileSystem/Logger example from before. `LazyList[Int]` is a proper subtype of `{l} LazyList[Int]`. So if the `test` method in that example +was declared with a result type `LazyList[Int]`, we'd get a type error. Here is the error message: +``` +11 |def test(using fs: {*} FileSystem): LazyList[Int] = { + | ^ + | Found: {fs} LazyList[Int] + | Required: LazyList[Int] +``` +Why does it say `{fs} LazyList[Int]` and not `{l} LazyList[Int]`, which is, after all, the type of the returned value `xs`? The reason is that `l` is a local variable in the body of `test`, so it cannot be referred to in a type outside that body. What happens instead is that the type is _widened_ to the smallest supertype that does not mention `l`. Since `l` has capture set `fs`, we have that `{fs}` covers `{l}`, and `{fs}` is acceptable in a result type of `test`, so `{fs}` is the result of that widening. +This widening is called _avoidance_; it is not specific to capture checking but applies to all variable references in Scala types. + +## Capability Classes + +Classes like `CanThrow` or `FileSystem` have the property that their values are always intended to be capabilities. We can make this intention explicit and save boilerplate by declaring these classes with a `@capability` annotation. + +The capture set of a capability class type is always `{*}`. This means we could equivalently express the `FileSystem` and `Logger` classes as follows: +```scala +import annotation.capability + +@capability class FileSystem + +class Logger(using FileSystem): + def log(s: String): Unit = ??? + +def test(using fs: FileSystem) = + val l: {fs} Logger = Logger() + ... +``` +In this version, `FileSystem` is a capability class, which means that the `{*}` capture set is implied on the parameters of `Logger` and `test`. Writing the capture set explicitly produces a warning: +```scala +class Logger(using {*} FileSystem): + ^^^^^^^^^^^^^^ + redundant capture: FileSystem already accounts for * +``` +Another, unrelated change in the version of the last example here is that the `FileSystem` capability is now passed as an implicit parameter. It is quite natural to model capabilities with implicit parameters since it greatly reduces the wiring overhead once multiple capabilities are in play. + +## Capture Checking of Closures + +If a closure refers to capabilities in its body, it captures these capabilities in its type. For instance, consider: +```scala +def test(fs: FileSystem): {fs} String -> Unit = + (x: String) => Logger(fs).log(x) +``` +Here, the body of `test` is a lambda that refers to the capability `fs`, which means that `fs` is retained in the lambda. +Consequently, the type of the lambda is `{fs} String -> Unit`. + +**Note:** Function values are always written with `=>` (or `?=>` for context functions). There is no syntactic +distinction for pure _vs_ impure function values. The distinction is only made in their types. + +A closure also captures all capabilities that are captured by the functions +it calls. For instance, in +```scala +def test(fs: FileSystem) = + def f() = g() + def g() = (x: String) => Logger(fs).log(x) + f +``` +the result of `test` has type `{fs} String -> Unit` even though function `f` itself does not refer to `fs`. + +## Capture Checking of Classes + +The principles for capture checking closures also apply to classes. For instance, consider: +```scala +class Logger(using fs: FileSystem): + def log(s: String): Unit = ... summon[FileSystem] ... + +def test(xfs: FileSystem): {xfs} Logger = + Logger(xfs) +``` +Here, class `Logger` retains the capability `fs` as a (private) field. Hence, the result +of `test` is of type `{xfs} Logger` + +Sometimes, a tracked capability is meant to be used only in the constructor of a class, but +is not intended to be retained as a field. This fact can be communicated to the capture +checker by declaring the parameter as `@constructorOnly`. Example: +```scala +import annotation.constructorOnly + +class NullLogger(using @constructorOnly fs: FileSystem): + ... +def test2(using fs: FileSystem): NullLogger = NullLogger() // OK +``` + +The captured references of a class include _local capabilities_ and _argument capabilities_. Local capabilities are capabilities defined outside the class and referenced from its body. Argument capabilities are passed as parameters to the primary constructor of the class. Local capabilities are inherited: +the local capabilities of a superclass are also local capabilities of its subclasses. Example: + +```scala +@capability class Cap + +def test(a: Cap, b: Cap, c: Cap) = + class Super(y: Cap): + def f = a + class Sub(x: Cap) extends Super(x) + def g = b + Sub(c) +``` +Here class `Super` has local capability `a`, which gets inherited by class +`Sub` and is combined with `Sub`'s own local capability `b`. Class `Sub` also has an argument capability corresponding to its parameter `x`. This capability gets instantiated to `c` in the final constructor call `Sub(c)`. Hence, +the capture set of that call is `{a, b, c}`. + +The capture set of the type of `this` of a class is inferred by the capture checker, unless the type is explicitly declared with a self type annotation like this one: +```scala +class C: + self: {a, b} D => ... +``` +The inference observes the following constraints: + + - The type of `this` of a class `C` includes all captured references of `C`. + - The type of `this` of a class `C` is a subtype of the type of `this` + of each parent class of `C`. + - The type of `this` must observe all constraints where `this` is used. + +For instance, in +```scala +@capability class Cap +def test(c: Cap) = + class A: + val x: A = this + def f = println(c) // error +``` +we know that the type of `this` must be pure, since `this` is the right hand side of a `val` with type `A`. However, in the last line we find that the capture set of the class, and with it the capture set of `this`, would include `c`. This leads to a contradiction, and hence to a checking error: +``` +16 | def f = println(c) // error + | ^ + |(c : Cap) cannot be referenced here; it is not included in the allowed capture set {} +``` + +## Capture Tunnelling + +Consider the following simple definition of a `Pair` class: +```scala +class Pair[+A, +B](x: A, y: B): + def fst: A = x + def snd: B = y +``` +What happens if `Pair` is instantiated like this (assuming `ct` and `fs` are two capabilities in scope)? +```scala +def x: {ct} Int -> String +def y: {fs} Logger +def p = Pair(x, y) +``` +The last line will be typed as follows: +```scala +def p: Pair[{ct} Int -> String, {fs} Logger] = Pair(x, y) +``` +This might seem surprising. The `Pair(x, y)` value does capture capabilities `ct` and `fs`. Why don't they show up in its type at the outside? + +The answer is capture tunnelling. Once a type variable is instantiated to a capturing type, the +capture is not propagated beyond this point. On the other hand, if the type variable is instantiated +again on access, the capture information "pops out" again. For instance, even though `p` is technically pure because its capture set is empty, writing `p.fst` would record a reference to the captured capability `ct`. So if this access was put in a closure, the capability would again form part of the outer capture set. E.g. +```scala +() => p.fst : {ct} () -> {ct} Int -> String +``` +In other words, references to capabilities "tunnel through" in generic instantiations from creation to access; they do not affect the capture set of the enclosing generic data constructor applications. +This principle plays an important part in making capture checking concise and practical. + +## Escape Checking + +The universal capability `*` should be conceptually available only as a parameter to the main program. Indeed, if it was available everywhere, capability checking would be undermined since one could mint new capabilities +at will. In line with this reasoning, some capture sets are restricted so that +they are not allowed to contain the universal capability. + +Specifically, if a capturing type is an instance of a type variable, that capturing type +is not allowed to carry the universal capability `{*}`. There's a connection to tunnelling here. +The capture set of a type has to be present in the environment when a type is instantiated from +a type variable. But `*` is not itself available as a global entity in the environment. Hence, +an error should result. + +We can now reconstruct how this principle produced the error in the introductory example, where +`usingLogFile` was declared like this: +```scala +def usingLogFile[T](op: ({*} FileOutputStream) => T): T = ... +``` +The error message was: +``` + | val later = usingLogFile { f => () => f.write(0) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |The expression's type {*} () -> Unit is not allowed to capture the root capability `*`. + |This usually means that a capability persists longer than its allowed lifetime. +``` +This error message was produced by the following logic: + + - The `f` parameter has type `{*} FileOutputStream`, which makes it a capability. + - Therefore, the type of the expression `() => f.write(0)` is `{f} () -> Unit`. + - This makes the type of the whole closure passed to `usingLogFile` the dependent function type + `(f: {*} FileOutputStream) -> {f} () -> Unit`. + - The expected type of the closure is a simple, parametric, impure function type `({*} FileOutputStream) => T`, + for some instantiation of the type variable `T`. + - The smallest supertype of the closure's dependent function type that is a parametric function type is + `({*} FileOutputStream) => {*} () -> Unit` + - Hence, the type variable `T` is instantiated to `* () -> Unit`, which causes the error. + +An analogous restriction applies to the type of a mutable variable. +Another way one could try to undermine capture checking would be to +assign a closure with a local capability to a global variable. Maybe +like this: +```scala +var loophole: {*} () -> Unit = () => () +usingLogFile { f => + loophole = () => f.write(0) +} +loophole() +``` +But this will not compile either, since mutable variables cannot have universal capture sets. + +One also needs to prevent returning or assigning a closure with a local capability in an argument of a parametric type. For instance, here is a +slightly more refined attack: +```scala +class Cell[+A](x: A) +val sneaky = usingLogFile { f => Cell(() => f.write(0)) } +sneaky.x() +``` +At the point where the `Cell` is created, the capture set of the argument is `f`, which +is OK. But at the point of use, it is `*` (because `f` is no longer in scope), which causes again an error: +``` + | sneaky.x() + | ^^^^^^^^ + |The expression's type {*} () -> Unit is not allowed to capture the root capability `*`. + |This usually means that a capability persists longer than its allowed lifetime. +``` + +Looking at object graphs, we observe a monotonicity property: The capture set of an object `x` covers the capture sets of all objects reachable through `x`. This property is reflected in the type system by the following _monotonicity rule_: + + - In a class `C` with a field `f`, the capture set `{this}` covers the capture set `{this.f}` as well as the capture set of any application of `this.f` to pure arguments. + +## Checked Exceptions + +Scala enables checked exceptions through a language import. Here is an example, +taken from the [safer exceptions page](./canthrow.md), and also described in a +[paper](https://infoscience.epfl.ch/record/290885) presented at the + 2021 Scala Symposium. +```scala +import language.experimental.saferExceptions + +class LimitExceeded extends Exception + +val limit = 10e+10 +def f(x: Double): Double throws LimitExceeded = + if x < limit then x * x else throw LimitExceeded() +``` +The new `throws` clause expands into an implicit parameter that provides +a `CanThrow` capability. Hence, function `f` could equivalently be written +like this: +```scala +def f(x: Double)(using CanThrow[LimitExceeded]): Double = ... +``` +If the implicit parameter is missing, an error is reported. For instance, the function definition +```scala +def g(x: Double): Double = + if x < limit then x * x else throw LimitExceeded() +``` +is rejected with this error message: +``` + | if x < limit then x * x else throw LimitExceeded() + | ^^^^^^^^^^^^^^^^^^^^^ + |The capability to throw exception LimitExceeded is missing. + |The capability can be provided by one of the following: + | - Adding a using clause `(using CanThrow[LimitExceeded])` to the definition of the enclosing method + | - Adding `throws LimitExceeded` clause after the result type of the enclosing method + | - Wrapping this piece of code with a `try` block that catches LimitExceeded +``` +`CanThrow` capabilities are required by `throw` expressions and are created +by `try` expressions. For instance, the expression +```scala +try xs.map(f).sum +catch case ex: LimitExceeded => -1 +``` +would be expanded by the compiler to something like the following: +```scala +try + erased given ctl: CanThrow[LimitExceeded] = compiletime.erasedValue + xs.map(f).sum +catch case ex: LimitExceeded => -1 +``` +(The `ctl` capability is only used for type checking but need not show up in the generated code, so it can be declared as +erased.) + +As with other capability based schemes, one needs to guard against capabilities +that are captured in results. For instance, here is a problematic use case: +```scala +def escaped(xs: Double*): (() => Double) throws LimitExceeded = + try () => xs.map(f).sum + catch case ex: LimitExceeded => () => -1 +val crasher = escaped(1, 2, 10e+11) +crasher() +``` +This code needs to be rejected since otherwise the call to `crasher()` would cause +an unhandled `LimitExceeded` exception to be thrown. + +Under `-Ycc`, the code is indeed rejected +``` +14 | try () => xs.map(f).sum + | ^ + |The expression's type {*} () -> Double is not allowed to capture the root capability `*`. + |This usually means that a capability persists longer than its allowed lifetime. +15 | catch case ex: LimitExceeded => () => -1 +``` +To integrate exception and capture checking, only two changes are needed: + + - `CanThrow` is declared as a `@capability` class, so all references to `CanThrow` instances are tracked. + - Escape checking is extended to `try` expressions. The result type of a `try` is not allowed to + capture the universal capability. + +## A Larger Example + +As a larger example, we present an implementation of lazy lists and some use cases. For simplicity, +our lists are lazy only in their tail part. This corresponds to what the Scala-2 type `Stream` did, whereas Scala 3's `LazyList` type computes strictly less since it is also lazy in the first argument. + +Here is the base trait `LzyList` for our version of lazy lists: +```scala +trait LzyList[+A]: + def isEmpty: Boolean + def head: A + def tail: {this} LzyList[A] +``` +Note that `tail` carries a capture annotation. It says that the tail of a lazy list can +potentially capture the same references as the lazy list as a whole. + +The empty case of a `LzyList` is written as usual: +```scala +object LzyNil extends LzyList[Nothing]: + def isEmpty = true + def head = ??? + def tail = ??? +``` +Here is a formulation of the class for lazy cons nodes: +```scala +import scala.compiletime.uninitialized + +final class LzyCons[+A](hd: A, tl: () => {*} LzyList[A]) extends LzyList[A]: + private var forced = false + private var cache: {this} LzyList[A] = uninitialized + private def force = + if !forced then { cache = tl(); forced = true } + cache + + def isEmpty = false + def head = hd + def tail: {this} LzyList[A] = force +end LzyCons +``` +The `LzyCons` class takes two parameters: A head `hd` and a tail `tl`, which is a function +returning a `LzyList`. Both the function and its result can capture arbitrary capabilities. +The result of applying the function is memoized after the first dereference of `tail` in +the private mutable field `cache`. Note that the typing of the assignment `cache = tl()` relies on the monotonicity rule for `{this}` capture sets. + +Here is an extension method to define an infix cons operator `#:` for lazy lists. It is analogous +to `::` but instead of a strict list it produces a lazy list without evaluating its right operand. +```scala +extension [A](x: A) + def #:(xs1: => {*} LzyList[A]): {xs1} LzyList[A] = + LzyCons(x, () => xs1) +``` +Note that `#:` takes an impure call-by-name parameter `xs1` as its right argument. The result +of `#:` is a lazy list that captures that argument. + +As an example usage of `#:`, here is a method `tabulate` that creates a lazy list +of given length with a generator function `gen`. The generator function is allowed +to have side effects. +```scala +def tabulate[A](n: Int)(gen: Int => A) = + def recur(i: Int): {gen} LzyList[A] = + if i == n then LzyNil + else gen(i) #: recur(i + 1) + recur(0) +``` +Here is a use of `tabulate`: +```scala +class LimitExceeded extends Exception +def squares(n: Int)(using ct: CanThrow[LimitExceeded]) = + tabulate(10) { i => + if i > 9 then throw LimitExceeded() + i * i + } +``` +The inferred result type of `squares` is `{ct} LzyList[Int]`, i.e it is a lazy list of +`Int`s that can throw the `LimitExceeded` exception when it is elaborated by calling `tail` +one or more times. + +Here are some further extension methods for mapping, filtering, and concatenating lazy lists: +```scala +extension [A](xs: {*} LzyList[A]) + def map[B](f: A => B): {xs, f} LzyList[B] = + if xs.isEmpty then LzyNil + else f(xs.head) #: xs.tail.map(f) + + def filter(p: A => Boolean): {xs, p} LzyList[A] = + if xs.isEmpty then LzyNil + else if p(xs.head) then xs.head #: xs.tail.filter(p) + else xs.tail.filter(p) + + def concat(ys: {*} LzyList[A]): {xs, ys} LzyList[A] = + if xs.isEmpty then ys + else xs.head #: xs.tail.concat(ys) + + def drop(n: Int): {xs} LzyList[A] = + if n == 0 then xs else xs.tail.drop(n - 1) +``` +Their capture annotations are all as one would expect: + + - Mapping a lazy list produces a lazy list that captures the original list as well + as the (possibly impure) mapping function. + - Filtering a lazy list produces a lazy list that captures the original list as well + as the (possibly impure) filtering predicate. + - Concatenating two lazy lists produces a lazy list that captures both arguments. + - Dropping elements from a lazy list gives a safe approximation where the original list is captured in the result. In fact, it's only some suffix of the list that is retained at run time, but our modelling identifies lazy lists and their suffixes, so this additional knowledge would not be useful. + +Of course the function passed to `map` or `filter` could also be pure. After all, `A -> B` is a subtype of `{*} A -> B` which is the same as `A => B`. In that case, the pure function +argument will _not_ show up in the result type of `map` or `filter`. For instance: +```scala +val xs = squares(10) +val ys: {xs} LzyList[Int] = xs.map(_ + 1) +``` +The type of the mapped list `ys` has only `xs` in its capture set. The actual function +argument does not show up since it is pure. Likewise, if the lazy list +`xs` was pure, it would not show up in any of the method results. +This demonstrates that capability-based +effect systems with capture checking are naturally _effect polymorphic_. + +This concludes our example. It's worth mentioning that an equivalent program defining and using standard, strict lists would require no capture annotations whatsoever. It would compile exactly as written now in standard Scala 3, yet one gets the capture checking for free. Essentially, `=>` already means "can capture anything" and since in a strict list side effecting operations are not retained in the result, there are no additional captures to record. A strict list could of course capture side-effecting closures in its elements but then tunnelling applies, since +these elements are represented by a type variable. This means we don't need to annotate anything there either. + +Another possibility would be a variant of lazy lists that requires all functions passed to `map`, `filter` and other operations like it to be pure. E.g. `map` on such a list would be defined like this: +```scala +extension [A](xs: LzyList[A]) + def map[B](f: A -> B): LzyList[B] = ... +``` +That variant would not require any capture annotations either. + +To summarize, there are two "sweet spots" of data structure design: strict lists in +side-effecting or resource-aware code and lazy lists in purely functional code. +Both are already correctly capture-typed without requiring any explicit annotations. Capture annotations only come into play where the semantics gets more complicated because we deal with delayed effects such as in impure lazy lists or side-effecting iterators over strict lists. This property is probably one of the greatest plus points of our approach to capture checking compared to previous techniques which tend to be more noisy. + +## Function Type Shorthands + +TBD + +## Compilation Options + +The following options are relevant for capture checking. + + - **-Ycc** Enables capture checking. + - **-Xprint:cc** Prints the program with capturing types as inferred by capture checking. + - **-Ycc-debug** Gives more detailed, implementation-oriented information about capture checking, as described in the next section. + + The implementation supporting capture checking with these options is currently in branch `cc-experiment` on dotty.epfl.ch. + +## Capture Checking Internals + +The capture checker is architected as a propagation constraint solver, which runs as a separate phase after type-checking and some initial transformations. + +Constraint variables stand for unknown capture sets. A constraint variable is introduced + + - for every part of a previously inferred type, + - for the accessed references of every method, class, anonymous function, or by-name argument, + - for the parameters passed in a class constructor call. + +Capture sets in explicitly written types are treated as constants (before capture checking, such sets are simply ignored). + +The capture checker essentially rechecks the program with the usual typing rules. Every time a subtype requirement between capturing types is checked, this translates to a subcapturing test on capture sets. If the two sets are constant, this is simply a yes/no question, where a no will produce an error message. + +If the lower set `C₁` of a comparison `C₁ <: C₂` is a variable, the set `C₂` is recorded +as a _superset_ of `C₁`. If the upper set `C₂` is a variable, the elements of `C₁` are _propagated_ to `C₂`. Propagation of an element `x` to a set `C` means that `x` is included as an element in `C`, and it is also propagated +to all known supersets of `C`. If such a superset is a constant, it is checked that `x` is included in it. If that's not the case, the original comparison `C₁ <: C₂` has no solution and an error is reported. + +The type checker also performs various maps on types, for instance when substituting actual argument types for formal parameter types in dependent functions, or mapping +member types with "as-seen-from" in a selection. Maps keep track of the variance +of positions in a type. The variance is initially covariant, it flips to +contravariant in function parameter positions, and can be either covariant, +contravariant, or nonvariant in type arguments, depending on the variance of +the type parameter. + +When capture checking, the same maps are also performed on capture sets. If a capture set is a constant, its elements (which are capabilities) are mapped as regular types. If the result of such a map is not a capability, the result is approximated according to the variance of the type. A covariant approximation replaces a type by its capture set. +A contravariant approximation replaces it with the empty capture set. A nonvariant +approximation replaces the enclosing capturing type with a range of possible types +that gets propagated and resolved further out. + +When a mapping `m` is performed on a capture set variable `C`, a new variable `Cm` is created that contains the mapped elements and that is linked with `C`. If `C` subsequently acquires further elements through propagation, these are also propagated to `Cm` after being transformed by the `m` mapping. `Cm` also gets the same supersets as `C`, mapped again using `m`. + +One interesting aspect of the capture checker concerns the implementation of capture tunnelling. The [foundational theory](https://infoscience.epfl.ch/record/290885) on which capture checking is based makes tunnelling explicit through so-called _box_ and +_unbox_ operations. Boxing hides a capture set and unboxing recovers it. The capture checker inserts virtual box and unbox operations based on actual and expected types similar to the way the type checker inserts implicit conversions. When capture set variables are first introduced, any capture set in a capturing type that is an instance of a type parameter instance is marked as "boxed". A boxing operation is +inserted if the expected type of an expression is a capturing type with +a boxed capture set variable. The effect of the insertion is that any references +to capabilities in the boxed expression are forgotten, which means that capture +propagation is stopped. Dually, if the actual type of an expression has +a boxed variable as capture set, an unbox operation is inserted, which adds all +elements of the capture set to the environment. + +Boxing and unboxing has no runtime effect, so the insertion of these operations is only simulated; the only visible effect is the retraction and insertion +of variables in the capture sets representing the environment of the currently checked expression. + +The `-Ycc-debug` option provides some insight into the workings of the capture checker. +When it is turned on, boxed sets are marked explicitly and capture set variables are printed with an ID and some information about their provenance. For instance, the string `{f, xs}33M5V` indicates a capture set +variable that is known to hold elements `f` and `xs`. The variable's ID is `33`. The `M` +indicates that the variable was created through a mapping from a variable with ID `5`. The latter is a regular variable, as indicated + by `V`. + +Generally, the string following the capture set consists of alternating numbers and letters where each number gives a variable ID and each letter gives the provenance of the variable. Possible letters are + + - `V` : a regular variable, + - `M` : a variable resulting from a _mapping_ of the variable indicated by the string to the right, + - `B` : similar to `M` but where the mapping is a _bijection_, + - `F` : a variable resulting from _filtering_ the elements of the variable indicated by the string to the right, + - `I` : a variable resulting from an _intersection_ of two capture sets, + - `D` : a variable resulting from the set _difference_ of two capture sets. + +At the end of a compilation run, `-Ycc-debug` will print all variable dependencies of variables referred to in previous output. Here is an example: +``` +Capture set dependencies: + {}2V :: + {}3V :: + {}4V :: + {f, xs}5V :: {f, xs}31M5V, {f, xs}32M5V + {f, xs}31M5V :: {xs, f} + {f, xs}32M5V :: +``` +This section lists all variables that appeared in previous diagnostics and their dependencies, recursively. For instance, we learn that + + - variables 2, 3, 4 are empty and have no dependencies, + - variable `5` has two dependencies: variables `31` and `32` which both result from mapping variable `5`, + - variable `31` has a constant fixed superset `{xs, f}` + - variable `32` has no dependencies. + diff --git a/docs/_spec/TODOreference/experimental/erased-defs-spec.md b/docs/_spec/TODOreference/experimental/erased-defs-spec.md new file mode 100644 index 000000000000..5395a8468399 --- /dev/null +++ b/docs/_spec/TODOreference/experimental/erased-defs-spec.md @@ -0,0 +1,64 @@ +--- +layout: doc-page +title: "Erased Definitions - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/erased-defs-spec.html +--- + +TODO: complete +## Rules + +1. `erased` is a soft modifier. It can appear: + * At the start of a parameter block of a method, function or class + * In a method definition + * In a `val` definition (but not `lazy val` or `var`) + * In a `class` or `trait` definition + + ```scala + erased val x = ... + erased def f = ... + + def g(erased x: Int) = ... + + (erased x: Int) => ... + def h(x: (erased Int) => Int) = ... + + class K(erased x: Int) { ... } + erased class E {} + ``` + + +2. A reference to an `erased` val or def can only be used + * Inside the expression of argument to an `erased` parameter + * Inside the body of an `erased` `val` or `def` + + +3. Functions + * `(erased x1: T1, x2: T2, ..., xN: TN) => y : (erased T1, T2, ..., TN) => R` + * `(given erased x1: T1, x2: T2, ..., xN: TN) => y: (given erased T1, T2, ..., TN) => R` + * `(given erased T1) => R <:< erased T1 => R` + * `(given erased T1, T2) => R <:< (erased T1, T2) => R` + * ... + + Note that there is no subtype relation between `(erased T) => R` and `T => R` (or `(given erased T) => R` and `(given T) => R`) + + +4. Eta expansion + + if `def f(erased x: T): U` then `f: (erased T) => U`. + + +5. Erasure semantics + * All `erased` parameters are removed from the function + * All argument to `erased` parameters are not passed to the function + * All `erased` definitions are removed + * All `(erased T1, T2, ..., TN) => R` and `(given erased T1, T2, ..., TN) => R` become `() => R` + + +6. Overloading + + Method with `erased` parameters will follow the normal overloading constraints after erasure. + + +7. Overriding + * Member definitions overriding each other must both be `erased` or not be `erased` + * `def foo(x: T): U` cannot be overridden by `def foo(erased x: T): U` and vice-versa diff --git a/docs/_spec/TODOreference/experimental/erased-defs.md b/docs/_spec/TODOreference/experimental/erased-defs.md new file mode 100644 index 000000000000..28455f26cdc0 --- /dev/null +++ b/docs/_spec/TODOreference/experimental/erased-defs.md @@ -0,0 +1,231 @@ +--- +layout: doc-page +title: "Erased Definitions" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/erased-defs.html +--- + +`erased` is a modifier that expresses that some definition or expression is erased by the compiler instead of being represented in the compiled output. It is not yet part of the Scala language standard. To enable `erased`, turn on the language feature +[`experimental.erasedDefinitions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$experimental$$erasedDefinitions$.html). This can be done with a language import +```scala +import scala.language.experimental.erasedDefinitions +``` +or by setting the command line option `-language:experimental.erasedDefinitions`. +Erased definitions must be in an experimental scope (see [Experimental definitions](../other-new-features/experimental-defs.md)). + +## Why erased terms? + +Let's describe the motivation behind erased terms with an example. In the +following we show a simple state machine which can be in a state `On` or `Off`. +The machine can change state from `Off` to `On` with `turnedOn` only if it is +currently `Off`. This last constraint is captured with the `IsOff[S]` contextual +evidence which only exists for `IsOff[Off]`. For example, not allowing calling +`turnedOn` on in an `On` state as we would require an evidence of type +`IsOff[On]` that will not be found. + +```scala +sealed trait State +final class On extends State +final class Off extends State + +@implicitNotFound("State must be Off") +class IsOff[S <: State] +object IsOff: + given isOff: IsOff[Off] = new IsOff[Off] + +class Machine[S <: State]: + def turnedOn(using IsOff[S]): Machine[On] = new Machine[On] + +val m = new Machine[Off] +m.turnedOn +m.turnedOn.turnedOn // ERROR +// ^ +// State must be Off +``` + +Note that in the code above the actual context arguments for `IsOff` are never +used at runtime; they serve only to establish the right constraints at compile +time. As these terms are never used at runtime there is not real need to have +them around, but they still need to be present in some form in the generated +code to be able to do separate compilation and retain binary compatibility. We +introduce _erased terms_ to overcome this limitation: we are able to enforce the +right constrains on terms at compile time. These terms have no run time +semantics and they are completely erased. + +## How to define erased terms? + +Parameters of methods and functions can be declared as erased, placing `erased` +in front of a parameter list (like `given`). + +```scala +def methodWithErasedEv(erased ev: Ev): Int = 42 + +val lambdaWithErasedEv: erased Ev => Int = + (erased ev: Ev) => 42 +``` + +`erased` parameters will not be usable for computations, though they can be used +as arguments to other `erased` parameters. + +```scala +def methodWithErasedInt1(erased i: Int): Int = + i + 42 // ERROR: can not use i + +def methodWithErasedInt2(erased i: Int): Int = + methodWithErasedInt1(i) // OK +``` + +Not only parameters can be marked as erased, `val` and `def` can also be marked +with `erased`. These will also only be usable as arguments to `erased` +parameters. + +```scala +erased val erasedEvidence: Ev = ... +methodWithErasedEv(erasedEvidence) +``` + +## What happens with erased values at runtime? + +As `erased` are guaranteed not to be used in computations, they can and will be +erased. + +```scala +// becomes def methodWithErasedEv(): Int at runtime +def methodWithErasedEv(erased ev: Ev): Int = ... + +def evidence1: Ev = ... +erased def erasedEvidence2: Ev = ... // does not exist at runtime +erased val erasedEvidence3: Ev = ... // does not exist at runtime + +// evidence1 is not evaluated and no value is passed to methodWithErasedEv +methodWithErasedEv(evidence1) +``` + +## State machine with erased evidence example + +The following example is an extended implementation of a simple state machine +which can be in a state `On` or `Off`. The machine can change state from `Off` +to `On` with `turnedOn` only if it is currently `Off`, conversely from `On` to +`Off` with `turnedOff` only if it is currently `On`. These last constraint are +captured with the `IsOff[S]` and `IsOn[S]` given evidence only exist for +`IsOff[Off]` and `IsOn[On]`. For example, not allowing calling `turnedOff` on in +an `Off` state as we would require an evidence `IsOn[Off]` that will not be +found. + +As the given evidences of `turnedOn` and `turnedOff` are not used in the +bodies of those functions we can mark them as `erased`. This will remove the +evidence parameters at runtime, but we would still evaluate the `isOn` and +`isOff` givens that were found as arguments. As `isOn` and `isOff` are not +used except as `erased` arguments, we can mark them as `erased`, hence removing +the evaluation of the `isOn` and `isOff` evidences. + +```scala +import scala.annotation.implicitNotFound + +sealed trait State +final class On extends State +final class Off extends State + +@implicitNotFound("State must be Off") +class IsOff[S <: State] +object IsOff: + // will not be called at runtime for turnedOn, the + // compiler will only require that this evidence exists + given IsOff[Off] = new IsOff[Off] + +@implicitNotFound("State must be On") +class IsOn[S <: State] +object IsOn: + // will not exist at runtime, the compiler will only + // require that this evidence exists at compile time + erased given IsOn[On] = new IsOn[On] + +class Machine[S <: State] private (): + // ev will disappear from both functions + def turnedOn(using erased ev: IsOff[S]): Machine[On] = new Machine[On] + def turnedOff(using erased ev: IsOn[S]): Machine[Off] = new Machine[Off] + +object Machine: + def newMachine(): Machine[Off] = new Machine[Off] + +@main def test = + val m = Machine.newMachine() + m.turnedOn + m.turnedOn.turnedOff + + // m.turnedOff + // ^ + // State must be On + + // m.turnedOn.turnedOn + // ^ + // State must be Off +``` + +Note that in [Inline](../metaprogramming/inline.md) we discussed `erasedValue` and inline +matches. `erasedValue` is implemented with `erased`, so the state machine above +can be encoded as follows: + +```scala +import scala.compiletime.* + +sealed trait State +final class On extends State +final class Off extends State + +class Machine[S <: State]: + transparent inline def turnOn(): Machine[On] = + inline erasedValue[S] match + case _: Off => new Machine[On] + case _: On => error("Turning on an already turned on machine") + + transparent inline def turnOff(): Machine[Off] = + inline erasedValue[S] match + case _: On => new Machine[Off] + case _: Off => error("Turning off an already turned off machine") + +object Machine: + def newMachine(): Machine[Off] = + println("newMachine") + new Machine[Off] +end Machine + +@main def test = + val m = Machine.newMachine() + m.turnOn() + m.turnOn().turnOff() + m.turnOn().turnOn() // error: Turning on an already turned on machine +``` + +## Erased Classes + +`erased` can also be used as a modifier for a class. An erased class is intended to be used only in erased definitions. If the type of a val definition or parameter is +a (possibly aliased, refined, or instantiated) erased class, the definition is assumed to be `erased` itself. Likewise, a method with an erased class return type is assumed to be `erased` itself. Since given instances expand to vals and defs, they are also assumed to be erased if the type they produce is an erased class. Finally +function types with erased classes as arguments turn into erased function types. + +Example: +```scala +erased class CanRead + +val x: CanRead = ... // `x` is turned into an erased val +val y: CanRead => Int = ... // the function is turned into an erased function +def f(x: CanRead) = ... // `f` takes an erased parameter +def g(): CanRead = ... // `g` is turned into an erased def +given CanRead = ... // the anonymous given is assumed to be erased +``` +The code above expands to +```scala +erased class CanRead + +erased val x: CanRead = ... +val y: (erased CanRead) => Int = ... +def f(erased x: CanRead) = ... +erased def g(): CanRead = ... +erased given CanRead = ... +``` +After erasure, it is checked that no references to values of erased classes remain and that no instances of erased classes are created. So the following would be an error: +```scala +val err: Any = CanRead() // error: illegal reference to erased class CanRead +``` +Here, the type of `err` is `Any`, so `err` is not considered erased. Yet its initializing value is a reference to the erased class `CanRead`. + +[More Details](./erased-defs-spec.md) diff --git a/docs/_spec/TODOreference/experimental/explicit-nulls.md b/docs/_spec/TODOreference/experimental/explicit-nulls.md new file mode 100644 index 000000000000..b3fa53429cfe --- /dev/null +++ b/docs/_spec/TODOreference/experimental/explicit-nulls.md @@ -0,0 +1,543 @@ +--- +layout: doc-page +title: "Explicit Nulls" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/explicit-nulls.html +--- + +Explicit nulls is an opt-in feature that modifies the Scala type system, which makes reference types +(anything that extends [`AnyRef`](https://scala-lang.org/api/3.x/scala/AnyRef.html)) _non-nullable_. + +This means the following code will no longer typecheck: + +```scala +val x: String = null // error: found `Null`, but required `String` +``` + +Instead, to mark a type as nullable we use a [union type](../new-types/union-types.md) + +```scala +val x: String | Null = null // ok +``` + +A nullable type could have null value during runtime; hence, it is not safe to select a member without checking its nullity. + +```scala +x.trim // error: trim is not member of String | Null +``` + +Explicit nulls are enabled via a `-Yexplicit-nulls` flag. + +Read on for details. + +## New Type Hierarchy + +Originally, `Null` is a subtype of all reference types. + +!["Original Type Hierarchy"](images/explicit-nulls/scalaHierarchyWithMatchable.png) + +When explicit nulls is enabled, the type hierarchy changes so that `Null` is only +a subtype of `Any` and `Matchable`, as opposed to every reference type, +which means `null` is no longer a value of `AnyRef` and its subtypes. + +This is the new type hierarchy: + +!["Type Hierarchy for Explicit Nulls"](images/explicit-nulls/scalaHierarchyWithMatchableAndSafeNull.png) + +After erasure, `Null` remains a subtype of all reference types (as forced by the JVM). + +## Working with `Null` + +To make working with nullable values easier, we propose adding a few utilities to the standard library. +So far, we have found the following useful: + +- An extension method `.nn` to "cast away" nullability + + ```scala + extension [T](x: T | Null) + inline def nn: T = + assert(x != null) + x.asInstanceOf[T] + ``` + + This means that given `x: String | Null`, `x.nn` has type `String`, so we can call all the + usual methods on it. Of course, `x.nn` will throw a NPE if `x` is `null`. + + Don't use `.nn` on mutable variables directly, because it may introduce an unknown type into the type of the variable. + +- An [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html) language feature. + + When imported, `T | Null` can be used as `T`, similar to regular Scala (without explicit nulls). + + See [UnsafeNulls](#unsafenulls) section for more details. + +## Unsoundness + +The new type system is unsound with respect to `null`. This means there are still instances where an expression has a non-nullable type like `String`, but its value is actually `null`. + +The unsoundness happens because uninitialized fields in a class start out as `null`: + +```scala +class C: + val f: String = foo(f) + def foo(f2: String): String = f2 + +val c = new C() +// c.f == "field is null" +``` + +The unsoundness above can be caught by the compiler with the option `-Ysafe-init`. +More details can be found in [safe initialization](../other-new-features/safe-initialization.md). + +## Equality + +We don't allow the double-equal (`==` and `!=`) and reference (`eq` and `ne`) comparison between +`AnyRef` and `Null` anymore, since a variable with a non-nullable type cannot have `null` as value. +`null` can only be compared with `Null`, nullable union (`T | Null`), or `Any` type. + +For some reason, if we really want to compare `null` with non-null values, we have to provide a type hint (e.g. `: Any`). + +```scala +val x: String = ??? +val y: String | Null = ??? + +x == null // error: Values of types String and Null cannot be compared with == or != +x eq null // error +"hello" == null // error + +y == null // ok +y == x // ok + +(x: String | Null) == null // ok +(x: Any) == null // ok +``` + +## Java Interoperability + +The Scala compiler can load Java classes in two ways: from source or from bytecode. In either case, +when a Java class is loaded, we "patch" the type of its members to reflect that Java types +remain implicitly nullable. + +Specifically, we patch + +- the type of fields + +- the argument type and return type of methods + +We illustrate the rules with following examples: + +- The first two rules are easy: we nullify reference types but not value types. + + ```java + class C { + String s; + int x; + } + ``` + + ==> + + ```scala + class C: + val s: String | Null + val x: Int + ``` + +- We nullify type parameters because in Java a type parameter is always nullable, so the following code compiles. + + ```java + class C { T foo() { return null; } } + ``` + + ==> + + ```scala + class C[T] { def foo(): T | Null } + ``` + + Notice this is rule is sometimes too conservative, as witnessed by + + ```scala + class InScala: + val c: C[Bool] = ??? // C as above + val b: Bool = c.foo() // no longer typechecks, since foo now returns Bool | Null + ``` + +- We can reduce the number of redundant nullable types we need to add. Consider + + ```java + class Box { T get(); } + class BoxFactory { Box makeBox(); } + ``` + + ==> + + ```scala + class Box[T] { def get(): T | Null } + class BoxFactory[T] { def makeBox(): Box[T] | Null } + ``` + + Suppose we have a `BoxFactory[String]`. Notice that calling `makeBox()` on it returns a + `Box[String] | Null`, not a `Box[String | Null] | Null`. This seems at first + glance unsound ("What if the box itself has `null` inside?"), but is sound because calling + `get()` on a `Box[String]` returns a `String | Null`. + + Notice that we need to patch _all_ Java-defined classes that transitively appear in the + argument or return type of a field or method accessible from the Scala code being compiled. + Absent crazy reflection magic, we think that all such Java classes _must_ be visible to + the Typer in the first place, so they will be patched. + +- We will append `Null` to the type arguments if the generic class is defined in Scala. + + ```java + class BoxFactory { + Box makeBox(); // Box is Scala-defined + List>> makeCrazyBoxes(); // List is Java-defined + } + ``` + + ==> + + ```scala + class BoxFactory[T]: + def makeBox(): Box[T | Null] | Null + def makeCrazyBoxes(): java.util.List[Box[java.util.List[T] | Null]] | Null + ``` + + In this case, since `Box` is Scala-defined, we will get `Box[T | Null] | Null`. + This is needed because our nullability function is only applied (modularly) to the Java + classes, but not to the Scala ones, so we need a way to tell `Box` that it contains a + nullable value. + + The `List` is Java-defined, so we don't append `Null` to its type argument. But we + still need to nullify its inside. + +- We don't nullify _simple_ literal constant (`final`) fields, since they are known to be non-null + + ```java + class Constants { + final String NAME = "name"; + final int AGE = 0; + final char CHAR = 'a'; + + final String NAME_GENERATED = getNewName(); + } + ``` + + ==> + + ```scala + class Constants: + val NAME: String("name") = "name" + val AGE: Int(0) = 0 + val CHAR: Char('a') = 'a' + + val NAME_GENERATED: String | Null = getNewName() + ``` + +- We don't append `Null` to a field nor to a return type of a method which is annotated with a + `NotNull` annotation. + + ```java + class C { + @NotNull String name; + @NotNull List getNames(String prefix); // List is Java-defined + @NotNull Box getBoxedName(); // Box is Scala-defined + } + ``` + + ==> + + ```scala + class C: + val name: String + def getNames(prefix: String | Null): java.util.List[String] // we still need to nullify the paramter types + def getBoxedName(): Box[String | Null] // we don't append `Null` to the outmost level, but we still need to nullify inside + ``` + + The annotation must be from the list below to be recognized as `NotNull` by the compiler. + Check `Definitions.scala` for an updated list. + + ```scala + // A list of annotations that are commonly used to indicate + // that a field/method argument or return type is not null. + // These annotations are used by the nullification logic in + // JavaNullInterop to improve the precision of type nullification. + // We don't require that any of these annotations be present + // in the class path, but we want to create Symbols for the + // ones that are present, so they can be checked during nullification. + @tu lazy val NotNullAnnots: List[ClassSymbol] = ctx.getClassesIfDefined( + "javax.annotation.Nonnull" :: + "edu.umd.cs.findbugs.annotations.NonNull" :: + "androidx.annotation.NonNull" :: + "android.support.annotation.NonNull" :: + "android.annotation.NonNull" :: + "com.android.annotations.NonNull" :: + "org.eclipse.jdt.annotation.NonNull" :: + "org.checkerframework.checker.nullness.qual.NonNull" :: + "org.checkerframework.checker.nullness.compatqual.NonNullDecl" :: + "org.jetbrains.annotations.NotNull" :: + "lombok.NonNull" :: + "io.reactivex.annotations.NonNull" :: Nil map PreNamedString) + ``` + +### Override check + +When we check overriding between Scala classes and Java classes, the rules are relaxed for [`Null`](https://scala-lang.org/api/3.x/scala/Null.html) type with this feature, in order to help users to working with Java libraries. + +Suppose we have Java method `String f(String x)`, we can override this method in Scala in any of the following forms: + +```scala +def f(x: String | Null): String | Null + +def f(x: String): String | Null + +def f(x: String | Null): String + +def f(x: String): String +``` + +Note that some of the definitions could cause unsoundness. For example, the return type is not nullable, but a `null` value is actually returned. + +## Flow Typing + +We added a simple form of flow-sensitive type inference. The idea is that if `p` is a +stable path or a trackable variable, then we can know that `p` is non-null if it's compared +with `null`. This information can then be propagated to the `then` and `else` branches +of an if-statement (among other places). + +Example: + +```scala +val s: String | Null = ??? +if s != null then + // s: String + +// s: String | Null + +assert(s != null) +// s: String +``` + +A similar inference can be made for the `else` case if the test is `p == null` + +```scala +if s == null then + // s: String | Null +else + // s: String +``` + +`==` and `!=` is considered a comparison for the purposes of the flow inference. + +### Logical Operators + +We also support logical operators (`&&`, `||`, and `!`): + +```scala +val s: String | Null = ??? +val s2: String | Null = ??? +if s != null && s2 != null then + // s: String + // s2: String + +if s == null || s2 == null then + // s: String | Null + // s2: String | Null +else + // s: String + // s2: String +``` + +### Inside Conditions + +We also support type specialization _within_ the condition, taking into account that `&&` and `||` are short-circuiting: + +```scala +val s: String | Null = ??? + +if s != null && s.length > 0 then // s: String in `s.length > 0` + // s: String + +if s == null || s.length > 0 then // s: String in `s.length > 0` + // s: String | Null +else + // s: String +``` + +### Match Case + +The non-null cases can be detected in match statements. + +```scala +val s: String | Null = ??? + +s match + case _: String => // s: String + case _ => +``` + +### Mutable Variable + +We are able to detect the nullability of some local mutable variables. A simple example is: + +```scala +class C(val x: Int, val next: C | Null) + +var xs: C | Null = C(1, C(2, null)) +// xs is trackable, since all assignments are in the same method +while xs != null do + // xs: C + val xsx: Int = xs.x + val xscpy: C = xs + xs = xscpy // since xscpy is non-null, xs still has type C after this line + // xs: C + xs = xs.next // after this assignment, xs can be null again + // xs: C | Null +``` + +When dealing with local mutable variables, there are two questions: + +1. Whether to track a local mutable variable during flow typing. + We track a local mutable variable if the variable is not assigned in a closure. + For example, in the following code `x` is assigned to by the closure `y`, so we do not + do flow typing on `x`. + + ```scala + var x: String | Null = ??? + def y = + x = null + + if x != null then + // y can be called here, which would break the fact + val a: String = x // error: x is captured and mutated by the closure, not trackable + ``` + +2. Whether to generate and use flow typing on a specific _use_ of a local mutable variable. + We only want to do flow typing on a use that belongs to the same method as the definition + of the local variable. + For example, in the following code, even `x` is not assigned to by a closure, we can only + use flow typing in one of the occurrences (because the other occurrence happens within a + nested closure). + + ```scala + var x: String | Null = ??? + def y = + if x != null then + // not safe to use the fact (x != null) here + // since y can be executed at the same time as the outer block + val _: String = x + if x != null then + val a: String = x // ok to use the fact here + x = null + ``` + +See [more examples](https://github.com/lampepfl/dotty/blob/main/tests/explicit-nulls/neg/flow-varref-in-closure.scala). + +Currently, we are unable to track paths with a mutable variable prefix. +For example, `x.a` if `x` is mutable. + +### Unsupported Idioms + +We don't support: + +- flow facts not related to nullability (`if x == 0 then { // x: 0.type not inferred }`) +- tracking aliasing between non-nullable paths + + ```scala + val s: String | Null = ??? + val s2: String | Null = ??? + if s != null && s == s2 then + // s: String inferred + // s2: String not inferred + ``` + +### UnsafeNulls + +It is difficult to work with many nullable values, we introduce a language feature [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html). +Inside this "unsafe" scope, all `T | Null` values can be used as `T`. + +Users can import [`scala.language.unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html) to create such scopes, or use `-language:unsafeNulls` to enable this feature globally (for migration purpose only). + +Assume `T` is a reference type (a subtype of `AnyRef`), the following unsafe operation rules are +applied in this unsafe-nulls scope: + +1. the members of `T` can be found on `T | Null` + +2. a value with type `T` can be compared with `T | Null` and `Null` + +3. suppose `T1` is not a subtype of `T2` using explicit-nulls subtyping (where `Null` is a direct +subtype of Any), extension methods and implicit conversions designed for `T2` can be used for +`T1` if `T1` is a subtype of `T2` using regular subtyping rules (where `Null` is a subtype of every +reference type) + +4. suppose `T1` is not a subtype of `T2` using explicit-nulls subtyping, a value with type `T1` +can be used as `T2` if `T1` is a subtype of `T2` using regular subtyping rules + +Addtionally, `null` can be used as `AnyRef` (`Object`), which means you can select `.eq` or `.toString` on it. + +The program in [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html) will have a **similar** semantic as regular Scala, but not **equivalent**. + +For example, the following code cannot be compiled even using unsafe nulls. Because of the +Java interoperation, the type of the get method becomes `T | Null`. + +```scala +def head[T](xs: java.util.List[T]): T = xs.get(0) // error +``` + +Since the compiler doesn’t know whether `T` is a reference type, it is unable to cast `T | Null` +to `T`. A `.nn` need to be inserted after `xs.get(0)` by user manually to fix the error, which +strips the `Null` from its type. + +The intention of this [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html) is to give users a better migration path for explicit nulls. +Projects for Scala 2 or regular Scala 3 can try this by adding `-Yexplicit-nulls -language:unsafeNulls` +to the compile options. A small number of manual modifications are expected. To migrate to the full +explicit nulls feature in the future, `-language:unsafeNulls` can be dropped and add +`import scala.language.unsafeNulls` only when needed. + +```scala +def f(x: String): String = ??? +def nullOf[T >: Null]: T = null + +import scala.language.unsafeNulls + +val s: String | Null = ??? +val a: String = s // unsafely convert String | Null to String + +val b1 = s.trim // call .trim on String | Null unsafely +val b2 = b1.length + +f(s).trim // pass String | Null as an argument of type String unsafely + +val c: String = null // Null to String + +val d1: Array[String] = ??? +val d2: Array[String | Null] = d1 // unsafely convert Array[String] to Array[String | Null] +val d3: Array[String] = Array(null) // unsafe + +class C[T >: Null <: String] // define a type bound with unsafe conflict bound + +val n = nullOf[String] // apply a type bound unsafely +``` + +Without the [`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html), all these unsafe operations will not be type-checked. + +[`unsafeNulls`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$unsafeNulls$.html) also works for extension methods and implicit search. + +```scala +import scala.language.unsafeNulls + +val x = "hello, world!".split(" ").map(_.length) + +given Conversion[String, Array[String]] = _ => ??? + +val y: String | Null = ??? +val z: Array[String | Null] = y +``` + +## Binary Compatibility + +Our strategy for binary compatibility with Scala binaries that predate explicit nulls +and new libraries compiled without `-Yexplicit-nulls` is to leave the types unchanged +and be compatible but unsound. + +[More details](https://dotty.epfl.ch/docs/internals/explicit-nulls.html) diff --git a/docs/_spec/TODOreference/experimental/fewer-braces.md b/docs/_spec/TODOreference/experimental/fewer-braces.md new file mode 100644 index 000000000000..eb454886ad03 --- /dev/null +++ b/docs/_spec/TODOreference/experimental/fewer-braces.md @@ -0,0 +1,7 @@ +--- +layout: doc-page +title: "Fewer Braces" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/fewer-braces.html +--- + +The documentation contained in this file is now part of [./indentation.html]. \ No newline at end of file diff --git a/docs/_spec/TODOreference/experimental/main-annotation.md b/docs/_spec/TODOreference/experimental/main-annotation.md new file mode 100644 index 000000000000..0c60e1050b87 --- /dev/null +++ b/docs/_spec/TODOreference/experimental/main-annotation.md @@ -0,0 +1,97 @@ +--- +layout: doc-page +title: "MainAnnotation" +--- + +`MainAnnotation` provides a generic way to define main annotations such as `@main`. + +When a users annotates a method with an annotation that extends `MainAnnotation` a class with a `main` method will be generated. The main method will contain the code needed to parse the command line arguments and run the application. + +```scala +/** Sum all the numbers + * + * @param first Fist number to sum + * @param rest The rest of the numbers to sum + */ +@myMain def sum(first: Int, second: Int = 0, rest: Int*): Int = first + second + rest.sum +``` + +```scala +object foo { + def main(args: Array[String]): Unit = { + val mainAnnot = new myMain() + val info = new Info( + name = "foo.main", + documentation = "Sum all the numbers", + parameters = Seq( + new Parameter("first", "scala.Int", hasDefault=false, isVarargs=false, "Fist number to sum", Seq()), + new Parameter("second", "scala.Int", hasDefault=true, isVarargs=false, "", Seq()), + new Parameter("rest", "scala.Int" , hasDefault=false, isVarargs=true, "The rest of the numbers to sum", Seq()) + ) + ) + val mainArgsOpt = mainAnnot.command(info, args) + if mainArgsOpt.isDefined then + val mainArgs = mainArgsOpt.get + val args0 = mainAnnot.argGetter[Int](info.parameters(0), mainArgs(0), None) // using a parser of Int + val args1 = mainAnnot.argGetter[Int](info.parameters(1), mainArgs(1), Some(() => sum$default$1())) // using a parser of Int + val args2 = mainAnnot.varargGetter[Int](info.parameters(2), mainArgs.drop(2)) // using a parser of Int + mainAnnot.run(() => sum(args0(), args1(), args2()*)) + } +} +``` + +The implementation of the `main` method first instantiates the annotation and then call `command`. +When calling the `command`, the arguments can be checked and preprocessed. +Then it defines a series of argument getters calling `argGetter` for each parameter and `varargGetter` for the last one if it is a varargs. `argGetter` gets an optional lambda that computes the default argument. +Finally, the `run` method is called to run the application. It receives a by-name argument that contains the call the annotated method with the instantiations arguments (using the lambdas from `argGetter`/`varargGetter`). + + +Example of implementation of `myMain` that takes all arguments positionally. It used `util.CommandLineParser.FromString` and expects no default arguments. For simplicity, any errors in preprocessing or parsing results in crash. + +```scala +// Parser used to parse command line arguments +import scala.util.CommandLineParser.FromString[T] + +// Result type of the annotated method is Int and arguments are parsed using FromString +@experimental class myMain extends MainAnnotation[FromString, Int]: + import MainAnnotation.{ Info, Parameter } + + def command(info: Info, args: Seq[String]): Option[Seq[String]] = + if args.contains("--help") then + println(info.documentation) + None // do not parse or run the program + else if info.parameters.exists(_.hasDefault) then + println("Default arguments are not supported") + None + else if info.hasVarargs then + val numPlainArgs = info.parameters.length - 1 + if numPlainArgs > args.length then + println("Not enough arguments") + None + else + Some(args) + else + if info.parameters.length > args.length then + println("Not enough arguments") + None + else if info.parameters.length < args.length then + println("Too many arguments") + None + else + Some(args) + + def argGetter[T](param: Parameter, arg: String, defaultArgument: Option[() => T])(using parser: FromString[T]): () => T = + () => parser.fromString(arg) + + def varargGetter[T](param: Parameter, args: Seq[String])(using parser: FromString[T]): () => Seq[T] = + () => args.map(arg => parser.fromString(arg)) + + def run(program: () => Int): Unit = + println("executing program") + + val result = program() + println("result: " + result) + println("executed program") + +end myMain +``` diff --git a/docs/_spec/TODOreference/experimental/named-typeargs-spec.md b/docs/_spec/TODOreference/experimental/named-typeargs-spec.md new file mode 100644 index 000000000000..9e1113bbac86 --- /dev/null +++ b/docs/_spec/TODOreference/experimental/named-typeargs-spec.md @@ -0,0 +1,41 @@ +--- +layout: doc-page +title: "Named Type Arguments - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/named-typeargs-spec.html +--- + +In this section we give more details about the [named type arguments](named-typeargs.md) (*experimental*). + +## Syntax + +The addition to the grammar is: + +``` +SimpleExpr1 ::= ... + | SimpleExpr (TypeArgs | NamedTypeArgs) +NamedTypeArgs ::= ‘[’ NamedTypeArg {‘,’ NamedTypeArg} ‘]’ +NamedTypeArg ::= id ‘=’ Type +``` + +Note in particular that named arguments cannot be passed to type constructors: + +``` scala +class C[T] + +val x: C[T = Int] = // error + new C[T = Int] // error + +class E extends C[T = Int] // error +``` + +## Compatibility considerations + +Named type arguments do not have an impact on binary compatibility, but they +have an impact on source compatibility: if the name of a method type parameter +is changed, any existing named reference to this parameter will break. This +means that the names of method type parameters are now part of the public API +of a library. + +(Unimplemented proposal: to mitigate this, +[`scala.deprecatedName`](https://www.scala-lang.org/api/current/scala/deprecatedName.html) +could be extended to also be applicable on method type parameters.) diff --git a/docs/_spec/TODOreference/experimental/named-typeargs.md b/docs/_spec/TODOreference/experimental/named-typeargs.md new file mode 100644 index 000000000000..4928a40f8a6a --- /dev/null +++ b/docs/_spec/TODOreference/experimental/named-typeargs.md @@ -0,0 +1,34 @@ +--- +layout: doc-page +title: "Named Type Arguments" +redirectFrom: /docs/reference/other-new-features/named-typeargs.html +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/named-typeargs.html +--- + +**Note:** This feature is implemented in Scala 3, but is not expected to be part of Scala 3.0. + +Type arguments of methods can now be specified by name as well as by position. Example: + +``` scala +def construct[Elem, Coll[_]](xs: Elem*): Coll[Elem] = ??? + +val xs1 = construct[Coll = List, Elem = Int](1, 2, 3) +val xs2 = construct[Coll = List](1, 2, 3) +``` + +Similar to a named value argument `(x = e)`, a named type argument +`[X = T]` instantiates the type parameter `X` to the type `T`. +Named type arguments do not have to be in order (see `xs1` above) and +unspecified arguments are inferred by the compiler (see `xs2` above). +Type arguments must be all named or un-named, mixtures of named and +positional type arguments are not supported. + +## Motivation + +The main benefit of named type arguments is that unlike positional arguments, +you are allowed to omit passing arguments for some parameters, like in the +definition of `xs2` above. A missing type argument is inferred as usual by +local type inference. This is particularly useful in situations where some type +arguments can be easily inferred from others. + +[More details](./named-typeargs-spec.md) diff --git a/docs/_spec/TODOreference/experimental/numeric-literals.md b/docs/_spec/TODOreference/experimental/numeric-literals.md new file mode 100644 index 000000000000..f493ef459265 --- /dev/null +++ b/docs/_spec/TODOreference/experimental/numeric-literals.md @@ -0,0 +1,257 @@ +--- +layout: doc-page +title: "Numeric Literals" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/numeric-literals.html +--- + +**Note**: This feature is not yet part of the Scala 3 language definition. It can be made available by a language import: + +```scala +import scala.language.experimental.genericNumberLiterals +``` + +In Scala 2, numeric literals were confined to the primitive numeric types `Int`, `Long`, `Float`, and `Double`. Scala 3 allows to write numeric literals also for user-defined types. Example: + +```scala +val x: Long = -10_000_000_000 +val y: BigInt = 0x123_abc_789_def_345_678_901 +val z: BigDecimal = 110_222_799_799.99 + +(y: BigInt) match + case 123_456_789_012_345_678_901 => +``` + +The syntax of numeric literals is the same as before, except there are no pre-set limits +how large they can be. + +## Meaning of Numeric Literals + +The meaning of a numeric literal is determined as follows: + +- If the literal ends with `l` or `L`, it is a `Long` integer (and must fit in its legal range). +- If the literal ends with `f` or `F`, it is a single precision floating point number of type `Float`. +- If the literal ends with `d` or `D`, it is a double precision floating point number of type `Double`. + +In each of these cases the conversion to a number is exactly as in Scala 2 or in Java. If a numeric literal does _not_ end in one of these suffixes, its meaning is determined by the expected type: + +1. If the expected type is `Int`, `Long`, `Float`, or `Double`, the literal is + treated as a standard literal of that type. +2. If the expected type is a fully defined type `T` that has a given instance of type + [`scala.util.FromDigits[T]`](https://scala-lang.org/api/3.x/scala/util/FromDigits.html), the literal is converted to a value of type `T` by passing it as an argument to + the `fromDigits` method of that instance (more details below). +3. Otherwise, the literal is treated as a `Double` literal (if it has a decimal point or an + exponent), or as an `Int` literal (if not). (This last possibility is again as in Scala 2 or Java.) + +With these rules, the definition + +```scala +val x: Long = -10_000_000_000 +``` + +is legal by rule (1), since the expected type is `Long`. The definitions + +```scala +val y: BigInt = 0x123_abc_789_def_345_678_901 +val z: BigDecimal = 111222333444.55 +``` + +are legal by rule (2), since both `BigInt` and `BigDecimal` have [`FromDigits`](https://scala-lang.org/api/3.x/scala/util/FromDigits.html) instances (which implement the `FromDigits` subclasses [`FromDigits.WithRadix`](https://scala-lang.org/api/3.x/scala/util/FromDigits$$WithRadix.html) and [`FromDigits.Decimal`](https://scala-lang.org/api/3.x/scala/util/FromDigits$$Decimal.html), respectively). On the other hand, + +```scala +val x = -10_000_000_000 +``` + +gives a type error, since without an expected type `-10_000_000_000` is treated by rule (3) as an `Int` literal, but it is too large for that type. + +## The `FromDigits` Trait + +To allow numeric literals, a type simply has to define a `given` instance of the +[`scala.util.FromDigits`](https://scala-lang.org/api/3.x/scala/util/FromDigits.html) type class, or one of its subclasses. `FromDigits` is defined as follows: + +```scala +trait FromDigits[T]: + def fromDigits(digits: String): T +``` + +Implementations of `fromDigits` convert strings of digits to the values of the +implementation type `T`. +The `digits` string consists of digits between `0` and `9`, possibly preceded by a +sign ("+" or "-"). Number separator characters `_` are filtered out before +the string is passed to `fromDigits`. + +The companion object [`FromDigits`](https://scala-lang.org/api/3.x/scala/util/FromDigits$.html) also defines subclasses of `FromDigits` for whole numbers with a given radix, for numbers with a decimal point, and for numbers that can have both a decimal point and an exponent: + +```scala +object FromDigits: + + /** A subclass of `FromDigits` that also allows to convert whole + * number literals with a radix other than 10 + */ + trait WithRadix[T] extends FromDigits[T]: + def fromDigits(digits: String): T = fromDigits(digits, 10) + def fromDigits(digits: String, radix: Int): T + + /** A subclass of `FromDigits` that also allows to convert number + * literals containing a decimal point ".". + */ + trait Decimal[T] extends FromDigits[T] + + /** A subclass of `FromDigits`that allows also to convert number + * literals containing a decimal point "." or an + * exponent `('e' | 'E')['+' | '-']digit digit*`. + */ + trait Floating[T] extends Decimal[T] +``` + +A user-defined number type can implement one of those, which signals to the compiler +that hexadecimal numbers, decimal points, or exponents are also accepted in literals +for this type. + +## Error Handling + +`FromDigits` implementations can signal errors by throwing exceptions of some subtype +of [`FromDigitsException`](https://scala-lang.org/api/3.x/scala/util/FromDigits$$FromDigitsException.html). `FromDigitsException` is defined with three subclasses in the +`FromDigits` object as follows: + +```scala +abstract class FromDigitsException(msg: String) extends NumberFormatException(msg) + +class NumberTooLarge (msg: String = "number too large") extends FromDigitsException(msg) +class NumberTooSmall (msg: String = "number too small") extends FromDigitsException(msg) +class MalformedNumber(msg: String = "malformed number literal") extends FromDigitsException(msg) +``` + +## Example + +As a fully worked out example, here is an implementation of a new numeric class, `BigFloat`, that accepts numeric literals. `BigFloat` is defined in terms of a `BigInt` mantissa and an `Int` exponent: + +```scala +case class BigFloat(mantissa: BigInt, exponent: Int): + override def toString = s"${mantissa}e${exponent}" +``` + +`BigFloat` literals can have a decimal point as well as an exponent. E.g. the following expression +should produce the `BigFloat` number `BigFloat(-123, 997)`: + +```scala +-0.123E+1000: BigFloat +``` + +The companion object of `BigFloat` defines an `apply` constructor method to construct a `BigFloat` +from a `digits` string. Here is a possible implementation: + +```scala +object BigFloat: + import scala.util.FromDigits + + def apply(digits: String): BigFloat = + val (mantissaDigits, givenExponent) = + digits.toUpperCase.split('E') match + case Array(mantissaDigits, edigits) => + val expo = + try FromDigits.intFromDigits(edigits) + catch case ex: FromDigits.NumberTooLarge => + throw FromDigits.NumberTooLarge(s"exponent too large: $edigits") + (mantissaDigits, expo) + case Array(mantissaDigits) => + (mantissaDigits, 0) + val (intPart, exponent) = + mantissaDigits.split('.') match + case Array(intPart, decimalPart) => + (intPart ++ decimalPart, givenExponent - decimalPart.length) + case Array(intPart) => + (intPart, givenExponent) + BigFloat(BigInt(intPart), exponent) +``` + +To accept `BigFloat` literals, all that's needed in addition is a `given` instance of type +`FromDigits.Floating[BigFloat]`: + +```scala + given FromDigits: FromDigits.Floating[BigFloat] with + def fromDigits(digits: String) = apply(digits) +end BigFloat +``` + +Note that the `apply` method does not check the format of the `digits` argument. It is +assumed that only valid arguments are passed. For calls coming from the compiler +that assumption is valid, since the compiler will first check whether a numeric +literal has the correct format before it gets passed on to a conversion method. + +## Compile-Time Errors + +With the setup of the previous section, a literal like + +```scala +1e10_0000_000_000: BigFloat +``` + +would be expanded by the compiler to + +```scala +BigFloat.FromDigits.fromDigits("1e100000000000") +``` + +Evaluating this expression throws a [`NumberTooLarge`](https://scala-lang.org/api/3.x/scala/util/FromDigits$$NumberTooLarge.html) exception at run time. We would like it to +produce a compile-time error instead. We can achieve this by tweaking the `BigFloat` class +with a small dose of metaprogramming. The idea is to turn the `fromDigits` method +into a macro, i.e. make it an inline method with a splice as right-hand side. +To do this, replace the `FromDigits` instance in the `BigFloat` object by the following two definitions: + +```scala +object BigFloat: + ... + + class FromDigits extends FromDigits.Floating[BigFloat]: + def fromDigits(digits: String) = apply(digits) + + given FromDigits with + override inline def fromDigits(digits: String) = ${ + fromDigitsImpl('digits) + } +``` + +Note that an inline method cannot directly fill in for an abstract method, since it produces +no code that can be executed at runtime. That is why we define an intermediary class +`FromDigits` that contains a fallback implementation which is then overridden by the inline +method in the `FromDigits` given instance. That method is defined in terms of a macro +implementation method `fromDigitsImpl`. Here is its definition: + +```scala + private def fromDigitsImpl(digits: Expr[String])(using ctx: Quotes): Expr[BigFloat] = + digits.value match + case Some(ds) => + try + val BigFloat(m, e) = apply(ds) + '{BigFloat(${Expr(m)}, ${Expr(e)})} + catch case ex: FromDigits.FromDigitsException => + ctx.error(ex.getMessage) + '{BigFloat(0, 0)} + case None => + '{apply($digits)} +end BigFloat +``` + +The macro implementation takes an argument of type `Expr[String]` and yields +a result of type `Expr[BigFloat]`. It tests whether its argument is a constant +string. If that is the case, it converts the string using the `apply` method +and lifts the resulting `BigFloat` back to `Expr` level. For non-constant +strings `fromDigitsImpl(digits)` is simply `apply(digits)`, i.e. everything is +evaluated at runtime in this case. + +The interesting part is the `catch` part of the case where `digits` is constant. +If the `apply` method throws a `FromDigitsException`, the exception's message is issued as a compile time error in the `ctx.error(ex.getMessage)` call. + +With this new implementation, a definition like + +```scala +val x: BigFloat = 1234.45e3333333333 +``` + +would give a compile time error message: + +```scala +3 | val x: BigFloat = 1234.45e3333333333 + | ^^^^^^^^^^^^^^^^^^ + | exponent too large: 3333333333 +``` diff --git a/docs/_spec/TODOreference/experimental/overview.md b/docs/_spec/TODOreference/experimental/overview.md new file mode 100644 index 000000000000..254f103896e4 --- /dev/null +++ b/docs/_spec/TODOreference/experimental/overview.md @@ -0,0 +1,29 @@ +--- +layout: doc-page +title: "Experimental" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/overview.html +redirectFrom: overview.html +--- + +## Experimental language features + +All experimental language features can be found under the `scala.language.experimental` package. +They are enabled by importing the feature or using the `-language` compiler flag. + +* [`erasedDefinitions`](./erased-defs.md): Enable support for `erased` modifier. +* `fewerBraces`: Enable support for using indentation for arguments. +* [`genericNumberLiterals`](./numeric-literals.md): Enable support for generic number literals. +* [`namedTypeArguments`](./named-typeargs.md): Enable support for named type arguments +* [`saferExceptions`](./canthrow.md): Enable support for checked exceptions. + +## Experimental language imports + +In general, experimental language features can be imported in an experimental scope (see [experimental definitions](../other-new-features/experimental-defs.md)). +They can be imported at the top-level if all top-level definitions are `@experimental`. + +## Experimental language features supported by special compiler options + +Some experimental language features that are still in research and development can be enabled with special compiler options. These include + +* [`-Yexplicit-nulls`](./explicit-nulls.md). Enable support for tracking null references in the type system. +* [`-Ycc`](./cc.md). Enable support for capture checking. diff --git a/docs/_spec/TODOreference/experimental/tupled-function.md b/docs/_spec/TODOreference/experimental/tupled-function.md new file mode 100644 index 000000000000..da108fc832ad --- /dev/null +++ b/docs/_spec/TODOreference/experimental/tupled-function.md @@ -0,0 +1,82 @@ +--- +layout: doc-page +title: "Tupled Function" +--- + +Tupled Function +---------------------- + +With functions bounded to arities up to 22 it was possible to generalize some operation on all function types using overloading. +Now that we have functions and tuples generalized to [arities above 22](../dropped-features/limit22.md) overloading is not an option anymore. +The type class `TupleFunction` provides a way to abstract directly over a function of any arity converting it to an equivalent function that receives all arguments in a single tuple. + +```scala +/** Type class relating a `FunctionN[..., R]` with an equivalent tupled function `Function1[TupleN[...], R]` + * + * @tparam F a function type + * @tparam G a tupled function type (function of arity 1 receiving a tuple as argument) + */ +@implicitNotFound("${F} cannot be tupled as ${G}") +sealed trait TupledFunction[F, G] { + def tupled(f: F): G + def untupled(g: G): F +} +``` + +The compiler will synthesize an instance of `TupledFunction[F, G]` if: + +* `F` is a function type of arity `N` +* `G` is a function with a single tuple argument of size `N` and its types are equal to the arguments of `F` +* The return type of `F` is equal to the return type of `G` +* `F` and `G` are the same sort of function (both are `(...) => R` or both are `(...) ?=> R`) +* If only one of `F` or `G` is instantiated the second one is inferred. + +Examples +-------- +`TupledFunction` can be used to generalize the `Function1.tupled`, ... `Function22.tupled` methods to functions of any arities. +The following defines `tupled` as [extension method](../contextual/extension-methods.html) ([full example](https://github.com/lampepfl/dotty/blob/main/tests/run/tupled-function-tupled.scala)). + +```scala +/** Creates a tupled version of this function: instead of N arguments, + * it accepts a single [[scala.Tuple]] with N elements as argument. + * + * @tparam F the function type + * @tparam Args the tuple type with the same types as the function arguments of F + * @tparam R the return type of F + */ +extension [F, Args <: Tuple, R](f: F) + def tupled(using tf: TupledFunction[F, Args => R]): Args => R = tf.tupled(f) +``` + +`TupledFunction` can be used to generalize the `Function.untupled` to a function of any arities ([full example](https://github.com/lampepfl/dotty/blob/main/tests/run/tupled-function-untupled.scala)) + +```scala +/** Creates an untupled version of this function: instead of a single argument of type [[scala.Tuple]] with N elements, + * it accepts N arguments. + * + * This is a generalization of [[scala.Function.untupled]] that work on functions of any arity + * + * @tparam F the function type + * @tparam Args the tuple type with the same types as the function arguments of F + * @tparam R the return type of F + */ +extension [F, Args <: Tuple, R](f: Args => R) + def untupled(using tf: TupledFunction[F, Args => R]): F = tf.untupled(f) +``` + +`TupledFunction` can also be used to generalize the [`Tuple1.compose`](https://github.com/lampepfl/dotty/blob/main/tests/run/tupled-function-compose.scala) and [`Tuple1.andThen`](https://github.com/lampepfl/dotty/blob/main/tests/run/tupled-function-andThen.scala) methods to compose functions of larger arities and with functions that return tuples. + +```scala +/** Composes two instances of TupledFunction into a new TupledFunction, with this function applied last. + * + * @tparam F a function type + * @tparam G a function type + * @tparam FArgs the tuple type with the same types as the function arguments of F and return type of G + * @tparam GArgs the tuple type with the same types as the function arguments of G + * @tparam R the return type of F + */ +extension [F, G, FArgs <: Tuple, GArgs <: Tuple, R](f: F) + def compose(g: G)(using tg: TupledFunction[G, GArgs => FArgs], tf: TupledFunction[F, FArgs => R]): GArgs => R = { + (x: GArgs) => tf.tupled(f)(tg.tupled(g)(x)) +} +``` diff --git a/docs/_spec/TODOreference/features-classification.md b/docs/_spec/TODOreference/features-classification.md new file mode 100644 index 000000000000..36cea3b9e72d --- /dev/null +++ b/docs/_spec/TODOreference/features-classification.md @@ -0,0 +1,199 @@ +--- +layout: doc-page +title: "A Classification of Proposed Language Features" +nightlyOf: https://docs.scala-lang.org/scala3/reference/features-classification.html +--- + +This document provides an overview of the constructs proposed for Scala 3 with the aim to facilitate the discussion what to include and when to include it. It classifies features into eight groups: (1) essential foundations, (2) simplifications, (3) restrictions, (4) dropped features, (5) changed features, (6) new features, (7) features oriented towards metaprogramming with the aim to replace existing macros, and (8) changes to type checking and inference. + +Each group contains sections classifying the status (i.e. relative importance to be a part of Scala 3, and relative urgency when to decide this) and the migration cost +of the constructs in it. + +The current document reflects the state of things as of April, 2019. It will be updated to reflect any future changes in that status. + +## Essential Foundations + +These new constructs directly model core features of [DOT](https://www.scala-lang.org/blog/2016/02/03/essence-of-scala.html), higher-kinded types, and the [SI calculus for implicit resolution](https://infoscience.epfl.ch/record/229878/files/simplicitly_1.pdf). + + - [Intersection types](new-types/intersection-types.md), replacing compound types, + - [Union types](new-types/union-types.md), + - [Type lambdas](new-types/type-lambdas.md), + replacing encodings using structural types and type projection. + - [Context functions](contextual/context-functions.md) offering abstraction over given parameters. + +**Status: essential** + +These are essential core features of Scala 3. Without them, Scala 3 would be a completely different language, with different foundations. + +**Migration cost: none to low** + +Since these are additions, there's generally no migration cost for old code. An exception are intersection types which replace compound types with slightly cleaned-up semantics. But few programs would be affected by this change. + +## Simplifications + +These constructs replace existing constructs with the aim of making the language safer and simpler to use, and to promote uniformity in code style. + + - [Trait parameters](other-new-features/trait-parameters.md) replace [early initializers](dropped-features/early-initializers.md) with a more generally useful construct. + - [Given instances](contextual/givens.md) + replace implicit objects and defs, focussing on intent over mechanism. + - [Using clauses](contextual/using-clauses.md) replace implicit parameters, avoiding their ambiguities. + - [Extension methods](contextual/extension-methods.md) replace implicit classes with a clearer and simpler mechanism. + - [Opaque type aliases](other-new-features/opaques.md) replace most uses + of value classes while guaranteeing absence of boxing. + - [Top-level definitions](dropped-features/package-objects.md) replace package objects, dropping syntactic boilerplate. + - [Export clauses](other-new-features/export.md) + provide a simple and general way to express aggregation, which can replace the + previous facade pattern of package objects inheriting from classes. + - [Vararg splices](changed-features/vararg-splices.md) now use the form `*` instead of `@ _*`, mirroring vararg expressions, + - [Creator applications](other-new-features/creator-applications.md) allow using simple function call syntax + instead of `new` expressions. `new` expressions stay around as a fallback for + the cases where creator applications cannot be used. + +With the exception of early initializers and old-style vararg splices, all superseded constructs continue to be available in Scala 3.0. The plan is to deprecate and phase them out later. + +Value classes (superseded by opaque type aliases) are a special case. There are currently no deprecation plans for value classes, since we might bring them back in a more general form if they are supported natively by the JVM as is planned by project Valhalla. + +**Status: bimodal: now or never / can delay** + +These are essential simplifications. If we decide to adopt them, we should do it for 3.0. Otherwise we are faced with the awkward situation that the Scala 3 documentation has to describe an old feature that will be replaced or superseded by a simpler one in the future. + +On the other hand, we need to decide now only about the new features in this list. The decision to drop the superseded features can be delayed. Of course, adopting a new feature without deciding to drop the superseded feature will make the language larger. + +**Migration cost: moderate** + +For the next several versions, old features will remain available and deprecation and rewrite techniques can make any migration effort low and gradual. + + +## Restrictions + +These constructs are restricted to make the language safer. + + - [Implicit Conversions](contextual/conversions.md): there is only one way to define implicit conversions instead of many, and potentially surprising implicit conversions require a language import. + - [Given Imports](contextual/given-imports.md): implicits now require a special form of import, to make the import clearly visible. + - [Type Projection](dropped-features/type-projection.md): only classes can be used as prefix `C` of a type projection `C#A`. Type projection on abstract types is no longer supported since it is unsound. + - [Multiversal equality](contextual/multiversal-equality.md) implements an "opt-in" scheme to rule out nonsensical comparisons with `==` and `!=`. + - [infix](https://github.com/lampepfl/dotty/pull/5975) + makes method application syntax uniform across code bases. + +Unrestricted implicit conversions continue to be available in Scala 3.0, but will be deprecated and removed later. Unrestricted versions of the other constructs in the list above are available only under `-source 3.0-migration`. + +**Status: now or never** + +These are essential restrictions. If we decide to adopt them, we should do it for 3.0. Otherwise we are faced with the awkward situation that the Scala 3 documentation has to describe a feature that will be restricted in the future. + +**Migration cost: low to high** + + - _low_: multiversal equality rules out code that is nonsensical, so any rewrites required by its adoption should be classified as bug fixes. + - _moderate_: Restrictions to implicits can be accommodated by straightforward rewriting. + - _high_: Unrestricted type projection cannot always rewritten directly since it is unsound in general. + +## Dropped Constructs + +These constructs are proposed to be dropped without a new construct replacing them. The motivation for dropping these constructs is to simplify the language and its implementation. + + - [DelayedInit](dropped-features/delayed-init.md), + - [Existential types](dropped-features/existential-types.md), + - [Procedure syntax](dropped-features/procedure-syntax.md), + - [Class shadowing](dropped-features/class-shadowing.md), + - [XML literals](dropped-features/xml.md), + - [Symbol literals](dropped-features/symlits.md), + - [Auto application](dropped-features/auto-apply.md), + - [Weak conformance](dropped-features/weak-conformance.md), + - [Compound types](new-types/intersection-types.md), + - [Auto tupling](https://github.com/lampepfl/dotty/pull/4311) (implemented, but not merged). + +The date when these constructs are dropped varies. The current status is: + + - Not implemented at all: + - DelayedInit, existential types, weak conformance. + - Supported under `-source 3.0-migration`: + - procedure syntax, class shadowing, symbol literals, auto application, auto tupling in a restricted form. + - Supported in 3.0, to be deprecated and phased out later: + - XML literals, compound types. + +**Status: mixed** + +Currently unimplemented features would require considerable implementation effort which would in most cases make the compiler more buggy and fragile and harder to understand. If we do not decide to drop them, they will probably show up as "not yet implemented" in the Scala 3.0 release. + +Currently implemented features could stay around indefinitely. Updated docs may simply ignore them, in the expectation that they might go away eventually. So the decision about their removal can be delayed. + +**Migration cost: moderate to high** + +Dropped features require rewrites to avoid their use in programs. These rewrites can sometimes be automatic (e.g. for procedure syntax, symbol literals, auto application) +and sometimes need to be manual (e.g. class shadowing, auto tupling). Sometimes the rewrites would have to be non-local, affecting use sites as well as definition sites (e.g., in the case of `DelayedInit`, unless we find a solution). + +## Changes + +These constructs have undergone changes to make them more regular and useful. + + - [Structural Types](changed-features/structural-types.md): They now allow pluggable implementations, which greatly increases their usefulness. Some usage patterns are restricted compared to the status quo. + - [Name-based pattern matching](changed-features/pattern-matching.md): The existing undocumented Scala 2 implementation has been codified in a slightly simplified form. + - [Eta expansion](changed-features/eta-expansion.md) is now performed universally also in the absence of an expected type. The postfix `_` operator is thus made redundant. It will be deprecated and dropped after Scala 3.0. + - [Implicit Resolution](changed-features/implicit-resolution.md): The implicit resolution rules have been cleaned up to make them more useful and less surprising. Implicit scope is restricted to no longer include package prefixes. + +Most aspects of old-style implicit resolution are still available under `-source 3.0-migration`. The other changes in this list are applied unconditionally. + +**Status: strongly advisable** + +The features have been implemented in their new form in Scala 3.0's compiler. They provide clear improvements in simplicity and functionality compared to the status quo. Going back would require significant implementation effort for a net loss of functionality. + +**Migration cost: low to high** + +Only a few programs should require changes, but some necessary changes might be non-local (as in the case of restrictions to implicit scope). + +## New Constructs + +These are additions to the language that make it more powerful or pleasant to use. + + - [Enums](enums/enums.md) provide concise syntax for enumerations and [algebraic data types](enums/adts.md). + - [Parameter untupling](other-new-features/parameter-untupling.md) avoids having to use `case` for tupled parameter destructuring. + - [Dependent function types](new-types/dependent-function-types.md) generalize dependent methods to dependent function values and types. + - [Polymorphic function types](https://github.com/lampepfl/dotty/pull/4672) generalize polymorphic methods to dependent function values and types. _Current status_: There is a proposal, and a prototype implementation, but the implementation has not been finalized or merged yet. + - [Kind polymorphism](other-new-features/kind-polymorphism.md) allows the definition of operators working equally on types and type constructors. + +**Status: mixed** + +Enums offer an essential simplification of fundamental use patterns, so they should be adopted for Scala 3.0. Auto-parameter tupling is a very small change that removes some awkwardness, so it might as well be adopted now. The other features constitute more specialized functionality which could be introduced in later versions. On the other hand, except for polymorphic function types they are all fully implemented, so if the Scala 3.0 spec does not include them, they might be still made available under a language flag. + +**Migration cost: none** + +Being new features, existing code migrates without changes. To be sure, sometimes it would be attractive to rewrite code to make use of the new features in order to increase clarity and conciseness. + +## Metaprogramming + +The following constructs together aim to put metaprogramming in Scala on a new basis. So far, metaprogramming was achieved by a combination of macros and libraries such as [Shapeless](https://github.com/milessabin/shapeless) that were in turn based on some key macros. Current Scala 2 macro mechanisms are a thin veneer on top the current Scala 2 compiler, which makes them fragile and in many cases impossible to port to Scala 3. + +It's worth noting that macros were never included in the [Scala 2 language specification](https://scala-lang.org/files/archive/spec/2.13/) and were so far made available only under an `-experimental` flag. This has not prevented their widespread usage. + +To enable porting most uses of macros, we are experimenting with the advanced language constructs listed below. These designs are more provisional than the rest of the proposed language constructs for Scala 3.0. There might still be some changes until the final release. Stabilizing the feature set needed for metaprogramming is our first priority. + +- [Match types](new-types/match-types.md) allow computation on types. +- [Inline](metaprogramming/inline.md) provides +by itself a straightforward implementation of some simple macros and is at the same time an essential building block for the implementation of complex macros. +- [Quotes and splices](metaprogramming/macros.md) provide a principled way to express macros and staging with a unified set of abstractions. +- [Type class derivation](contextual/derivation.md) provides an in-language implementation of the `Gen` macro in Shapeless and other foundational libraries. The new implementation is more robust, efficient and easier to use than the macro. +- [Implicit by-name parameters](contextual/by-name-context-parameters.md) provide a more robust in-language implementation of the `Lazy` macro in Shapeless. + +**Status: not yet settled** + +We know we need a practical replacement for current macros. The features listed above are very promising in that respect, but we need more complete implementations and more use cases to reach a final verdict. + +**Migration cost: very high** + +Existing macro libraries will have to be rewritten from the ground up. In many cases the rewritten libraries will turn out to be simpler and more robust than the old ones, but that does not relieve one of the cost of the rewrites. It's currently unclear to what degree users of macro libraries will be affected. We aim to provide sufficient functionality so that core macros can be re-implemented fully, but given the vast feature set of the various macro extensions to Scala 2 it is difficult to arrive at a workable limitation of scope. + +## Changes to Type Checking and Inference + +The Scala 3 compiler uses a new algorithm for type inference, which relies on a general subtype constraint solver. The new algorithm often [works better than the old](https://contributors.scala-lang.org/t/better-type-inference-for-scala-send-us-your-problematic-cases/2410), but there are inevitably situations where the results of both algorithms differ, leading to errors diagnosed by Scala 3 for programs that the Scala 2 compiler accepts. + +**Status: essential** + +The new type-checking and inference algorithms are the essential core of the new compiler. They cannot be reverted without dropping the whole implementation of Scala 3. + +**Migration cost: high** + +Some existing programs will break and, given the complex nature of type inference, it will not always be clear what change caused the breakage and how to fix it. + +In our experience, macros and changes in type and implicit argument inference together cause the large majority of problems encountered when porting existing code to Scala 3. The latter source of problems could be addressed systematically by a tool that added all inferred types and implicit arguments to a Scala 2 source code file. Most likely such a tool would be implemented as a [Scala 2 compiler plugin](https://docs.scala-lang.org/overviews/plugins/index.html). The resulting code would have a greatly increased likelihood to compile under Scala 3, but would often be bulky to the point of being unreadable. A second part of the rewriting tool should then selectively and iteratively remove type and implicit annotations that were synthesized by the first part as long as they compile under Scala 3. This second part could be implemented as a program that invokes the Scala 3 compiler `scalac` programmatically. + +Several people have proposed such a tool for some time now. I believe it is time we find the will and the resources to actually implement it. diff --git a/docs/_spec/TODOreference/language-versions/binary-compatibility.md b/docs/_spec/TODOreference/language-versions/binary-compatibility.md new file mode 100644 index 000000000000..df1c19f97868 --- /dev/null +++ b/docs/_spec/TODOreference/language-versions/binary-compatibility.md @@ -0,0 +1,13 @@ +--- +layout: doc-page +title: "Binary Compatibility" +nightlyOf: https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html +--- + +In Scala 2 different minor versions of the compiler were free to change the way how they encode different language features in JVM bytecode so each bump of the compiler's minor version resulted in breaking binary compatibility and if a project had any Scala dependencies they all needed to be (cross-)compiled to the same minor Scala version that was used in that project itself. On the contrary, Scala 3 has a stable encoding into JVM bytecode. + +In addition to classfiles the compilation process in Scala 3 also produces files with `.tasty` extension. The [TASTy](https://docs.scala-lang.org/scala3/guides/tasty-overview.html) format is an intermediate representation of Scala code containing full information about sources together with information provided by the typer. Some of this information is lost during generation of bytecode so Scala 3 compilers read TASTy files during compilation in addition to classfiles to know the exact types of values, methods, etc. in already compiled classes (although compilation from TASTy files only is also possible). TASTy files are also typically distributed together with classfiles in published artifacts. + +TASTy format is extensible but it preserves backward compatibility and the evolution happens between minor releases of the language. This means a Scala compiler in version `3.x1.y1` is able to read TASTy files produced by another compiler in version `3.x2.y2` if `x1 >= x2` (assuming two stable versions of the compiler are considered - `SNAPSHOT` or `NIGHTLY` compiler versions can read TASTy in an older stable format but their TASTY versions are not compatible between each other even if the compilers have the same minor version; also compilers in stable versions cannot read TASTy generated by an unstable version). + +TASTy version number has the format of `.-` and the numbering changes in parallel to language releases in such a way that a bump in language minor version corresponds to a bump in TASTy minor version (e.g. for Scala `3.0.0` the TASTy version is `28.0-0`). Experimental version set to 0 signifies a stable version while others are considered unstable/experimental. TASTy version is not strictly bound to the data format itself - any changes to the API of the standard library also require a change in TASTy minor version. diff --git a/docs/_spec/TODOreference/language-versions/language-versions.md b/docs/_spec/TODOreference/language-versions/language-versions.md new file mode 100644 index 000000000000..2dfd04857cab --- /dev/null +++ b/docs/_spec/TODOreference/language-versions/language-versions.md @@ -0,0 +1,7 @@ +--- +layout: index +title: "Language Versions" +nightlyOf: https://docs.scala-lang.org/scala3/reference/language-versions/index.html +--- + +Additional information on interoperability and migration between Scala 2 and 3 can be found [here](https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html). diff --git a/docs/_spec/TODOreference/language-versions/source-compatibility.md b/docs/_spec/TODOreference/language-versions/source-compatibility.md new file mode 100644 index 000000000000..4d5b468ac8f2 --- /dev/null +++ b/docs/_spec/TODOreference/language-versions/source-compatibility.md @@ -0,0 +1,43 @@ +--- +layout: doc-page +title: "Source Compatibility" +nightlyOf: https://docs.scala-lang.org/scala3/reference/language-versions/source-compatibility.html +--- + +Scala 3 does NOT guarantee source compatibility between different minor language versions (e.g. some syntax valid in 3.x might get deprecated and then phased out in 3.y for y > x). There are also some syntax structures that were valid in Scala 2 but are not anymore in Scala 3. However the compiler provides a possibility to specify the desired version of syntax used in a particular file or globally for a run of the compiler to make migration between versions easier. + +The default Scala language syntax version currently supported by the Dotty compiler is [`3.2`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2$.html). There are also other language versions that can be specified instead: + +- [`3.0-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/0-migration$.html): Same as +`3.0` and `3.1`, but with a Scala 2 compatibility mode that helps moving Scala 2.13 sources over to Scala 3. In particular, it + + - flags some Scala 2 constructs that are disallowed in Scala 3 as migration warnings instead of hard errors, + - changes some rules to be more lenient and backwards compatible with Scala 2.13 + - gives some additional warnings where the semantics has changed between Scala 2.13 and 3.0 + - in conjunction with `-rewrite`, offer code rewrites from Scala 2.13 to 3.0. + +- [`3.0`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/0$.html), [`3.1`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/1$.html): the default set of features included in scala versions `3.0.0` to `3.1.3`. +- [`3.2`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2$.html): the same as `3.0` and `3.1`, but in addition: + - [stricter pattern bindings](https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html) are now enabled (part of `future` in earlier `3.x` releases), producing warnings for refutable patterns. These warnings can be silenced to achieve the same runtime behavior, but in `future` they become errors and refutable patterns will not compile. + - [Nonlocal returns](https://docs.scala-lang.org/scala3/reference/dropped-features/nonlocal-returns.html) now produce a warning upon usage (they are still an error under `future`). +- [`3.2-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$3/2-migration$.html): the same as `3.2`, but in conjunction with `-rewrite`, offer code rewrites from Scala `3.0/3.1` to `3.2`. +- [`future`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future$.html): A preview of changes that will be introduced in `3.x` versions after `3.2`. +Some Scala 2 specific idioms are dropped in this version. The feature set supported by this version may grow over time as features become stabilised for preview. + +- [`future-migration`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$future-migration$.html): Same as `future` but with additional helpers to migrate from `3.2`. Similarly to the helpers available under `3.0-migration`, these include migration warnings and optional rewrites. + +There are two ways to specify a language version : + +- with a `-source` command line setting, e.g. `-source 3.0-migration`. +- with a `scala.language` import at the top of a source file, e.g: + +```scala +package p +import scala.language.`future-migration` + +class C { ... } +``` + +Language imports supersede command-line settings in the source files where they are specified. Only one language import specifying a source version is allowed in a source file, and it must come before any definitions in that file. + +**Note**: The [Scala 3 Migration Guide](https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html) gives further information to help the Scala programmer moving from Scala 2.13 to Scala 3. diff --git a/docs/_spec/TODOreference/metaprogramming/compiletime-ops.md b/docs/_spec/TODOreference/metaprogramming/compiletime-ops.md new file mode 100644 index 000000000000..a43c941ae943 --- /dev/null +++ b/docs/_spec/TODOreference/metaprogramming/compiletime-ops.md @@ -0,0 +1,294 @@ +--- +layout: doc-page +title: "Compile-time operations" +nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/compiletime-ops.html +--- + +## The `scala.compiletime` Package + +The [`scala.compiletime`](https://scala-lang.org/api/3.x/scala/compiletime.html) package contains helper definitions that provide support for compile-time operations over values. They are described in the following. + +### `constValue` and `constValueOpt` + +`constValue` is a function that produces the constant value represented by a +type. + +```scala +import scala.compiletime.constValue +import scala.compiletime.ops.int.S + +transparent inline def toIntC[N]: Int = + inline constValue[N] match + case 0 => 0 + case _: S[n1] => 1 + toIntC[n1] + +inline val ctwo = toIntC[2] +``` + +`constValueOpt` is the same as `constValue`, however returning an `Option[T]` +enabling us to handle situations where a value is not present. Note that `S` is +the type of the successor of some singleton type. For example the type `S[1]` is +the singleton type `2`. + +### `erasedValue` + +So far we have seen inline methods that take terms (tuples and integers) as +parameters. What if we want to base case distinctions on types instead? For +instance, one would like to be able to write a function `defaultValue`, that, +given a type `T`, returns optionally the default value of `T`, if it exists. +We can already express this using rewrite match expressions and a simple +helper function, `scala.compiletime.erasedValue`, which is defined as follows: + +```scala +def erasedValue[T]: T +``` + +The `erasedValue` function _pretends_ to return a value of its type argument `T`. +Calling this function will always result in a compile-time error unless the call +is removed from the code while inlining. + +Using `erasedValue`, we can then define `defaultValue` as follows: + +```scala +import scala.compiletime.erasedValue + +transparent inline def defaultValue[T] = + inline erasedValue[T] match + case _: Byte => Some(0: Byte) + case _: Char => Some(0: Char) + case _: Short => Some(0: Short) + case _: Int => Some(0) + case _: Long => Some(0L) + case _: Float => Some(0.0f) + case _: Double => Some(0.0d) + case _: Boolean => Some(false) + case _: Unit => Some(()) + case _ => None +``` + +Then: + +```scala +val dInt: Some[Int] = defaultValue[Int] +val dDouble: Some[Double] = defaultValue[Double] +val dBoolean: Some[Boolean] = defaultValue[Boolean] +val dAny: None.type = defaultValue[Any] +``` + +As another example, consider the type-level version of `toInt` below: +given a _type_ representing a Peano number, +return the integer _value_ corresponding to it. +Consider the definitions of numbers as in the _Inline +Match_ section above. Here is how `toIntT` can be defined: + +```scala +transparent inline def toIntT[N <: Nat]: Int = + inline scala.compiletime.erasedValue[N] match + case _: Zero.type => 0 + case _: Succ[n] => toIntT[n] + 1 + +inline val two = toIntT[Succ[Succ[Zero.type]]] +``` + +`erasedValue` is an `erased` method so it cannot be used and has no runtime +behavior. Since `toIntT` performs static checks over the static type of `N` we +can safely use it to scrutinize its return type (`S[S[Z]]` in this case). + +### `error` + +The `error` method is used to produce user-defined compile errors during inline expansion. +It has the following signature: + +```scala +inline def error(inline msg: String): Nothing +``` + +If an inline expansion results in a call `error(msgStr)` the compiler +produces an error message containing the given `msgStr`. + +```scala +import scala.compiletime.{error, codeOf} + +inline def fail() = + error("failed for a reason") + +fail() // error: failed for a reason +``` + +or + +```scala +inline def fail(inline p1: Any) = + error("failed on: " + codeOf(p1)) + +fail(identity("foo")) // error: failed on: identity[String]("foo") +``` + +### The `scala.compiletime.ops` package + +The [`scala.compiletime.ops`](https://scala-lang.org/api/3.x/scala/compiletime/ops.html) package contains types that provide support for +primitive operations on singleton types. For example, +`scala.compiletime.ops.int.*` provides support for multiplying two singleton +`Int` types, and `scala.compiletime.ops.boolean.&&` for the conjunction of two +`Boolean` types. When all arguments to a type in `scala.compiletime.ops` are +singleton types, the compiler can evaluate the result of the operation. + +```scala +import scala.compiletime.ops.int.* +import scala.compiletime.ops.boolean.* + +val conjunction: true && true = true +val multiplication: 3 * 5 = 15 +``` + +Many of these singleton operation types are meant to be used infix (as in [SLS §3.2.10](https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#infix-types)). + +Since type aliases have the same precedence rules as their term-level +equivalents, the operations compose with the expected precedence rules: + +```scala +import scala.compiletime.ops.int.* +val x: 1 + 2 * 3 = 7 +``` + +The operation types are located in packages named after the type of the +left-hand side parameter: for instance, `scala.compiletime.ops.int.+` represents +addition of two numbers, while `scala.compiletime.ops.string.+` represents string +concatenation. To use both and distinguish the two types from each other, a +match type can dispatch to the correct implementation: + +```scala +import scala.compiletime.ops.* + +import scala.annotation.infix + +type +[X <: Int | String, Y <: Int | String] = (X, Y) match + case (Int, Int) => int.+[X, Y] + case (String, String) => string.+[X, Y] + +val concat: "a" + "b" = "ab" +val addition: 1 + 1 = 2 +``` + +## Summoning Implicits Selectively + +It is foreseen that many areas of typelevel programming can be done with rewrite +methods instead of implicits. But sometimes implicits are unavoidable. The +problem so far was that the Prolog-like programming style of implicit search +becomes viral: Once some construct depends on implicit search it has to be +written as a logic program itself. Consider for instance the problem of creating +a `TreeSet[T]` or a `HashSet[T]` depending on whether `T` has an `Ordering` or +not. We can create a set of implicit definitions like this: + +```scala +trait SetFor[T, S <: Set[T]] + +class LowPriority: + implicit def hashSetFor[T]: SetFor[T, HashSet[T]] = ... + +object SetsFor extends LowPriority: + implicit def treeSetFor[T: Ordering]: SetFor[T, TreeSet[T]] = ... +``` + +Clearly, this is not pretty. Besides all the usual indirection of implicit +search, we face the problem of rule prioritization where we have to ensure that +`treeSetFor` takes priority over `hashSetFor` if the element type has an +ordering. This is solved (clumsily) by putting `hashSetFor` in a superclass +`LowPriority` of the object `SetsFor` where `treeSetFor` is defined. Maybe the +boilerplate would still be acceptable if the crufty code could be contained. +However, this is not the case. Every user of the abstraction has to be +parameterized itself with a `SetFor` implicit. Considering the simple task _"I +want a `TreeSet[T]` if `T` has an ordering and a `HashSet[T]` otherwise"_, this +seems like a lot of ceremony. + +There are some proposals to improve the situation in specific areas, for +instance by allowing more elaborate schemes to specify priorities. But they all +keep the viral nature of implicit search programs based on logic programming. + +By contrast, the new `summonFrom` construct makes implicit search available +in a functional context. To solve the problem of creating the right set, one +would use it as follows: + +```scala +import scala.compiletime.summonFrom + +inline def setFor[T]: Set[T] = summonFrom { + case ord: Ordering[T] => new TreeSet[T]()(using ord) + case _ => new HashSet[T] +} +``` + +A `summonFrom` call takes a pattern matching closure as argument. All patterns +in the closure are type ascriptions of the form `identifier : Type`. + +Patterns are tried in sequence. The first case with a pattern `x: T` such that an implicit value of type `T` can be summoned is chosen. + +Alternatively, one can also use a pattern-bound given instance, which avoids the explicit using clause. For instance, `setFor` could also be formulated as follows: + +```scala +import scala.compiletime.summonFrom + +inline def setFor[T]: Set[T] = summonFrom { + case given Ordering[T] => new TreeSet[T] + case _ => new HashSet[T] +} +``` + +`summonFrom` applications must be reduced at compile time. + +Consequently, if we summon an `Ordering[String]` the code above will return a +new instance of `TreeSet[String]`. + +```scala +summon[Ordering[String]] + +println(setFor[String].getClass) // prints class scala.collection.immutable.TreeSet +``` + +**Note** `summonFrom` applications can raise ambiguity errors. Consider the following +code with two givens in scope of type `A`. The pattern match in `f` will raise +an ambiguity error of `f` is applied. + +```scala +class A +given a1: A = new A +given a2: A = new A + +inline def f: Any = summonFrom { + case given _: A => ??? // error: ambiguous givens +} +``` + +## `summonInline` + +The shorthand `summonInline` provides a simple way to write a `summon` that is delayed until the call is inlined. +Unlike `summonFrom`, `summonInline` also yields the implicit-not-found error, if a given instance of the summoned +type is not found. +```scala +import scala.compiletime.summonInline +import scala.annotation.implicitNotFound + +@implicitNotFound("Missing One") +trait Missing1 + +@implicitNotFound("Missing Two") +trait Missing2 + +trait NotMissing +given NotMissing = ??? + +transparent inline def summonInlineCheck[T <: Int](inline t : T) : Any = + inline t match + case 1 => summonInline[Missing1] + case 2 => summonInline[Missing2] + case _ => summonInline[NotMissing] + +val missing1 = summonInlineCheck(1) // error: Missing One +val missing2 = summonInlineCheck(2) // error: Missing Two +val notMissing : NotMissing = summonInlineCheck(3) +``` + +## Reference + +For more information about compile-time operations, see [PR #4768](https://github.com/lampepfl/dotty/pull/4768), +which explains how `summonFrom`'s predecessor (implicit matches) can be used for typelevel programming and code specialization and [PR #7201](https://github.com/lampepfl/dotty/pull/7201) which explains the new `summonFrom` syntax. diff --git a/docs/_spec/TODOreference/metaprogramming/inline.md b/docs/_spec/TODOreference/metaprogramming/inline.md new file mode 100644 index 000000000000..0c4800069bad --- /dev/null +++ b/docs/_spec/TODOreference/metaprogramming/inline.md @@ -0,0 +1,390 @@ +--- +layout: doc-page +title: Inline +nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/inline.html +--- + +## Inline Definitions + +`inline` is a new [soft modifier](../soft-modifier.md) that guarantees that a +definition will be inlined at the point of use. Example: + +```scala +object Config: + inline val logging = false + +object Logger: + + private var indent = 0 + + inline def log[T](msg: String, indentMargin: =>Int)(op: => T): T = + if Config.logging then + println(s"${" " * indent}start $msg") + indent += indentMargin + val result = op + indent -= indentMargin + println(s"${" " * indent}$msg = $result") + result + else op +end Logger +``` + +The `Config` object contains a definition of the **inline value** `logging`. +This means that `logging` is treated as a _constant value_, equivalent to its +right-hand side `false`. The right-hand side of such an `inline val` must itself +be a [constant expression](https://scala-lang.org/files/archive/spec/2.13/06-expressions.html#constant-expressions). +Used in this way, `inline` is equivalent to Java and Scala 2's `final`. Note that `final`, meaning +_inlined constant_, is still supported in Scala 3, but will be phased out. + +The `Logger` object contains a definition of the **inline method** `log`. This +method will always be inlined at the point of call. + +In the inlined code, an `if-then-else` with a constant condition will be rewritten +to its `then`- or `else`-part. Consequently, in the `log` method above the +`if Config.logging` with `Config.logging == true` will get rewritten into its +`then`-part. + +Here's an example: + +```scala +var indentSetting = 2 + +def factorial(n: BigInt): BigInt = + log(s"factorial($n)", indentSetting) { + if n == 0 then 1 + else n * factorial(n - 1) + } +``` + +If `Config.logging == false`, this will be rewritten (simplified) to: + +```scala +def factorial(n: BigInt): BigInt = + if n == 0 then 1 + else n * factorial(n - 1) +``` + +As you notice, since neither `msg` or `indentMargin` were used, they do not +appear in the generated code for `factorial`. Also note the body of our `log` +method: the `else-` part reduces to just an `op`. In the generated code we do +not generate any closures because we only refer to a by-name parameter *once*. +Consequently, the code was inlined directly and the call was beta-reduced. + +In the `true` case the code will be rewritten to: + +```scala +def factorial(n: BigInt): BigInt = + val msg = s"factorial($n)" + println(s"${" " * indent}start $msg") + Logger.inline$indent_=(indent.+(indentSetting)) + val result = + if n == 0 then 1 + else n * factorial(n - 1) + Logger.inline$indent_=(indent.-(indentSetting)) + println(s"${" " * indent}$msg = $result") + result +``` + +Note that the by-value parameter `msg` is evaluated only once, per the usual Scala +semantics, by binding the value and reusing the `msg` through the body of +`factorial`. Also, note the special handling of the assignment to the private var +`indent`. It is achieved by generating a setter method `def inline$indent_=` and calling it instead. + +Inline methods always have to be fully applied. For instance, a call to +```scala +Logger.log[String]("some op", indentSetting) +``` +would be ill-formed and the compiler would complain that arguments are missing. +However, it is possible to pass wildcard arguments instead. For instance, +```scala +Logger.log[String]("some op", indentSetting)(_) +``` +would typecheck. + +### Recursive Inline Methods + +Inline methods can be recursive. For instance, when called with a constant +exponent `n`, the following method for `power` will be implemented by +straight inline code without any loop or recursion. + +```scala +inline def power(x: Double, n: Int): Double = + if n == 0 then 1.0 + else if n == 1 then x + else + val y = power(x, n / 2) + if n % 2 == 0 then y * y else y * y * x + +power(expr, 10) +// translates to +// +// val x = expr +// val y1 = x * x // ^2 +// val y2 = y1 * y1 // ^4 +// val y3 = y2 * x // ^5 +// y3 * y3 // ^10 +``` + +Parameters of inline methods can have an `inline` modifier as well. This means +that actual arguments to these parameters will be inlined in the body of the +`inline def`. `inline` parameters have call semantics equivalent to by-name parameters +but allow for duplication of the code in the argument. It is usually useful when constant +values need to be propagated to allow further optimizations/reductions. + +The following example shows the difference in translation between by-value, by-name and `inline` +parameters: + +```scala +inline def funkyAssertEquals(actual: Double, expected: =>Double, inline delta: Double): Unit = + if (actual - expected).abs > delta then + throw new AssertionError(s"difference between ${expected} and ${actual} was larger than ${delta}") + +funkyAssertEquals(computeActual(), computeExpected(), computeDelta()) +// translates to +// +// val actual = computeActual() +// def expected = computeExpected() +// if (actual - expected).abs > computeDelta() then +// throw new AssertionError(s"difference between ${expected} and ${actual} was larger than ${computeDelta()}") +``` + +### Rules for Overriding + +Inline methods can override other non-inline methods. The rules are as follows: + +1. If an inline method `f` implements or overrides another, non-inline method, the inline method can also be invoked at runtime. For instance, consider the scenario: + + ```scala + abstract class A: + def f: Int + def g: Int = f + + class B extends A: + inline def f = 22 + override inline def g = f + 11 + + val b = new B + val a: A = b + // inlined invocatons + assert(b.f == 22) + assert(b.g == 33) + // dynamic invocations + assert(a.f == 22) + assert(a.g == 33) + ``` + + The inlined invocations and the dynamically dispatched invocations give the same results. + +2. Inline methods are effectively final. + +3. Inline methods can also be abstract. An abstract inline method can be implemented only by other inline methods. It cannot be invoked directly: + + ```scala + abstract class A: + inline def f: Int + + object B extends A: + inline def f: Int = 22 + + B.f // OK + val a: A = B + a.f // error: cannot inline f in A. + ``` + +### Relationship to `@inline` + +Scala 2 also defines a `@inline` annotation which is used as a hint for the +backend to inline code. The `inline` modifier is a more powerful option: + +- expansion is guaranteed instead of best effort, +- expansion happens in the frontend instead of in the backend and +- expansion also applies to recursive methods. + + + +### The definition of constant expression + +Right-hand sides of inline values and of arguments for inline parameters must be +constant expressions in the sense defined by the [SLS §6.24](https://www.scala-lang.org/files/archive/spec/2.13/06-expressions.html#constant-expressions), +including _platform-specific_ extensions such as constant folding of pure +numeric computations. + +An inline value must have a literal type such as `1` or `true`. + +```scala +inline val four = 4 +// equivalent to +inline val four: 4 = 4 +``` + +It is also possible to have inline vals of types that do not have a syntax, such as `Short(4)`. + +```scala +trait InlineConstants: + inline val myShort: Short + +object Constants extends InlineConstants: + inline val myShort/*: Short(4)*/ = 4 +``` + +## Transparent Inline Methods + +Inline methods can additionally be declared `transparent`. +This means that the return type of the inline method can be +specialized to a more precise type upon expansion. Example: + +```scala +class A +class B extends A: + def m = true + +transparent inline def choose(b: Boolean): A = + if b then new A else new B + +val obj1 = choose(true) // static type is A +val obj2 = choose(false) // static type is B + +// obj1.m // compile-time error: `m` is not defined on `A` +obj2.m // OK +``` + +Here, the inline method `choose` returns an instance of either of the two types `A` or `B`. +If `choose` had not been declared to be `transparent`, the result +of its expansion would always be of type `A`, even though the computed value might be of the subtype `B`. +The inline method is a "blackbox" in the sense that details of its implementation do not leak out. +But if a `transparent` modifier is given, the expansion is the type of the expanded body. If the argument `b` +is `true`, that type is `A`, otherwise it is `B`. Consequently, calling `m` on `obj2` +type-checks since `obj2` has the same type as the expansion of `choose(false)`, which is `B`. +Transparent inline methods are "whitebox" in the sense that the type +of an application of such a method can be more specialized than its declared +return type, depending on how the method expands. + +In the following example, we see how the return type of `zero` is specialized to +the singleton type `0` permitting the addition to be ascribed with the correct +type `1`. + +```scala +transparent inline def zero: Int = 0 + +val one: 1 = zero + 1 +``` + +### Transparent vs. non-transparent inline + +As we already discussed, transparent inline methods may influence type checking at call site. +Technically this implies that transparent inline methods must be expanded during type checking of the program. +Other inline methods are inlined later after the program is fully typed. + +For example, the following two functions will be typed the same way but will be inlined at different times. + +```scala +inline def f1: T = ... +transparent inline def f2: T = (...): T +``` + +A noteworthy difference is the behavior of `transparent inline given`. +If there is an error reported when inlining that definition, it will be considered as an implicit search mismatch and the search will continue. +A `transparent inline given` can add a type ascription in its RHS (as in `f2` from the previous example) to avoid the precise type but keep the search behavior. +On the other hand, an `inline given` is taken as an implicit and then inlined after typing. +Any error will be emitted as usual. + +## Inline Conditionals + +An if-then-else expression whose condition is a constant expression can be simplified to +the selected branch. Prefixing an if-then-else expression with `inline` enforces that +the condition has to be a constant expression, and thus guarantees that the conditional will always +simplify. + +Example: + +```scala +inline def update(delta: Int) = + inline if delta >= 0 then increaseBy(delta) + else decreaseBy(-delta) +``` + +A call `update(22)` would rewrite to `increaseBy(22)`. But if `update` was called with +a value that was not a compile-time constant, we would get a compile time error like the one +below: + +```scala + | inline if delta >= 0 then ??? + | ^ + | cannot reduce inline if + | its condition + | delta >= 0 + | is not a constant value + | This location is in code that was inlined at ... +``` + +In a transparent inline, an `inline if` will force the inlining of any inline definition in its condition during type checking. + +## Inline Matches + +A `match` expression in the body of an `inline` method definition may be +prefixed by the `inline` modifier. If there is enough type information +at compile time to select a branch, the expression is reduced to that branch and the +type of the expression is the type of the right-hand side of that result. +If not, a compile-time error is raised that reports that the match cannot be reduced. + +The example below defines an inline method with a +single inline match expression that picks a case based on its static type: + +```scala +transparent inline def g(x: Any): Any = + inline x match + case x: String => (x, x) // Tuple2[String, String](x, x) + case x: Double => x + +g(1.0d) // Has type 1.0d which is a subtype of Double +g("test") // Has type (String, String) +``` + +The scrutinee `x` is examined statically and the inline match is reduced +accordingly returning the corresponding value (with the type specialized because `g` is declared `transparent`). +This example performs a simple type test over the scrutinee. +The type can have a richer structure like the simple ADT below. +`toInt` matches the structure of a number in [Church-encoding](https://en.wikipedia.org/wiki/Church_encoding) +and _computes_ the corresponding integer. + +```scala +trait Nat +case object Zero extends Nat +case class Succ[N <: Nat](n: N) extends Nat + +transparent inline def toInt(n: Nat): Int = + inline n match + case Zero => 0 + case Succ(n1) => toInt(n1) + 1 + +inline val natTwo = toInt(Succ(Succ(Zero))) +val intTwo: 2 = natTwo +``` + +`natTwo` is inferred to have the singleton type 2. + +## Reference + +For more information about the semantics of `inline`, see the [Scala 2020: Semantics-preserving inlining for metaprogramming](https://dl.acm.org/doi/10.1145/3426426.3428486) paper. diff --git a/docs/_spec/TODOreference/metaprogramming/macros-spec.md b/docs/_spec/TODOreference/metaprogramming/macros-spec.md new file mode 100644 index 000000000000..aa8f94a9a1f7 --- /dev/null +++ b/docs/_spec/TODOreference/metaprogramming/macros-spec.md @@ -0,0 +1,254 @@ +--- +layout: doc-page +title: "Macros Spec" +nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/macros-spec.html +--- + +## Implementation + +### Syntax + +Compared to the [Scala 3 reference grammar](../syntax.md) +there are the following syntax changes: +``` +SimpleExpr ::= ... + | ‘'’ ‘{’ Block ‘}’ + | ‘'’ ‘[’ Type ‘]’ + | ‘$’ ‘{’ Block ‘}’ +SimpleType ::= ... + | ‘$’ ‘{’ Block ‘}’ +``` +In addition, an identifier `$x` starting with a `$` that appears inside +a quoted expression or type is treated as a splice `${x}` and a quoted identifier +`'x` that appears inside a splice is treated as a quote `'{x}` + +### Implementation in `scalac` + +Quotes and splices are primitive forms in the generated abstract syntax trees. +Top-level splices are eliminated during macro expansion while typing. On the +other hand, top-level quotes are eliminated in an expansion phase `PickleQuotes` +phase (after typing and pickling). PCP checking occurs while preparing the RHS +of an inline method for top-level splices and in the `Staging` phase (after +typing and before pickling). + +Macro-expansion works outside-in. If the outermost scope is a splice, +the spliced AST will be evaluated in an interpreter. A call to a +previously compiled method can be implemented as a reflective call to +that method. With the restrictions on splices that are currently in +place that’s all that’s needed. We might allow more interpretation in +splices in the future, which would allow us to loosen the +restriction. Quotes in spliced, interpreted code are kept as they +are, after splices nested in the quotes are expanded. + +If the outermost scope is a quote, we need to generate code that +constructs the quoted tree at run-time. We implement this by +serializing the tree as a TASTy structure, which is stored +in a string literal. At runtime, an unpickler method is called to +deserialize the string into a tree. + +Splices inside quoted code insert the spliced tree as is, after +expanding any quotes in the spliced code recursively. + +## Formalization + +The phase consistency principle can be formalized in a calculus that +extends simply-typed lambda calculus with quotes and splices. + +### Syntax + +The syntax of terms, values, and types is given as follows: +``` +Terms t ::= x variable + (x: T) => t lambda + t t application + 't quote + $t splice + +Values v ::= (x: T) => t lambda + 'u quote + +Simple terms u ::= x | (x: T) => u | u u | 't + +Types T ::= A base type + T -> T function type + expr T quoted +``` +Typing rules are formulated using a stack of environments +`Es`. Individual environments `E` consist as usual of variable +bindings `x: T`. Environments can be combined using the two +combinators `'` and `$`. +``` +Environment E ::= () empty + E, x: T + +Env. stack Es ::= () empty + E simple + Es * Es combined + +Separator * ::= ' + $ +``` +The two environment combinators are both associative with left and +right identity `()`. + +### Operational semantics + +We define a small step reduction relation `-->` with the following rules: +``` + ((x: T) => t) v --> [x := v]t + + ${'u} --> u + + t1 --> t2 + ----------------- + e[t1] --> e[t2] +``` +The first rule is standard call-by-value beta-reduction. The second +rule says that splice and quotes cancel each other out. The third rule +is a context rule; it says that reduction is allowed in the hole `[ ]` +position of an evaluation context. Evaluation contexts `e` and +splice evaluation context `e_s` are defined syntactically as follows: +``` +Eval context e ::= [ ] | e t | v e | 'e_s[${e}] +Splice context e_s ::= [ ] | (x: T) => e_s | e_s t | u e_s +``` + +### Typing rules + +Typing judgments are of the form `Es |- t: T`. There are two +substructural rules which express the fact that quotes and splices +cancel each other out: +``` + Es1 * Es2 |- t: T + --------------------------- + Es1 $ E1 ' E2 * Es2 |- t: T + + + Es1 * Es2 |- t: T + --------------------------- + Es1 ' E1 $ E2 * Es2 |- t: T +``` +The lambda calculus fragment of the rules is standard, except that we +use a stack of environments. The rules only interact with the topmost +environment of the stack. +``` + x: T in E + -------------- + Es * E |- x: T + + + Es * E, x: T1 |- t: T2 + ------------------------------- + Es * E |- (x: T1) => t: T -> T2 + + + Es |- t1: T2 -> T Es |- t2: T2 + --------------------------------- + Es |- t1 t2: T +``` +The rules for quotes and splices map between `expr T` and `T` by trading `'` and `$` between +environments and terms. +``` + Es $ () |- t: expr T + -------------------- + Es |- $t: T + + + Es ' () |- t: T + ---------------- + Es |- 't: expr T +``` +The meta theory of a slightly simplified 2-stage variant of this calculus +is studied [separately](./simple-smp.md). + +## Going Further + +The metaprogramming framework as presented and currently implemented is quite restrictive +in that it does not allow for the inspection of quoted expressions and +types. It’s possible to work around this by providing all necessary +information as normal, unquoted inline parameters. But we would gain +more flexibility by allowing for the inspection of quoted code with +pattern matching. This opens new possibilities. + +For instance, here is a version of `power` that generates the multiplications +directly if the exponent is statically known and falls back to the dynamic +implementation of `power` otherwise. +```scala +import scala.quoted.* + +inline def power(x: Double, n: Int): Double = + ${ powerExpr('x, 'n) } + +private def powerExpr(x: Expr[Double], n: Expr[Int]) + (using Quotes): Expr[Double] = + n.value match + case Some(m) => powerExpr(x, m) + case _ => '{ dynamicPower($x, $n) } + +private def powerExpr(x: Expr[Double], n: Int) + (using Quotes): Expr[Double] = + if n == 0 then '{ 1.0 } + else if n == 1 then x + else if n % 2 == 0 then '{ val y = $x * $x; ${ powerExpr('y, n / 2) } } + else '{ $x * ${ powerExpr(x, n - 1) } } + +private def dynamicPower(x: Double, n: Int): Double = + if n == 0 then 1.0 + else if n % 2 == 0 then dynamicPower(x * x, n / 2) + else x * dynamicPower(x, n - 1) +``` + +In the above, the method `.value` maps a constant expression of the type +`Expr[T]` to its value of the type `T`. + +With the right extractors, the "AsFunction" conversion +that maps expressions over functions to functions over expressions can +be implemented in user code: +```scala +given AsFunction1[T, U]: Conversion[Expr[T => U], Expr[T] => Expr[U]] with + def apply(f: Expr[T => U]): Expr[T] => Expr[U] = + (x: Expr[T]) => f match + case Lambda(g) => g(x) + case _ => '{ ($f)($x) } +``` +This assumes an extractor +```scala +object Lambda: + def unapply[T, U](x: Expr[T => U]): Option[Expr[T] => Expr[U]] +``` +Once we allow inspection of code via extractors, it’s tempting to also +add constructors that create typed trees directly without going +through quotes. Most likely, those constructors would work over `Expr` +types which lack a known type argument. For instance, an `Apply` +constructor could be typed as follows: +```scala +def Apply(fn: Expr[Any], args: List[Expr[Any]]): Expr[Any] +``` +This would allow constructing applications from lists of arguments +without having to match the arguments one-by-one with the +corresponding formal parameter types of the function. We then need "at +the end" a method to convert an `Expr[Any]` to an `Expr[T]` where `T` is +given from the outside. For instance, if `code` yields a `Expr[Any]`, then +`code.atType[T]` yields an `Expr[T]`. The `atType` method has to be +implemented as a primitive; it would check that the computed type +structure of `Expr` is a subtype of the type structure representing +`T`. + +Before going down that route, we should evaluate in detail the tradeoffs it +presents. Constructing trees that are only verified _a posteriori_ +to be type correct loses a lot of guidance for constructing the right +trees. So we should wait with this addition until we have more +use-cases that help us decide whether the loss in type-safety is worth +the gain in flexibility. In this context, it seems that deconstructing types is +less error-prone than deconstructing terms, so one might also +envisage a solution that allows the former but not the latter. + +## Conclusion + +Metaprogramming has a reputation of being difficult and confusing. +But with explicit `Expr/Type` types and quotes and splices it can become +downright pleasant. A simple strategy first defines the underlying quoted or unquoted +values using `Expr` and `Type` and then inserts quotes and splices to make the types +line up. Phase consistency is at the same time a great guideline +where to insert a splice or a quote and a vital sanity check that +the result makes sense. diff --git a/docs/_spec/TODOreference/metaprogramming/macros.md b/docs/_spec/TODOreference/metaprogramming/macros.md new file mode 100644 index 000000000000..8045794d1143 --- /dev/null +++ b/docs/_spec/TODOreference/metaprogramming/macros.md @@ -0,0 +1,823 @@ +--- +layout: doc-page +title: "Macros" +nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/macros.html +--- + +> When developing macros enable `-Xcheck-macros` scalac option flag to have extra runtime checks. + +## Macros: Quotes and Splices + +Macros are built on two well-known fundamental operations: quotation and splicing. +Quotation is expressed as `'{...}` for expressions and splicing is expressed as `${ ... }`. +Additionally, within a quote or a splice we can quote or splice identifiers directly (i.e. `'e` and `$e`). +Readers may notice the resemblance of the two aforementioned syntactic +schemes with the familiar string interpolation syntax. + +```scala +println(s"Hello, $name, here is the result of 1 + 1 = ${1 + 1}") +``` + +In string interpolation we _quoted_ a string and then we _spliced_ into it, two others. The first, `name`, is a reference to a value of type [`String`](https://scala-lang.org/api/3.x/scala/Predef$.html#String-0), and the second is an arithmetic expression that will be _evaluated_ followed by the splicing of its string representation. + +Quotes and splices in this section allow us to treat code in a similar way, +effectively supporting macros. The entry point for macros is an inline method +with a top-level splice. We call it a top-level because it is the only occasion +where we encounter a splice outside a quote (consider as a quote the +compilation-unit at the call-site). For example, the code below presents an +`inline` method `assert` which calls at compile-time a method `assertImpl` with +a boolean expression tree as argument. `assertImpl` evaluates the expression and +prints it again in an error message if it evaluates to `false`. + +```scala +import scala.quoted.* + +inline def assert(inline expr: Boolean): Unit = + ${ assertImpl('expr) } + +def assertImpl(expr: Expr[Boolean])(using Quotes) = '{ + if !$expr then + throw AssertionError(s"failed assertion: ${${ showExpr(expr) }}") +} + +def showExpr(expr: Expr[Boolean])(using Quotes): Expr[String] = + '{ [actual implementation later in this document] } +``` + +If `e` is an expression, then `'{e}` represents the typed +abstract syntax tree representing `e`. If `T` is a type, then `Type.of[T]` +represents the type structure representing `T`. The precise +definitions of "typed abstract syntax tree" or "type-structure" do not +matter for now, the terms are used only to give some +intuition. Conversely, `${e}` evaluates the expression `e`, which must +yield a typed abstract syntax tree or type structure, and embeds the +result as an expression (respectively, type) in the enclosing program. + +Quotations can have spliced parts in them; in this case the embedded +splices are evaluated and embedded as part of the formation of the +quotation. + +Quotes and splices can also be applied directly to identifiers. An identifier +`$x` starting with a `$` that appears inside a quoted expression or type is treated as a +splice `${x}`. Analogously, an quoted identifier `'x` that appears inside a splice +is treated as a quote `'{x}`. See the Syntax section below for details. + +Quotes and splices are duals of each other. +For arbitrary expressions `e` we have: + +```scala +${'{e}} = e +'{${e}} = e +``` + +## Types for Quotations + +The type signatures of quotes and splices can be described using +two fundamental types: + +- `Expr[T]`: abstract syntax trees representing expressions of type `T` +- `Type[T]`: non erased representation of type `T`. + +Quoting takes expressions of type `T` to expressions of type `Expr[T]` +and it takes types `T` to expressions of type `Type[T]`. Splicing +takes expressions of type `Expr[T]` to expressions of type `T` and it +takes expressions of type `Type[T]` to types `T`. + +The two types can be defined in package [`scala.quoted`](https://scala-lang.org/api/3.x/scala/quoted.html) as follows: + +```scala +package scala.quoted + +sealed trait Expr[+T] +sealed trait Type[T] +``` + +Both `Expr` and `Type` are abstract and sealed, so all constructors for +these types are provided by the system. One way to construct values of +these types is by quoting, the other is by type-specific lifting +operations that will be discussed later on. + +## The Phase Consistency Principle + +A fundamental *phase consistency principle* (PCP) regulates accesses +to free variables in quoted and spliced code: + +- _For any free variable reference `x`, the number of quoted scopes and the number of spliced scopes between the reference to `x` and the definition of `x` must be equal_. + +Here, `this`-references count as free variables. On the other +hand, we assume that all imports are fully expanded and that `_root_` is +not a free variable. So references to global definitions are +allowed everywhere. + +The phase consistency principle can be motivated as follows: First, +suppose the result of a program `P` is some quoted text `'{ ... x +... }` that refers to a free variable `x` in `P`. This can be +represented only by referring to the original variable `x`. Hence, the +result of the program will need to persist the program state itself as +one of its parts. We don’t want to do this, hence this situation +should be made illegal. Dually, suppose a top-level part of a program +is a spliced text `${ ... x ... }` that refers to a free variable `x` +in `P`. This would mean that we refer during _construction_ of `P` to +a value that is available only during _execution_ of `P`. This is of +course impossible and therefore needs to be ruled out. Now, the +small-step evaluation of a program will reduce quotes and splices in +equal measure using the cancellation rules above. But it will neither +create nor remove quotes or splices individually. So the PCP ensures +that program elaboration will lead to neither of the two unwanted +situations described above. + +In what concerns the range of features it covers, this form of macros introduces +a principled metaprogramming framework that is quite close to the MetaML family of +languages. One difference is that MetaML does not have an equivalent of the PCP - +quoted code in MetaML _can_ access variables in its immediately enclosing +environment, with some restrictions and caveats since such accesses involve +serialization. However, this does not constitute a fundamental gain in +expressiveness. + +## From `Expr`s to Functions and Back + +It is possible to convert any `Expr[T => R]` into `Expr[T] => Expr[R]` and back. +These conversions can be implemented as follows: + +```scala +def to[T: Type, R: Type](f: Expr[T] => Expr[R])(using Quotes): Expr[T => R] = + '{ (x: T) => ${ f('x) } } + +def from[T: Type, R: Type](f: Expr[T => R])(using Quotes): Expr[T] => Expr[R] = + (x: Expr[T]) => '{ $f($x) } +``` + +Note how the fundamental phase consistency principle works in two +different directions here for `f` and `x`. In the method `to`, the reference to `f` is +legal because it is quoted, then spliced, whereas the reference to `x` +is legal because it is spliced, then quoted. + +They can be used as follows: + +```scala +val f1: Expr[Int => String] = + to((x: Expr[Int]) => '{ $x.toString }) // '{ (x: Int) => x.toString } + +val f2: Expr[Int] => Expr[String] = + from('{ (x: Int) => x.toString }) // (x: Expr[Int]) => '{ ((x: Int) => x.toString)($x) } +f2('{2}) // '{ ((x: Int) => x.toString)(2) } +``` + +One limitation of `from` is that it does not β-reduce when a lambda is called immediately, as evidenced in the code `{ ((x: Int) => x.toString)(2) }`. +In some cases we want to remove the lambda from the code, for this we provide the method `Expr.betaReduce` that turns a tree +describing a function into a function mapping trees to trees. + +```scala +object Expr: + ... + def betaReduce[...](...)(...): ... = ... +``` + +The definition of `Expr.betaReduce(f)(x)` is assumed to be functionally the same as +`'{($f)($x)}`, however it should optimize this call by returning the +result of beta-reducing `f(x)` if `f` is a known lambda expression. +`Expr.betaReduce` distributes applications of `Expr` over function arrows: + +```scala +Expr.betaReduce(_): Expr[(T1, ..., Tn) => R] => ((Expr[T1], ..., Expr[Tn]) => Expr[R]) +``` + +## Lifting Types + +Types are not directly affected by the phase consistency principle. +It is possible to use types defined at any level in any other level. +But, if a type is used in a subsequent stage it will need to be lifted to a `Type`. +Indeed, the definition of `to` above uses `T` in the next stage, there is a +quote but no splice between the parameter binding of `T` and its +usage. But the code can be rewritten by adding an explicit binding of a `Type[T]`: + +```scala +def to[T, R](f: Expr[T] => Expr[R])(using t: Type[T])(using Type[R], Quotes): Expr[T => R] = + '{ (x: t.Underlying) => ${ f('x) } } +``` + +In this version of `to`, the type of `x` is now the result of +inserting the type `Type[T]` and selecting its `Underlying`. + +To avoid clutter, the compiler converts any type reference to +a type `T` in subsequent phases to `summon[Type[T]].Underlying`. + +And to avoid duplication it does it once per type, and creates +an alias for that type at the start of the quote. + +For instance, the user-level definition of `to`: + +```scala +def to[T, R](f: Expr[T] => Expr[R])(using t: Type[T], r: Type[R])(using Quotes): Expr[T => R] = + '{ (x: T) => ${ f('x) } } +``` + +would be rewritten to + +```scala +def to[T, R](f: Expr[T] => Expr[R])(using t: Type[T], r: Type[R])(using Quotes): Expr[T => R] = + '{ + type T = t.Underlying + (x: T) => ${ f('x) } + } +``` + +The `summon` query succeeds because there is a given instance of +type `Type[T]` available (namely the given parameter corresponding +to the context bound `: Type`), and the reference to that value is +phase-correct. If that was not the case, the phase inconsistency for +`T` would be reported as an error. + +## Lifting Expressions + +Consider the following implementation of a staged interpreter that implements +a compiler through staging. + +```scala +import scala.quoted.* + +enum Exp: + case Num(n: Int) + case Plus(e1: Exp, e2: Exp) + case Var(x: String) + case Let(x: String, e: Exp, in: Exp) + +import Exp.* +``` + +The interpreted language consists of numbers `Num`, addition `Plus`, and variables +`Var` which are bound by `Let`. Here are two sample expressions in the language: + +```scala +val exp = Plus(Plus(Num(2), Var("x")), Num(4)) +val letExp = Let("x", Num(3), exp) +``` + +Here’s a compiler that maps an expression given in the interpreted +language to quoted Scala code of type `Expr[Int]`. +The compiler takes an environment that maps variable names to Scala `Expr`s. + +```scala +import scala.quoted.* + +def compile(e: Exp, env: Map[String, Expr[Int]])(using Quotes): Expr[Int] = + e match + case Num(n) => + Expr(n) + case Plus(e1, e2) => + '{ ${ compile(e1, env) } + ${ compile(e2, env) } } + case Var(x) => + env(x) + case Let(x, e, body) => + '{ val y = ${ compile(e, env) }; ${ compile(body, env + (x -> 'y)) } } +``` + +Running `compile(letExp, Map())` would yield the following Scala code: + +```scala +'{ val y = 3; (2 + y) + 4 } +``` + +The body of the first clause, `case Num(n) => Expr(n)`, looks suspicious. `n` +is declared as an `Int`, yet it is converted to an `Expr[Int]` with `Expr()`. +Shouldn’t `n` be quoted? In fact this would not +work since replacing `n` by `'n` in the clause would not be phase +correct. + +The `Expr.apply` method is defined in package `quoted`: + +```scala +package quoted + +object Expr: + ... + def apply[T: ToExpr](x: T)(using Quotes): Expr[T] = + summon[ToExpr[T]].toExpr(x) +``` + +This method says that values of types implementing the `ToExpr` type class can be +converted to `Expr` values using `Expr.apply`. + +Scala 3 comes with given instances of `ToExpr` for +several types including `Boolean`, `String`, and all primitive number +types. For example, `Int` values can be converted to `Expr[Int]` +values by wrapping the value in a `Literal` tree node. This makes use +of the underlying tree representation in the compiler for +efficiency. But the `ToExpr` instances are nevertheless not _magic_ +in the sense that they could all be defined in a user program without +knowing anything about the representation of `Expr` trees. For +instance, here is a possible instance of `ToExpr[Boolean]`: + +```scala +given ToExpr[Boolean] with + def toExpr(b: Boolean) = + if b then '{ true } else '{ false } +``` + +Once we can lift bits, we can work our way up. For instance, here is a +possible implementation of `ToExpr[Int]` that does not use the underlying +tree machinery: + +```scala +given ToExpr[Int] with + def toExpr(n: Int) = n match + case Int.MinValue => '{ Int.MinValue } + case _ if n < 0 => '{ - ${ toExpr(-n) } } + case 0 => '{ 0 } + case _ if n % 2 == 0 => '{ ${ toExpr(n / 2) } * 2 } + case _ => '{ ${ toExpr(n / 2) } * 2 + 1 } +``` + +Since `ToExpr` is a type class, its instances can be conditional. For example, +a `List` is liftable if its element type is: + +```scala +given [T: ToExpr : Type]: ToExpr[List[T]] with + def toExpr(xs: List[T]) = xs match + case head :: tail => '{ ${ Expr(head) } :: ${ toExpr(tail) } } + case Nil => '{ Nil: List[T] } +``` + +In the end, `ToExpr` resembles very much a serialization +framework. Like the latter it can be derived systematically for all +collections, case classes and enums. Note also that the synthesis +of _type-tag_ values of type `Type[T]` is essentially the type-level +analogue of lifting. + +Using lifting, we can now give the missing definition of `showExpr` in the introductory example: + +```scala +def showExpr[T](expr: Expr[T])(using Quotes): Expr[String] = + val code: String = expr.show + Expr(code) +``` + +That is, the `showExpr` method converts its `Expr` argument to a string (`code`), and lifts +the result back to an `Expr[String]` using `Expr.apply`. + +## Lifting Types + +The previous section has shown that the metaprogramming framework has +to be able to take a type `T` and convert it to a type tree of type +`Type[T]` that can be reified. This means that all free variables of +the type tree refer to types and values defined in the current stage. + +For a reference to a global class, this is easy: Just issue the fully +qualified name of the class. Members of reifiable types are handled by +just reifying the containing type together with the member name. But +what to do for references to type parameters or local type definitions +that are not defined in the current stage? Here, we cannot construct +the `Type[T]` tree directly, so we need to get it from a recursive +implicit search. For instance, to implement + +```scala +summon[Type[List[T]]] +``` + +where `T` is not defined in the current stage, we construct the type constructor +of `List` applied to the splice of the result of searching for a given instance for `Type[T]`: + +```scala +Type.of[ List[ summon[Type[T]].Underlying ] ] +``` + +This is exactly the algorithm that Scala 2 uses to search for type tags. +In fact Scala 2's type tag feature can be understood as a more ad-hoc version of +`quoted.Type`. As was the case for type tags, the implicit search for a `quoted.Type` +is handled by the compiler, using the algorithm sketched above. + +## Relationship with `inline` + +Seen by itself, principled metaprogramming looks more like a framework for +runtime metaprogramming than one for compile-time metaprogramming with macros. +But combined with Scala 3’s `inline` feature it can be turned into a compile-time +system. The idea is that macro elaboration can be understood as a combination of +a macro library and a quoted program. For instance, here’s the `assert` macro +again together with a program that calls `assert`. + +```scala +object Macros: + + inline def assert(inline expr: Boolean): Unit = + ${ assertImpl('expr) } + + def assertImpl(expr: Expr[Boolean])(using Quotes) = + val failMsg: Expr[String] = Expr("failed assertion: " + expr.show) + '{ if !($expr) then throw new AssertionError($failMsg) } + +@main def program = + val x = 1 + Macros.assert(x != 0) +``` + +Inlining the `assert` function would give the following program: + +```scala +@main def program = + val x = 1 + ${ Macros.assertImpl('{ x != 0}) } +``` + +The example is only phase correct because `Macros` is a global value and +as such not subject to phase consistency checking. Conceptually that’s +a bit unsatisfactory. If the PCP is so fundamental, it should be +applicable without the global value exception. But in the example as +given this does not hold since both `assert` and `program` call +`assertImpl` with a splice but no quote. + +However, one could argue that the example is really missing +an important aspect: The macro library has to be compiled in a phase +prior to the program using it, but in the code above, macro +and program are defined together. A more accurate view of +macros would be to have the user program be in a phase after the macro +definitions, reflecting the fact that macros have to be defined and +compiled before they are used. Hence, conceptually the program part +should be treated by the compiler as if it was quoted: + +```scala +@main def program = '{ + val x = 1 + ${ Macros.assertImpl('{ x != 0 }) } +} +``` + +If `program` is treated as a quoted expression, the call to +`Macro.assertImpl` becomes phase correct even if macro library and +program are conceptualized as local definitions. + +But what about the call from `assert` to `assertImpl`? Here, we need a +tweak of the typing rules. An inline function such as `assert` that +contains a splice operation outside an enclosing quote is called a +_macro_. Macros are supposed to be expanded in a subsequent phase, +i.e. in a quoted context. Therefore, they are also type checked as if +they were in a quoted context. For instance, the definition of +`assert` is typechecked as if it appeared inside quotes. This makes +the call from `assert` to `assertImpl` phase-correct, even if we +assume that both definitions are local. + +The `inline` modifier is used to declare a `val` that is +either a constant or is a parameter that will be a constant when instantiated. This +aspect is also important for macro expansion. + +To get values out of expressions containing constants `Expr` provides the method +`value` (or `valueOrError`). This will convert the `Expr[T]` into a `Some[T]` (or `T`) when the +expression contains value. Otherwise it will return `None` (or emit an error). +To avoid having incidental val bindings generated by the inlining of the `def` +it is recommended to use an inline parameter. To illustrate this, consider an +implementation of the `power` function that makes use of a statically known exponent: + +```scala +inline def power(x: Double, inline n: Int) = ${ powerCode('x, 'n) } + +private def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = + n.value match + case Some(m) => powerCode(x, m) + case None => '{ Math.pow($x, $n.toDouble) } + +private def powerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = + if n == 0 then '{ 1.0 } + else if n == 1 then x + else if n % 2 == 0 then '{ val y = $x * $x; ${ powerCode('y, n / 2) } } + else '{ $x * ${ powerCode(x, n - 1) } } +``` + +## Scope Extrusion + +Quotes and splices are duals as far as the PCP is concerned. But there is an +additional restriction that needs to be imposed on splices to guarantee +soundness: code in splices must be free of side effects. The restriction +prevents code like this: + +```scala +var x: Expr[T] = ... +'{ (y: T) => ${ x = 'y; 1 } } +``` + +This code, if it was accepted, would _extrude_ a reference to a quoted variable +`y` from its scope. This would subsequently allow access to a variable outside the +scope where it is defined, which is likely problematic. The code is clearly +phase consistent, so we cannot use PCP to rule it out. Instead, we postulate a +future effect system that can guarantee that splices are pure. In the absence of +such a system we simply demand that spliced expressions are pure by convention, +and allow for undefined compiler behavior if they are not. This is analogous to +the status of pattern guards in Scala, which are also required, but not +verified, to be pure. + +[Multi-Stage Programming](./staging.md) introduces one additional method where +you can expand code at runtime with a method `run`. There is also a problem with +that invocation of `run` in splices. Consider the following expression: + +```scala +'{ (x: Int) => ${ run('x); 1 } } +``` + +This is again phase correct, but will lead us into trouble. Indeed, evaluating +the splice will reduce the expression `run('x)` to `x`. But then the result + +```scala +'{ (x: Int) => ${ x; 1 } } +``` + +is no longer phase correct. To prevent this soundness hole it seems easiest to +classify `run` as a side-effecting operation. It would thus be prevented from +appearing in splices. In a base language with side effects we would have to do this +anyway: Since `run` runs arbitrary code it can always produce a side effect if +the code it runs produces one. + +## Example Expansion + +Assume we have two methods, one `map` that takes an `Expr[Array[T]]` and a +function `f` and one `sum` that performs a sum by delegating to `map`. + +```scala +object Macros: + + def map[T](arr: Expr[Array[T]], f: Expr[T] => Expr[Unit]) + (using Type[T], Quotes): Expr[Unit] = '{ + var i: Int = 0 + while i < ($arr).length do + val element: T = ($arr)(i) + ${f('element)} + i += 1 + } + + def sum(arr: Expr[Array[Int]])(using Quotes): Expr[Int] = '{ + var sum = 0 + ${ map(arr, x => '{sum += $x}) } + sum + } + + inline def sum_m(arr: Array[Int]): Int = ${sum('arr)} + +end Macros +``` + +A call to `sum_m(Array(1,2,3))` will first inline `sum_m`: + +```scala +val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*) +${_root_.Macros.sum('arr)} +``` + +then it will splice `sum`: + +```scala +val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*) + +var sum = 0 +${ map('arr, x => '{sum += $x}) } +sum +``` + +then it will inline `map`: + +```scala +val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*) + +var sum = 0 +val f = x => '{sum += $x} +${ _root_.Macros.map('arr, 'f)(Type.of[Int])} +sum +``` + +then it will expand and splice inside quotes `map`: + +```scala +val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*) + +var sum = 0 +val f = x => '{sum += $x} +var i: Int = 0 +while i < arr.length do + val element: Int = (arr)(i) + sum += element + i += 1 +sum +``` + +Finally cleanups and dead code elimination: + +```scala +val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*) +var sum = 0 +var i: Int = 0 +while i < arr.length do + val element: Int = arr(i) + sum += element + i += 1 +sum +``` + +## Find implicits within a macro + +Similarly to the `summonFrom` construct, it is possible to make implicit search available +in a quote context. For this we simply provide `scala.quoted.Expr.summon`: + +```scala +import scala.collection.immutable.{ TreeSet, HashSet } +inline def setFor[T]: Set[T] = ${ setForExpr[T] } + +def setForExpr[T: Type](using Quotes): Expr[Set[T]] = + Expr.summon[Ordering[T]] match + case Some(ord) => '{ new TreeSet[T]()($ord) } + case _ => '{ new HashSet[T] } +``` + +## Relationship with Transparent Inline + +[Inline](./inline.md) documents inlining. The code below introduces a transparent +inline method that can calculate either a value of type `Int` or a value of type +`String`. + +```scala +transparent inline def defaultOf(inline str: String) = + ${ defaultOfImpl('str) } + +def defaultOfImpl(strExpr: Expr[String])(using Quotes): Expr[Any] = + strExpr.valueOrError match + case "int" => '{1} + case "string" => '{"a"} + +// in a separate file +val a: Int = defaultOf("int") +val b: String = defaultOf("string") + +``` + +## Defining a macro and using it in a single project + +It is possible to define macros and use them in the same project as long as the implementation +of the macros does not have run-time dependencies on code in the file where it is used. +It might still have compile-time dependencies on types and quoted code that refers to the use-site file. + +To provide this functionality Scala 3 provides a transparent compilation mode where files that +try to expand a macro but fail because the macro has not been compiled yet are suspended. +If there are any suspended files when the compilation ends, the compiler will automatically restart +compilation of the suspended files using the output of the previous (partial) compilation as macro classpath. +In case all files are suspended due to cyclic dependencies the compilation will fail with an error. + +## Pattern matching on quoted expressions + +It is possible to deconstruct or extract values out of `Expr` using pattern matching. + +`scala.quoted` contains objects that can help extracting values from `Expr`. + +- `scala.quoted.Expr`/`scala.quoted.Exprs`: matches an expression of a value (or list of values) and returns the value (or list of values). +- `scala.quoted.Const`/`scala.quoted.Consts`: Same as `Expr`/`Exprs` but only works on primitive values. +- `scala.quoted.Varargs`: matches an explicit sequence of expressions and returns them. These sequences are useful to get individual `Expr[T]` out of a varargs expression of type `Expr[Seq[T]]`. + +These could be used in the following way to optimize any call to `sum` that has statically known values. + +```scala +inline def sum(inline args: Int*): Int = ${ sumExpr('args) } +private def sumExpr(argsExpr: Expr[Seq[Int]])(using Quotes): Expr[Int] = + argsExpr match + case Varargs(args @ Exprs(argValues)) => + // args is of type Seq[Expr[Int]] + // argValues is of type Seq[Int] + Expr(argValues.sum) // precompute result of sum + case Varargs(argExprs) => // argExprs is of type Seq[Expr[Int]] + val staticSum: Int = argExprs.map(_.value.getOrElse(0)).sum + val dynamicSum: Seq[Expr[Int]] = argExprs.filter(_.value.isEmpty) + dynamicSum.foldLeft(Expr(staticSum))((acc, arg) => '{ $acc + $arg }) + case _ => + '{ $argsExpr.sum } +``` + +### Quoted patterns + +Quoted pattens allow deconstructing complex code that contains a precise structure, types or methods. +Patterns `'{ ... }` can be placed in any location where Scala expects a pattern. + +For example + +```scala +optimize { + sum(sum(1, a, 2), 3, b) +} // should be optimized to 6 + a + b +``` + +```scala +def sum(args: Int*): Int = args.sum +inline def optimize(inline arg: Int): Int = ${ optimizeExpr('arg) } +private def optimizeExpr(body: Expr[Int])(using Quotes): Expr[Int] = + body match + // Match a call to sum without any arguments + case '{ sum() } => Expr(0) + // Match a call to sum with an argument $n of type Int. + // n will be the Expr[Int] representing the argument. + case '{ sum($n) } => n + // Match a call to sum and extracts all its args in an `Expr[Seq[Int]]` + case '{ sum(${Varargs(args)}: _*) } => sumExpr(args) + case body => body + +private def sumExpr(args1: Seq[Expr[Int]])(using Quotes): Expr[Int] = + def flatSumArgs(arg: Expr[Int]): Seq[Expr[Int]] = arg match + case '{ sum(${Varargs(subArgs)}: _*) } => subArgs.flatMap(flatSumArgs) + case arg => Seq(arg) + val args2 = args1.flatMap(flatSumArgs) + val staticSum: Int = args2.map(_.value.getOrElse(0)).sum + val dynamicSum: Seq[Expr[Int]] = args2.filter(_.value.isEmpty) + dynamicSum.foldLeft(Expr(staticSum))((acc, arg) => '{ $acc + $arg }) +``` + +### Recovering precise types using patterns + +Sometimes it is necessary to get a more precise type for an expression. This can be achieved using the following pattern match. + +```scala +def f(expr: Expr[Any])(using Quotes) = expr match + case '{ $x: t } => + // If the pattern match succeeds, then there is + // some type `t` such that + // - `x` is bound to a variable of type `Expr[t]` + // - `t` is bound to a new type `t` and a given + // instance `Type[t]` is provided for it + // That is, we have `x: Expr[t]` and `given Type[t]`, + // for some (unknown) type `t`. +``` + +This might be used to then perform an implicit search as in: + +```scala +extension (inline sc: StringContext) + inline def showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } + +private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using Quotes): Expr[String] = + import quotes.reflect.report + argsExpr match + case Varargs(argExprs) => + val argShowedExprs = argExprs.map { + case '{ $arg: tp } => + Expr.summon[Show[tp]] match + case Some(showExpr) => + '{ $showExpr.show($arg) } + case None => + report.error(s"could not find implicit for ${Type.show[Show[tp]]}", arg); '{???} + } + val newArgsExpr = Varargs(argShowedExprs) + '{ $sc.s($newArgsExpr: _*) } + case _ => + // `new StringContext(...).showMeExpr(args: _*)` not an explicit `showMeExpr"..."` + report.error(s"Args must be explicit", argsExpr) + '{???} + +trait Show[-T]: + def show(x: T): String + +// in a different file +given Show[Boolean] with + def show(b: Boolean) = "boolean!" + +println(showMe"${true}") +``` + +### Open code patterns + +Quoted pattern matching also provides higher-order patterns to match open terms. If a quoted term contains a definition, +then the rest of the quote can refer to this definition. + +```scala +'{ + val x: Int = 4 + x * x +} +``` + +To match such a term we need to match the definition and the rest of the code, but we need to explicitly state that the rest of the code may refer to this definition. + +```scala +case '{ val y: Int = $x; $body(y): Int } => +``` + +Here `$x` will match any closed expression while `$body(y)` will match an expression that is closed under `y`. Then +the subexpression of type `Expr[Int]` is bound to `body` as an `Expr[Int => Int]`. The extra argument represents the references to `y`. Usually this expression is used in combination with `Expr.betaReduce` to replace the extra argument. + +```scala +inline def eval(inline e: Int): Int = ${ evalExpr('e) } + +private def evalExpr(e: Expr[Int])(using Quotes): Expr[Int] = e match + case '{ val y: Int = $x; $body(y): Int } => + // body: Expr[Int => Int] where the argument represents + // references to y + evalExpr(Expr.betaReduce('{$body(${evalExpr(x)})})) + case '{ ($x: Int) * ($y: Int) } => + (x.value, y.value) match + case (Some(a), Some(b)) => Expr(a * b) + case _ => e + case _ => e +``` + +```scala +eval { // expands to the code: (16: Int) + val x: Int = 4 + x * x +} +``` + +We can also close over several bindings using `$b(a1, a2, ..., an)`. +To match an actual application we can use braces on the function part `${b}(a1, a2, ..., an)`. + +## More details + +[More details](./macros-spec.md) diff --git a/docs/_spec/TODOreference/metaprogramming/metaprogramming.md b/docs/_spec/TODOreference/metaprogramming/metaprogramming.md new file mode 100644 index 000000000000..3bce2d7c922e --- /dev/null +++ b/docs/_spec/TODOreference/metaprogramming/metaprogramming.md @@ -0,0 +1,47 @@ +--- +layout: index +title: "Metaprogramming" +nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming.html +--- + +The following pages introduce the redesign of metaprogramming in Scala. They +introduce the following fundamental facilities: + +1. [`inline`](./inline.md) is a new modifier that guarantees that + a definition will be inlined at the point of use. The primary motivation + behind inline is to reduce the overhead behind function calls and access to + values. The expansion will be performed by the Scala compiler during the + `Typer` compiler phase. As opposed to inlining in some other ecosystems, + inlining in Scala is not merely a request to the compiler but is a + _command_. The reason is that inlining in Scala can drive other compile-time + operations, like inline pattern matching (enabling type-level + programming), macros (enabling compile-time, generative, metaprogramming) and + runtime code generation (multi-stage programming). + +2. [Compile-time ops](./compiletime-ops.md) are helper definitions in the + standard library that provide support for compile-time operations over values and types. + +3. [Macros](./macros.md) are built on two well-known fundamental + operations: quotation and splicing. Quotation converts program code to + data, specifically, a (tree-like) representation of this code. It is + expressed as `'{...}` for expressions and as `'[...]` for types. Splicing, + expressed as `${ ... }`, goes the other way: it converts a program's representation + to program code. Together with `inline`, these two abstractions allow + to construct program code programmatically. + +4. [Runtime Staging](./staging.md) Where macros construct code at _compile-time_, + staging lets programs construct new code at _runtime_. That way, + code generation can depend not only on static data but also on data available at runtime. This splits the evaluation of the program in two or more phases or ... + stages. Consequently, this method of generative programming is called "Multi-Stage Programming". Staging is built on the same foundations as macros. It uses + quotes and splices, but leaves out `inline`. + +5. [Reflection](./reflection.md) Quotations are a "black-box" + representation of code. They can be parameterized and composed using + splices, but their structure cannot be analyzed from the outside. TASTy + reflection gives a way to analyze code structure by partly revealing the representation type of a piece of code in a standard API. The representation + type is a form of typed abstract syntax tree, which gives rise to the `TASTy` + moniker. + +6. [TASTy Inspection](./tasty-inspect.md) Typed abstract syntax trees are serialized + in a custom compressed binary format stored in `.tasty` files. TASTy inspection allows + to load these files and analyze their content's tree structure. diff --git a/docs/_spec/TODOreference/metaprogramming/reflection.md b/docs/_spec/TODOreference/metaprogramming/reflection.md new file mode 100644 index 000000000000..b2d492657a4e --- /dev/null +++ b/docs/_spec/TODOreference/metaprogramming/reflection.md @@ -0,0 +1,131 @@ +--- +layout: doc-page +title: "Reflection" +nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/reflection.html +--- + +Reflection enables inspection and construction of Typed Abstract Syntax Trees +(Typed-AST). It may be used on quoted expressions (`quoted.Expr`) and quoted +types (`quoted.Type`) from [Macros](./macros.md) or on full TASTy files. + +If you are writing macros, please first read [Macros](./macros.md). +You may find all you need without using quote reflection. + +## API: From quotes and splices to TASTy reflect trees and back + +With `quoted.Expr` and `quoted.Type` we can compute code but also analyze code +by inspecting the ASTs. [Macros](./macros.md) provide the guarantee that the +generation of code will be type-correct. Using quote reflection will break these +guarantees and may fail at macro expansion time, hence additional explicit +checks must be done. + +To provide reflection capabilities in macros we need to add an implicit parameter +of type `scala.quoted.Quotes` and import `quotes.reflect.*` from it in the scope +where it is used. + +```scala +import scala.quoted.* + +inline def natConst(inline x: Int): Int = ${natConstImpl('{x})} + +def natConstImpl(x: Expr[Int])(using Quotes): Expr[Int] = + import quotes.reflect.* + ... +``` + +### Extractors + +`import quotes.reflect.*` will provide all extractors and methods on `quotes.reflect.Tree`s. +For example the `Literal(_)` extractor used below. + +```scala +def natConstImpl(x: Expr[Int])(using Quotes): Expr[Int] = + import quotes.reflect.* + val tree: Term = x.asTerm + tree match + case Inlined(_, _, Literal(IntConstant(n))) => + if n <= 0 then + report.error("Parameter must be natural number") + '{0} + else + tree.asExprOf[Int] + case _ => + report.error("Parameter must be a known constant") + '{0} +``` + +We can easily know which extractors are needed using `Printer.TreeStructure.show`, +which returns the string representation the structure of the tree. Other printers +can also be found in the `Printer` module. + +```scala +tree.show(using Printer.TreeStructure) +// or +Printer.TreeStructure.show(tree) +``` + +The methods `quotes.reflect.Term.{asExpr, asExprOf}` provide a way to go back to +a `quoted.Expr`. Note that `asExpr` returns a `Expr[Any]`. On the other hand +`asExprOf[T]` returns a `Expr[T]`, if the type does not conform to it an exception +will be thrown at runtime. + +### Positions + +The `Position` in the context provides an `ofMacroExpansion` value. It corresponds +to the expansion site for macros. The macro authors can obtain various information +about that expansion site. The example below shows how we can obtain position +information such as the start line, the end line or even the source code at the +expansion point. + +```scala +def macroImpl()(quotes: Quotes): Expr[Unit] = + import quotes.reflect.* + val pos = Position.ofMacroExpansion + + val path = pos.sourceFile.jpath.toString + val start = pos.start + val end = pos.end + val startLine = pos.startLine + val endLine = pos.endLine + val startColumn = pos.startColumn + val endColumn = pos.endColumn + val sourceCode = pos.sourceCode + ... +``` + +### Tree Utilities + +`quotes.reflect` contains three facilities for tree traversal and +transformation. + +`TreeAccumulator` ties the knot of a traversal. By calling `foldOver(x, tree)(owner)` +we can dive into the `tree` node and start accumulating values of type `X` (e.g., +of type `List[Symbol]` if we want to collect symbols). The code below, for +example, collects the `val` definitions in the tree. + +```scala +def collectPatternVariables(tree: Tree)(using ctx: Context): List[Symbol] = + val acc = new TreeAccumulator[List[Symbol]]: + def foldTree(syms: List[Symbol], tree: Tree)(owner: Symbol): List[Symbol] = tree match + case ValDef(_, _, rhs) => + val newSyms = tree.symbol :: syms + foldTree(newSyms, body)(tree.symbol) + case _ => + foldOverTree(syms, tree)(owner) + acc(Nil, tree) +``` + +A `TreeTraverser` extends a `TreeAccumulator` and performs the same traversal +but without returning any value. Finally, a `TreeMap` performs a transformation. + +#### ValDef.let + +`quotes.reflect.ValDef` also offers a method `let` that allows us to bind the `rhs` (right-hand side) to a `val` and use it in `body`. +Additionally, `lets` binds the given `terms` to names and allows to use them in the `body`. +Their type definitions are shown below: + +```scala +def let(rhs: Term)(body: Ident => Term): Term = ... + +def lets(terms: List[Term])(body: List[Term] => Term): Term = ... +``` diff --git a/docs/_spec/TODOreference/metaprogramming/simple-smp.md b/docs/_spec/TODOreference/metaprogramming/simple-smp.md new file mode 100644 index 000000000000..2ba0155ad329 --- /dev/null +++ b/docs/_spec/TODOreference/metaprogramming/simple-smp.md @@ -0,0 +1,232 @@ +--- +layout: doc-page +title: "The Meta-theory of Symmetric Metaprogramming" +nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/simple-smp.html +--- + +This note presents a simplified variant of +[principled metaprogramming](./macros.md) +and sketches its soundness proof. The variant treats only dialogues +between two stages. A program can have quotes which can contain +splices (which can contain quotes, which can contain splices, and so +on). Or the program could start with a splice with embedded +quotes. The essential restriction is that (1) a term can contain top-level +quotes or top-level splices, but not both, and (2) quotes cannot appear +directly inside quotes and splices cannot appear directly inside +splices. In other words, the universe is restricted to two phases +only. + +Under this restriction we can simplify the typing rules so that there are +always exactly two environments instead of having a stack of environments. +The variant presented here differs from the full calculus also in that we +replace evaluation contexts with contextual typing rules. While this +is more verbose, it makes it easier to set up the meta theory. + +## Syntax +``` +Terms t ::= x variable + (x: T) => t lambda + t t application + ’t quote + ~t splice + +Simple terms u ::= x | (x: T) => u | u u + +Values v ::= (x: T) => t lambda + ’u quoted value + +Types T ::= A base type + T -> T function type + ’T quoted type +``` +## Operational semantics + +### Evaluation +``` + ((x: T) => t) v --> [x := v]t + + t1 --> t2 + --------------- + t1 t --> t2 t + + t1 --> t2 + --------------- + v t1 --> v t2 + + t1 ==> t2 + ------------- + ’t1 --> ’t2 +``` + +### Splicing +``` + ~’u ==> u + + t1 ==> t2 + ------------------------------- + (x: T) => t1 ==> (x: T) => t2 + + t1 ==> t2 + --------------- + t1 t ==> t2 t + + t1 ==> t2 + --------------- + u t1 ==> u t2 + + t1 --> t2 + ------------- + ~t1 ==> ~t2 + +``` +## Typing Rules + +Typing judgments are of the form `E1 * E2 |- t: T` where `E1, E2` are environments and +`*` is one of `~` and `’`. +``` + x: T in E2 + --------------- + E1 * E2 |- x: T + + + E1 * E2, x: T1 |- t: T2 + -------------------------------- + E1 * E2 |- (x: T1) => t: T -> T2 + + + E1 * E2 |- t1: T2 -> T E1 * E2 |- t2: T2 + ------------------------------------------- + E1 * E2 |- t1 t2: T + + + E2 ’ E1 |- t: T + ----------------- + E1 ~ E2 |- ’t: ’T + + + E2 ~ E1 |- t: ’T + ---------------- + E1 ’ E2 |- ~t: T +``` + +(Curiously, this looks a bit like a Christmas tree). + +## Soundness + +The meta-theory typically requires mutual inductions over two judgments. + +### Progress Theorem + + 1. If `E1 ~ |- t: T` then either `t = v` for some value `v` or `t --> t2` for some term `t2`. + 2. If ` ’ E2 |- t: T` then either `t = u` for some simple term `u` or `t ==> t2` for some term `t2`. + +Proof by structural induction over terms. + +To prove (1): + + - the cases for variables, lambdas and applications are as in [STLC](https://en.wikipedia.org/wiki/Simply_typed_lambda_calculus). + - If `t = ’t2`, then by inversion we have ` ’ E1 |- t2: T2` for some type `T2`. + By the second [induction hypothesis](https://en.wikipedia.org/wiki/Mathematical_induction) (I.H.), we have one of: + - `t2 = u`, hence `’t2` is a value, + - `t2 ==> t3`, hence `’t2 --> ’t3`. + - The case `t = ~t2` is not typable. + +To prove (2): + + - If `t = x` then `t` is a simple term. + - If `t = (x: T) => t2`, then either `t2` is a simple term, in which case `t` is as well. + Or by the second I.H. `t2 ==> t3`, in which case `t ==> (x: T) => t3`. + - If `t = t1 t2` then one of three cases applies: + + - `t1` and `t2` are a simple term, then `t` is as well a simple term. + - `t1` is not a simple term. Then by the second I.H., `t1 ==> t12`, hence `t ==> t12 t2`. + - `t1` is a simple term but `t2` is not. Then by the second I.H. `t2 ==> t22`, hence `t ==> t1 t22`. + + - The case `t = ’t2` is not typable. + - If `t = ~t2` then by inversion we have `E2 ~ |- t2: ’T2`, for some type `T2`. + By the first I.H., we have one of + + - `t2 = v`. Since `t2: ’T2`, we must have `v = ’u`, for some simple term `u`, hence `t = ~’u`. + By quote-splice reduction, `t ==> u`. + - `t2 --> t3`. Then by the context rule for `’t`, `t ==> ’t3`. + + +### Substitution Lemma + + 1. If `E1 ~ E2 |- s: S` and `E1 ~ E2, x: S |- t: T` then `E1 ~ E2 |- [x := s]t: T`. + 2. If `E1 ~ E2 |- s: S` and `E2, x: S ’ E1 |- t: T` then `E2 ’ E1 |- [x := s]t: T`. + +The proofs are by induction on typing derivations for `t`, analogous +to the proof for STL (with (2) a bit simpler than (1) since we do not +need to swap lambda bindings with the bound variable `x`). The +arguments that link the two hypotheses are as follows. + +To prove (1), let `t = ’t1`. Then `T = ’T1` for some type `T1` and the last typing rule is +``` + E2, x: S ’ E1 |- t1: T1 + ------------------------- + E1 ~ E2, x: S |- ’t1: ’T1 +``` +By the second I.H. `E2 ’ E1 |- [x := s]t1: T1`. By typing, `E1 ~ E2 |- ’[x := s]t1: ’T1`. +Since `[x := s]t = [x := s](’t1) = ’[x := s]t1` we get `[x := s]t: ’T1`. + +To prove (2), let `t = ~t1`. Then the last typing rule is +``` + E1 ~ E2, x: S |- t1: ’T + ----------------------- + E2, x: S ’ E1 |- ~t1: T +``` +By the first I.H., `E1 ~ E2 |- [x := s]t1: ’T`. By typing, `E2 ’ E1 |- ~[x := s]t1: T`. +Since `[x := s]t = [x := s](~t1) = ~[x := s]t1` we get `[x := s]t: T`. + + +### Preservation Theorem + + 1. If `E1 ~ E2 |- t1: T` and `t1 --> t2` then `E1 ~ E2 |- t2: T`. + 2. If `E1 ’ E2 |- t1: T` and `t1 ==> t2` then `E1 ’ E2 |- t2: T`. + +The proof is by structural induction on evaluation derivations. The proof of (1) is analogous +to the proof for STL, using the substitution lemma for the beta reduction case, with the addition of reduction of quoted terms, which goes as follows: + + - Assume the last rule was + ``` + t1 ==> t2 + ------------- + ’t1 --> ’t2 + ``` + By inversion of typing rules, we must have `T = ’T1` for some type `T1` such that `t1: T1`. + By the second I.H., `t2: T1`, hence `’t2: `T1`. + + +To prove (2): + + - Assume the last rule was `~’u ==> u`. The typing proof of `~’u` must have the form + + ``` + E1 ’ E2 |- u: T + ----------------- + E1 ~ E2 |- ’u: ’T + ----------------- + E1 ’ E2 |- ~’u: T + ``` + Hence, `E1 ’ E2 |- u: T`. + + - Assume the last rule was + ``` + t1 ==> t2 + ------------------------------- + (x: S) => t1 ==> (x: T) => t2 + ``` + By typing inversion, `E1 ' E2, x: S |- t1: T1` for some type `T1` such that `T = S -> T1`. + By the I.H, `t2: T1`. By the typing rule for lambdas the result follows. + + - The context rules for applications are equally straightforward. + + - Assume the last rule was + ``` + t1 ==> t2 + ------------- + ~t1 ==> ~t2 + ``` + By inversion of typing rules, we must have `t1: ’T`. + By the first I.H., `t2: ’T`, hence `~t2: T`. diff --git a/docs/_spec/TODOreference/metaprogramming/staging.md b/docs/_spec/TODOreference/metaprogramming/staging.md new file mode 100644 index 000000000000..e74d491402b5 --- /dev/null +++ b/docs/_spec/TODOreference/metaprogramming/staging.md @@ -0,0 +1,121 @@ +--- +layout: doc-page +title: "Runtime Multi-Stage Programming" +nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/staging.html +--- + +The framework expresses at the same time compile-time metaprogramming and +multi-stage programming. We can think of compile-time metaprogramming as a +two stage compilation process: one that we write the code in top-level splices, +that will be used for code generation (macros) and one that will perform all +necessary evaluations at compile-time and an object program that we will run +as usual. What if we could synthesize code at run-time and offer one extra stage +to the programmer? Then we can have a value of type `Expr[T]` at run-time that we +can essentially treat as a typed-syntax tree that we can either _show_ as a +string (pretty-print) or compile and run. If the number of quotes exceeds the +number of splices by more than one (effectively handling at run-time values of type +`Expr[Expr[T]]`, `Expr[Expr[Expr[T]]]`, ...) then we talk about Multi-Stage +Programming. + +The motivation behind this _paradigm_ is to let runtime information affect or +guide code-generation. + +Intuition: The phase in which code is run is determined by the difference +between the number of splice scopes and quote scopes in which it is embedded. + + - If there are more splices than quotes, the code is run at compile-time i.e. + as a macro. In the general case, this means running an interpreter that + evaluates the code, which is represented as a typed abstract syntax tree. The + interpreter can fall back to reflective calls when evaluating an application + of a previously compiled method. If the splice excess is more than one, it + would mean that a macro’s implementation code (as opposed to the code it + expands to) invokes other macros. If macros are realized by interpretation, + this would lead to towers of interpreters, where the first interpreter would + itself interpret an interpreter code that possibly interprets another + interpreter and so on. + + - If the number of splices equals the number of quotes, the code is compiled + and run as usual. + + - If the number of quotes exceeds the number of splices, the code is staged. + That is, it produces a typed abstract syntax tree or type structure at + run-time. A quote excess of more than one corresponds to multi-staged + programming. + +Providing an interpreter for the full language is quite difficult, and it is +even more difficult to make that interpreter run efficiently. So we currently +impose the following restrictions on the use of splices. + + 1. A top-level splice must appear in an inline method (turning that method + into a macro) + + 2. The splice must call a previously compiled + method passing quoted arguments, constant arguments or inline arguments. + + 3. Splices inside splices (but no intervening quotes) are not allowed. + + +## API + +The framework as discussed so far allows code to be staged, i.e. be prepared +to be executed at a later stage. To run that code, there is another method +in class `Expr` called `run`. Note that `$` and `run` both map from `Expr[T]` +to `T` but only `$` is subject to the [PCP](./macros.md#the-phase-consistency-principle), whereas `run` is just a normal method. +`scala.quoted.staging.run` provides a `Quotes` that can be used to show the expression in its scope. +On the other hand `scala.quoted.staging.withQuotes` provides a `Quotes` without evaluating the expression. + +```scala +package scala.quoted.staging + +def run[T](expr: Quotes ?=> Expr[T])(using Compiler): T = ... + +def withQuotes[T](thunk: Quotes ?=> T)(using Compiler): T = ... +``` + +## Create a new Scala 3 project with staging enabled + +```shell +sbt new scala/scala3-staging.g8 +``` + +From [`scala/scala3-staging.g8`](https://github.com/scala/scala3-staging.g8). + +It will create a project with the necessary dependencies and some examples. + +In case you prefer to create the project on your own, make sure to define the following dependency in your [`build.sbt` build definition](https://www.scala-sbt.org/1.x/docs/Basic-Def.html) + +```scala +libraryDependencies += "org.scala-lang" %% "scala3-staging" % scalaVersion.value +``` + +and in case you use `scalac`/`scala` directly, then use the `-with-compiler` flag for both: + +```shell +scalac -with-compiler -d out Test.scala +scala -with-compiler -classpath out Test +``` + +## Example + +Now take exactly the same example as in [Macros](./macros.md). Assume that we +do not want to pass an array statically but generate code at run-time and pass +the value, also at run-time. Note, how we make a future-stage function of type +`Expr[Array[Int] => Int]` in line 6 below. Using `staging.run { ... }` we can evaluate an +expression at runtime. Within the scope of `staging.run` we can also invoke `show` on an expression +to get a source-like representation of the expression. + +```scala +import scala.quoted.* + +// make available the necessary compiler for runtime code generation +given staging.Compiler = staging.Compiler.make(getClass.getClassLoader) + +val f: Array[Int] => Int = staging.run { + val stagedSum: Expr[Array[Int] => Int] = + '{ (arr: Array[Int]) => ${sum('arr)}} + println(stagedSum.show) // Prints "(arr: Array[Int]) => { var sum = 0; ... }" + stagedSum +} + +f.apply(Array(1, 2, 3)) // Returns 6 +``` diff --git a/docs/_spec/TODOreference/metaprogramming/tasty-inspect.md b/docs/_spec/TODOreference/metaprogramming/tasty-inspect.md new file mode 100644 index 000000000000..e643775243e0 --- /dev/null +++ b/docs/_spec/TODOreference/metaprogramming/tasty-inspect.md @@ -0,0 +1,57 @@ +--- +layout: doc-page +title: "TASTy Inspection" +nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/tasty-inspect.html +--- + +```scala +libraryDependencies += "org.scala-lang" %% "scala3-tasty-inspector" % scalaVersion.value +``` + +TASTy files contain the full typed tree of a class including source positions +and documentation. This is ideal for tools that analyze or extract semantic +information from the code. To avoid the hassle of working directly with the TASTy +file we provide the `Inspector` which loads the contents and exposes it +through the TASTy reflect API. + +## Inspecting TASTy files + +To inspect the trees of a TASTy file a consumer can be defined in the following way. + +```scala +import scala.quoted.* +import scala.tasty.inspector.* + +class MyInspector extends Inspector: + def inspect(using Quotes)(tastys: List[Tasty[quotes.type]]): Unit = + import quotes.reflect.* + for tasty <- tastys do + val tree = tasty.ast + // Do something with the tree +``` + +Then the consumer can be instantiated with the following code to get the tree of the `foo/Bar.tasty` file. + +```scala +object Test: + def main(args: Array[String]): Unit = + val tastyFiles = List("foo/Bar.tasty") + TastyInspector.inspectTastyFiles(tastyFiles)(new MyInspector) +``` + +Note that if we need to run the main (in the example below defined in an object called `Test`) after compilation we need to make the compiler available to the runtime: + +```shell +scalac -d out Test.scala +scala -with-compiler -classpath out Test +``` + +## Template project + +Using sbt version `1.1.5+`, do: + +```shell +sbt new scala/scala3-tasty-inspector.g8 +``` + +in the folder where you want to clone the template. diff --git a/docs/_spec/TODOreference/new-types/dependent-function-types-spec.md b/docs/_spec/TODOreference/new-types/dependent-function-types-spec.md new file mode 100644 index 000000000000..f3237ddf7b9a --- /dev/null +++ b/docs/_spec/TODOreference/new-types/dependent-function-types-spec.md @@ -0,0 +1,125 @@ +--- +layout: doc-page +title: "Dependent Function Types - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/dependent-function-types-spec.html +--- + +Initial implementation in [PR #3464](https://github.com/lampepfl/dotty/pull/3464). + +## Syntax + +``` +FunArgTypes ::= InfixType + | ‘(’ [ FunArgType {',' FunArgType } ] ‘)’ + | ‘(’ TypedFunParam {',' TypedFunParam } ‘)’ +TypedFunParam ::= id ‘:’ Type +``` + +Dependent function types associate to the right, e.g. +`(s: S) => (t: T) => U` is the same as `(s: S) => ((t: T) => U)`. + +## Implementation + +Dependent function types are shorthands for class types that define `apply` +methods with a dependent result type. Dependent function types desugar to +refinement types of `scala.FunctionN`. A dependent function type +`(x1: K1, ..., xN: KN) => R` of arity `N` translates to: + +```scala +FunctionN[K1, ..., Kn, R']: + def apply(x1: K1, ..., xN: KN): R +``` + +where the result type parameter `R'` is the least upper approximation of the +precise result type `R` without any reference to value parameters `x1, ..., xN`. + +The syntax and semantics of anonymous dependent functions is identical to the +one of regular functions. Eta expansion is naturally generalized to produce +dependent function types for methods with dependent result types. + +Dependent functions can be implicit, and generalize to arity `N > 22` in the +same way that other functions do, see +[the corresponding documentation](../dropped-features/limit22.md). + +## Examples + +The example below defines a trait `C` and the two dependent function types +`DF` and `IDF` and prints the results of the respective function applications: + +[depfuntype.scala]: https://github.com/lampepfl/dotty/blob/main/tests/pos/depfuntype.scala + +```scala +trait C { type M; val m: M } + +type DF = (x: C) => x.M + +type IDF = (x: C) ?=> x.M + +@main def test = + val c = new C { type M = Int; val m = 3 } + + val depfun: DF = (x: C) => x.m + val t = depfun(c) + println(s"t=$t") // prints "t=3" + + val idepfun: IDF = summon[C].m + val u = idepfun(using c) + println(s"u=$u") // prints "u=3" + +``` + +In the following example the depend type `f.Eff` refers to the effect type `CanThrow`: + +[eff-dependent.scala]: https://github.com/lampepfl/dotty/blob/main/tests/run/eff-dependent.scala + +```scala +trait Effect + +// Type X => Y +abstract class Fun[-X, +Y]: + type Eff <: Effect + def apply(x: X): Eff ?=> Y + +class CanThrow extends Effect +class CanIO extends Effect + +given ct: CanThrow = new CanThrow +given ci: CanIO = new CanIO + +class I2S extends Fun[Int, String]: + type Eff = CanThrow + def apply(x: Int) = x.toString + +class S2I extends Fun[String, Int]: + type Eff = CanIO + def apply(x: String) = x.length + +// def map(f: A => B)(xs: List[A]): List[B] +def map[A, B](f: Fun[A, B])(xs: List[A]): f.Eff ?=> List[B] = + xs.map(f.apply) + +// def mapFn[A, B]: (A => B) -> List[A] -> List[B] +def mapFn[A, B]: (f: Fun[A, B]) => List[A] => f.Eff ?=> List[B] = + f => xs => map(f)(xs) + +// def compose(f: A => B)(g: B => C)(x: A): C +def compose[A, B, C](f: Fun[A, B])(g: Fun[B, C])(x: A): + f.Eff ?=> g.Eff ?=> C = + g(f(x)) + +// def composeFn: (A => B) -> (B => C) -> A -> C +def composeFn[A, B, C]: + (f: Fun[A, B]) => (g: Fun[B, C]) => A => f.Eff ?=> g.Eff ?=> C = + f => g => x => compose(f)(g)(x) + +@main def test = + val i2s = new I2S + val s2i = new S2I + + assert(mapFn(i2s)(List(1, 2, 3)).mkString == "123") + assert(composeFn(i2s)(s2i)(22) == 2) +``` + +## Type Checking + +After desugaring no additional typing rules are required for dependent function types. diff --git a/docs/_spec/TODOreference/new-types/dependent-function-types.md b/docs/_spec/TODOreference/new-types/dependent-function-types.md new file mode 100644 index 000000000000..adbee1d8b3c8 --- /dev/null +++ b/docs/_spec/TODOreference/new-types/dependent-function-types.md @@ -0,0 +1,49 @@ +--- +layout: doc-page +title: "Dependent Function Types" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/dependent-function-types.html +--- + +A dependent function type is a function type whose result depends +on the function's parameters. For example: + +```scala +trait Entry { type Key; val key: Key } + +def extractKey(e: Entry): e.Key = e.key // a dependent method + +val extractor: (e: Entry) => e.Key = extractKey // a dependent function value +// ^^^^^^^^^^^^^^^^^^^ +// a dependent function type +``` + +Scala already has _dependent methods_, i.e. methods where the result +type refers to some of the parameters of the method. Method +`extractKey` is an example. Its result type, `e.Key` refers to its +parameter `e` (we also say, `e.Key` _depends_ on `e`). But so far it +was not possible to turn such methods into function values, so that +they can be passed as parameters to other functions, or returned as +results. Dependent methods could not be turned into functions simply +because there was no type that could describe them. + +In Scala 3 this is now possible. The type of the `extractor` value above is + +```scala +(e: Entry) => e.Key +``` + +This type describes function values that take any argument `e` of type +`Entry` and return a result of type `e.Key`. + +Recall that a normal function type `A => B` is represented as an +instance of the [`Function1` trait](https://scala-lang.org/api/3.x/scala/Function1.html) +(i.e. `Function1[A, B]`) and analogously for functions with more parameters. Dependent functions +are also represented as instances of these traits, but they get an additional +refinement. In fact, the dependent function type above is just syntactic sugar for + +```scala +Function1[Entry, Entry#Key]: + def apply(e: Entry): e.Key +``` + +[More details](./dependent-function-types-spec.md) diff --git a/docs/_spec/TODOreference/new-types/intersection-types-spec.md b/docs/_spec/TODOreference/new-types/intersection-types-spec.md new file mode 100644 index 000000000000..346c57c004f0 --- /dev/null +++ b/docs/_spec/TODOreference/new-types/intersection-types-spec.md @@ -0,0 +1,108 @@ +--- +layout: doc-page +title: "Intersection Types - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/intersection-types-spec.html +--- + +## Syntax + +Syntactically, the type `S & T` is an infix type, where the infix operator is `&`. +The operator `&` is a normal identifier +with the usual precedence and subject to usual resolving rules. +Unless shadowed by another definition, it resolves to the type `scala.&`, +which acts as a type alias to an internal representation of intersection types. + +``` +Type ::= ...| InfixType +InfixType ::= RefinedType {id [nl] RefinedType} +``` + +## Subtyping Rules + +``` +T <: A T <: B +---------------- + T <: A & B + + A <: T +---------------- + A & B <: T + + B <: T +---------------- + A & B <: T +``` + +From the rules above, we can show that `&` is _commutative_: `A & B <: B & A` for any type `A` and `B`. + +``` + B <: B A <: A +---------- ----------- +A & B <: B A & B <: A +--------------------------- + A & B <: B & A +``` + +In another word, `A & B` is the same type as `B & A`, in the sense that the two types +have the same values and are subtypes of each other. + +If `C` is a type constructor, then `C[A] & C[B]` can be simplified using the following three rules: + +- If `C` is covariant, `C[A] & C[B] ~> C[A & B]` +- If `C` is contravariant, `C[A] & C[B] ~> C[A | B]` +- If `C` is non-variant, emit a compile error + +When `C` is covariant, `C[A & B] <: C[A] & C[B]` can be derived: + +``` + A <: A B <: B + ---------- --------- + A & B <: A A & B <: B +--------------- ----------------- +C[A & B] <: C[A] C[A & B] <: C[B] +------------------------------------------ + C[A & B] <: C[A] & C[B] +``` + +When `C` is contravariant, `C[A | B] <: C[A] & C[B]` can be derived: + +``` + A <: A B <: B + ---------- --------- + A <: A | B B <: A | B +------------------- ---------------- +C[A | B] <: C[A] C[A | B] <: C[B] +-------------------------------------------------- + C[A | B] <: C[A] & C[B] +``` + +## Erasure + +The erased type for `S & T` is the erased _glb_ (greatest lower bound) of the +erased type of `S` and `T`. The rules for erasure of intersection types are given +below in pseudocode: + +``` +|S & T| = glb(|S|, |T|) + +glb(JArray(A), JArray(B)) = JArray(glb(A, B)) +glb(JArray(T), _) = JArray(T) +glb(_, JArray(T)) = JArray(T) +glb(A, B) = A if A extends B +glb(A, B) = B if B extends A +glb(A, _) = A if A is not a trait +glb(_, B) = B if B is not a trait +glb(A, _) = A // use first +``` + +In the above, `|T|` means the erased type of `T`, `JArray` refers to +the type of Java Array. + +See also: [`TypeErasure#erasedGlb`](https://github.com/lampepfl/dotty/blob/main/compiler/src/dotty/tools/dotc/core/TypeErasure.scala#L289). + +## Relationship with Compound Type (`with`) + +Intersection types `A & B` replace compound types `A with B` in Scala 2. For the +moment, the syntax `A with B` is still allowed and interpreted as `A & B`, but +its usage as a type (as opposed to in a `new` or `extends` clause) will be +deprecated and removed in the future. diff --git a/docs/_spec/TODOreference/new-types/intersection-types.md b/docs/_spec/TODOreference/new-types/intersection-types.md new file mode 100644 index 000000000000..a4eedeb000f6 --- /dev/null +++ b/docs/_spec/TODOreference/new-types/intersection-types.md @@ -0,0 +1,68 @@ +--- +layout: doc-page +title: "Intersection Types" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/intersection-types.html +--- + +Used on types, the `&` operator creates an intersection type. + +## Type Checking + +The type `S & T` represents values that are of the type `S` and `T` at the same time. + +```scala +trait Resettable: + def reset(): Unit + +trait Growable[T]: + def add(t: T): Unit + +def f(x: Resettable & Growable[String]) = + x.reset() + x.add("first") +``` + +The parameter `x` is required to be _both_ a `Resettable` and a +`Growable[String]`. + +The members of an intersection type `A & B` are all the members of `A` and all +the members of `B`. For instance `Resettable & Growable[String]` +has member methods `reset` and `add`. + +`&` is _commutative_: `A & B` is the same type as `B & A`. + +If a member appears in both `A` and `B`, its type in `A & B` is the intersection +of its type in `A` and its type in `B`. For instance, assume the definitions: + +```scala +trait A: + def children: List[A] + +trait B: + def children: List[B] + +val x: A & B = new C +val ys: List[A & B] = x.children +``` + +The type of `children` in `A & B` is the intersection of `children`'s +type in `A` and its type in `B`, which is `List[A] & List[B]`. This +can be further simplified to `List[A & B]` because `List` is +covariant. + +One might wonder how the compiler could come up with a definition for +`children` of type `List[A & B]` since what is given are `children` +definitions of type `List[A]` and `List[B]`. The answer is the compiler does not +need to. `A & B` is just a type that represents a set of requirements for +values of the type. At the point where a value is _constructed_, one +must make sure that all inherited members are correctly defined. +So if one defines a class `C` that inherits `A` and `B`, one needs +to give at that point a definition of a `children` method with the required type. + +```scala +class C extends A, B: + def children: List[A & B] = ??? +``` + + +[More details](./intersection-types-spec.md) diff --git a/docs/_spec/TODOreference/new-types/match-types.md b/docs/_spec/TODOreference/new-types/match-types.md new file mode 100644 index 000000000000..d646dd11880b --- /dev/null +++ b/docs/_spec/TODOreference/new-types/match-types.md @@ -0,0 +1,247 @@ +--- +layout: doc-page +title: "Match Types" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/match-types.html +--- + +A match type reduces to one of its right-hand sides, depending on the type of +its scrutinee. For example: + +```scala +type Elem[X] = X match + case String => Char + case Array[t] => t + case Iterable[t] => t +``` + +This defines a type that reduces as follows: + +```scala +Elem[String] =:= Char +Elem[Array[Int]] =:= Int +Elem[List[Float]] =:= Float +Elem[Nil.type] =:= Nothing +``` + +Here `=:=` is understood to mean that left and right-hand sides are mutually +subtypes of each other. + +In general, a match type is of the form + +```scala +S match { P1 => T1 ... Pn => Tn } +``` + +where `S`, `T1`, ..., `Tn` are types and `P1`, ..., `Pn` are type patterns. Type +variables in patterns start with a lower case letter, as usual. + +Match types can form part of recursive type definitions. Example: + +```scala +type LeafElem[X] = X match + case String => Char + case Array[t] => LeafElem[t] + case Iterable[t] => LeafElem[t] + case AnyVal => X +``` + +Recursive match type definitions can also be given an upper bound, like this: + +```scala +type Concat[Xs <: Tuple, +Ys <: Tuple] <: Tuple = Xs match + case EmptyTuple => Ys + case x *: xs => x *: Concat[xs, Ys] +``` + +In this definition, every instance of `Concat[A, B]`, whether reducible or not, +is known to be a subtype of `Tuple`. This is necessary to make the recursive +invocation `x *: Concat[xs, Ys]` type check, since `*:` demands a `Tuple` as its +right operand. + +## Dependent Typing + +Match types can be used to define dependently typed methods. For instance, here +is the value level counterpart to the `LeafElem` type defined above (note the +use of the match type as the return type): + +```scala +def leafElem[X](x: X): LeafElem[X] = x match + case x: String => x.charAt(0) + case x: Array[t] => leafElem(x(0)) + case x: Iterable[t] => leafElem(x.head) + case x: AnyVal => x +``` + +This special mode of typing for match expressions is only used when the +following conditions are met: + +1. The match expression patterns do not have guards +2. The match expression scrutinee's type is a subtype of the match type + scrutinee's type +3. The match expression and the match type have the same number of cases +4. The match expression patterns are all [Typed Patterns](https://scala-lang.org/files/archive/spec/2.13/08-pattern-matching.html#typed-patterns), + and these types are `=:=` to their corresponding type patterns in the match + type + +So you know, while the case body will be expected to have the type on the right-hand +side of the corresponding match type case, that doesn't imply the match type argument +is constrained. Using the example, the last case body must conform to X, but that +doesn't constrain X to be AnyVal, and therefore a LeafElem[X] inside the body wouldn't +reduce; it would remain stuck, and as such just an abstract type. + +## Representation of Match Types + +The internal representation of a match type +``` +S match { P1 => T1 ... Pn => Tn } +``` +is `Match(S, C1, ..., Cn) <: B` where each case `Ci` is of the form +``` +[Xs] =>> P => T +``` + +Here, `[Xs]` is a type parameter clause of the variables bound in pattern `Pi`. +If there are no bound type variables in a case, the type parameter clause is +omitted and only the function type `P => T` is kept. So each case is either a +unary function type or a type lambda over a unary function type. + +`B` is the declared upper bound of the match type, or `Any` if no such bound is +given. We will leave it out in places where it does not matter for the +discussion. The scrutinee, bound, and pattern types must all be first-order +types. + +## Match Type Reduction + +Match type reduction follows the semantics of match expressions, that is, a +match type of the form `S match { P1 => T1 ... Pn => Tn }` reduces to `Ti` if +and only if `s: S match { _: P1 => T1 ... _: Pn => Tn }` evaluates to a value of +type `Ti` for all `s: S`. + +The compiler implements the following reduction algorithm: + +- If the scrutinee type `S` is an empty set of values (such as `Nothing` or + `String & Int`), do not reduce. +- Sequentially consider each pattern `Pi` + - If `S <: Pi` reduce to `Ti`. + - Otherwise, try constructing a proof that `S` and `Pi` are disjoint, or, in + other words, that no value `s` of type `S` is also of type `Pi`. + - If such proof is found, proceed to the next case (`Pi+1`), otherwise, do + not reduce. + +Disjointness proofs rely on the following properties of Scala types: + +1. Single inheritance of classes +2. Final classes cannot be extended +3. Constant types with distinct values are nonintersecting +4. Singleton paths to distinct values are nonintersecting, such as `object` definitions or singleton enum cases. + +Type parameters in patterns are minimally instantiated when computing `S <: Pi`. +An instantiation `Is` is _minimal_ for `Xs` if all type variables in `Xs` that +appear covariantly and nonvariantly in `Is` are as small as possible and all +type variables in `Xs` that appear contravariantly in `Is` are as large as +possible. Here, "small" and "large" are understood with respect to `<:`. + +For simplicity, we have omitted constraint handling so far. The full formulation +of subtyping tests describes them as a function from a constraint and a pair of +types to either _success_ and a new constraint or _failure_. In the context of +reduction, the subtyping test `S <: [Xs := Is] P` is understood to leave the +bounds of all variables in the input constraint unchanged, i.e. existing +variables in the constraint cannot be instantiated by matching the scrutinee +against the patterns. + +## Subtyping Rules for Match Types + +The following rules apply to match types. For simplicity, we omit environments +and constraints. + +1. The first rule is a structural comparison between two match types: + + ``` + S match { P1 => T1 ... Pm => Tm } <: T match { Q1 => U1 ... Qn => Un } + ``` + + if + + ``` + S =:= T, m >= n, Pi =:= Qi and Ti <: Ui for i in 1..n + ``` + + I.e. scrutinees and patterns must be equal and the corresponding bodies must + be subtypes. No case re-ordering is allowed, but the subtype can have more + cases than the supertype. + +2. The second rule states that a match type and its redux are mutual subtypes. + + ``` + S match { P1 => T1 ... Pn => Tn } <: U + U <: S match { P1 => T1 ... Pn => Tn } + ``` + + if + + `S match { P1 => T1 ... Pn => Tn }` reduces to `U` + +3. The third rule states that a match type conforms to its upper bound: + + ``` + (S match { P1 => T1 ... Pn => Tn } <: B) <: B + ``` + +## Termination + +Match type definitions can be recursive, which means that it's possible to run +into an infinite loop while reducing match types. + +Since reduction is linked to subtyping, we already have a cycle detection +mechanism in place. As a result, the following will already give a reasonable +error message: + +```scala +type L[X] = X match + case Int => L[X] + +def g[X]: L[X] = ??? +``` + +```scala + | val x: Int = g[Int] + | ^ + |Recursion limit exceeded. + |Maybe there is an illegal cyclic reference? + |If that's not the case, you could also try to + |increase the stacksize using the -Xss JVM option. + |A recurring operation is (inner to outer): + | + | subtype LazyRef(Test.L[Int]) <:< Int +``` + +Internally, the Scala compiler detects these cycles by turning selected stack overflows into +type errors. If there is a stack overflow during subtyping, the exception will +be caught and turned into a compile-time error that indicates a trace of the +subtype tests that caused the overflow without showing a full stack trace. + + +## Match Types Variance + +All type positions in a match type (scrutinee, patterns, bodies) are considered invariant. + +## Related Work + +Match types have similarities with +[closed type families](https://wiki.haskell.org/GHC/Type_families) in Haskell. +Some differences are: + +- Subtyping instead of type equalities. +- Match type reduction does not tighten the underlying constraint, whereas type + family reduction does unify. This difference in approach mirrors the + difference between local type inference in Scala and global type inference in + Haskell. + +Match types are also similar to Typescript's +[conditional types](https://github.com/Microsoft/TypeScript/pull/21316). The +main differences here are: + + - Conditional types only reduce if both the scrutinee and pattern are ground, + whereas match types also work for type parameters and abstract types. + - Match types support direct recursion. + - Conditional types distribute through union types. diff --git a/docs/_spec/TODOreference/new-types/new-types.md b/docs/_spec/TODOreference/new-types/new-types.md new file mode 100644 index 000000000000..84c157495d6f --- /dev/null +++ b/docs/_spec/TODOreference/new-types/new-types.md @@ -0,0 +1,7 @@ +--- +layout: index +title: "New Types" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/index.html +--- + +This chapter documents the new types introduced in Scala 3. diff --git a/docs/_spec/TODOreference/new-types/polymorphic-function-types.md b/docs/_spec/TODOreference/new-types/polymorphic-function-types.md new file mode 100644 index 000000000000..1754bf844831 --- /dev/null +++ b/docs/_spec/TODOreference/new-types/polymorphic-function-types.md @@ -0,0 +1,94 @@ +--- +layout: doc-page +title: "Polymorphic Function Types" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/polymorphic-function-types.html +--- + +A polymorphic function type is a function type which accepts type parameters. +For example: + +```scala +// A polymorphic method: +def foo[A](xs: List[A]): List[A] = xs.reverse + +// A polymorphic function value: +val bar: [A] => List[A] => List[A] +// ^^^^^^^^^^^^^^^^^^^^^^^^^ +// a polymorphic function type + = [A] => (xs: List[A]) => foo[A](xs) +``` + +Scala already has _polymorphic methods_, i.e. methods which accepts type parameters. +Method `foo` above is an example, accepting a type parameter `A`. +So far, it +was not possible to turn such methods into polymorphic function values like `bar` above, +which can be passed as parameters to other functions, or returned as results. + +In Scala 3 this is now possible. The type of the `bar` value above is + +```scala +[A] => List[A] => List[A] +``` + +This type describes function values which take a type `A` as a parameter, +then take a list of type `List[A]`, and return a list of the same type `List[A]`. + +[More details](https://github.com/lampepfl/dotty/pull/4672) + + +## Example Usage + +Polymorphic function type are particularly useful +when callers of a method are required to provide a +function which has to be polymorphic, +meaning that it should accept arbitrary types as part of its inputs. + +For instance, consider the situation where we have +a data type to represent the expressions of a simple language +(consisting only of variables and function applications) +in a strongly-typed way: + +```scala +enum Expr[A]: + case Var(name: String) + case Apply[A, B](fun: Expr[B => A], arg: Expr[B]) extends Expr[A] +``` + +We would like to provide a way for users to map a function +over all immediate subexpressions of a given `Expr`. +This requires the given function to be polymorphic, +since each subexpression may have a different type. +Here is how to implement this using polymorphic function types: + +```scala +def mapSubexpressions[A](e: Expr[A])(f: [B] => Expr[B] => Expr[B]): Expr[A] = + e match + case Apply(fun, arg) => Apply(f(fun), f(arg)) + case Var(n) => Var(n) +``` + +And here is how to use this function to _wrap_ each subexpression +in a given expression with a call to some `wrap` function, +defined as a variable: + +```scala +val e0 = Apply(Var("f"), Var("a")) +val e1 = mapSubexpressions(e0)( + [B] => (se: Expr[B]) => Apply(Var[B => B]("wrap"), se)) +println(e1) // Apply(Apply(Var(wrap),Var(f)),Apply(Var(wrap),Var(a))) +``` + +## Relationship With Type Lambdas + +Polymorphic function types are not to be confused with +[_type lambdas_](type-lambdas.md). +While the former describes the _type_ of a polymorphic _value_, +the latter is an actual function value _at the type level_. + +A good way of understanding the difference is to notice that +**_type lambdas are applied in types, +whereas polymorphic functions are applied in terms_**: +One would call the function `bar` above +by passing it a type argument `bar[Int]` _within a method body_. +On the other hand, given a type lambda such as `type F = [A] =>> List[A]`, +one would call `F` _within a type expression_, as in `type Bar = F[Int]`. diff --git a/docs/_spec/TODOreference/new-types/type-lambdas-spec.md b/docs/_spec/TODOreference/new-types/type-lambdas-spec.md new file mode 100644 index 000000000000..52f88dab4217 --- /dev/null +++ b/docs/_spec/TODOreference/new-types/type-lambdas-spec.md @@ -0,0 +1,116 @@ +--- +layout: doc-page +title: "Type Lambdas - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/type-lambdas-spec.html +--- + +## Syntax + +``` +Type ::= ... | TypeParamClause ‘=>>’ Type +TypeParamClause ::= ‘[’ TypeParam {‘,’ TypeParam} ‘]’ +TypeParam ::= {Annotation} (id [HkTypeParamClause] | ‘_’) TypeBounds +TypeBounds ::= [‘>:’ Type] [‘<:’ Type] +``` + +## Type Checking + +A type lambda such as `[X] =>> F[X]` defines a function from types to types. The parameter(s) may carry bounds. +If a parameter is bounded, as in `[X >: L <: U] =>> F[X]` it is checked that arguments to the parameters conform to the bounds `L` and `U`. +Only the upper bound `U` can be F-bounded, i.e. `X` can appear in it. + +## Subtyping Rules + +Assume two type lambdas +```scala +type TL1 = [X >: L1 <: U1] =>> R1 +type TL2 = [X >: L2 <: U2] =>> R2 +``` +Then `TL1 <: TL2`, if + + - the type interval `L2..U2` is contained in the type interval `L1..U1` (i.e. +`L1 <: L2` and `U2 <: U1`), + - `R1 <: R2` + +Here we have relied on [alpha renaming](https://en.wikipedia.org/wiki/Lambda_calculus#%CE%B1-conversion) to match the two bound types `X`. + +A partially applied type constructor such as `List` is assumed to be equivalent to +its eta expansion. I.e, `List = [X] =>> List[X]`. This allows type constructors to be compared with type lambdas. + +## Relationship with Parameterized Type Definitions + +A parameterized type definition +```scala +type T[X] = R +``` +is regarded as a shorthand for an unparameterized definition with a type lambda as right-hand side: +```scala +type T = [X] =>> R +``` +If the type definition carries `+` or `-` variance annotations, +it is checked that the variance annotations are satisfied by the type lambda. +For instance, +```scala +type F2[A, +B] = A => B +``` +expands to +```scala +type F2 = [A, B] =>> A => B +``` +and at the same time it is checked that the parameter `B` appears covariantly in `A => B`. + +A parameterized abstract type +```scala +type T[X] >: L <: U +``` +is regarded as shorthand for an unparameterized abstract type with type lambdas as bounds. +```scala +type T >: ([X] =>> L) <: ([X] =>> U) +``` +However, if `L` is `Nothing` it is not parameterized, since `Nothing` is treated as a bottom type for all kinds. For instance, +```scala +type T[X] <: X => X +``` +is expanded to +```scala +type T >: Nothing <: ([X] =>> X => X) +``` +instead of +```scala +type T >: ([X] =>> Nothing) <: ([X] =>> X => X) +``` + +The same expansions apply to type parameters. For instance, +```scala +[F[X] <: Coll[X]] +``` +is treated as a shorthand for +```scala +[F >: Nothing <: [X] =>> Coll[X]] +``` +Abstract types and opaque type aliases remember the variances they were created with. So the type +```scala +type F2[-A, +B] +``` +is known to be contravariant in `A` and covariant in `B` and can be instantiated only +with types that satisfy these constraints. Likewise +```scala +opaque type O[X] = List[X] +``` +`O` is known to be invariant (and not covariant, as its right-hand side would suggest). On the other hand, a transparent alias +```scala +type O2[X] = List[X] +``` +would be treated as covariant, `X` is used covariantly on its right-hand side. + +**Note**: The decision to treat `Nothing` as universal bottom type is provisional, and might be changed after further discussion. + +**Note**: Scala 2 and 3 differ in that Scala 2 also treats `Any` as universal top-type. This is not done in Scala 3. See also the discussion on [kind polymorphism](../other-new-features/kind-polymorphism.md) + +## Curried Type Parameters + +The body of a type lambda can again be a type lambda. Example: +```scala +type TL = [X] =>> [Y] =>> (X, Y) +``` +Currently, no special provision is made to infer type arguments to such curried type lambdas. This is left for future work. diff --git a/docs/_spec/TODOreference/new-types/type-lambdas.md b/docs/_spec/TODOreference/new-types/type-lambdas.md new file mode 100644 index 000000000000..ba88e28f5d56 --- /dev/null +++ b/docs/_spec/TODOreference/new-types/type-lambdas.md @@ -0,0 +1,17 @@ +--- +layout: doc-page +title: "Type Lambdas" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/type-lambdas.html +--- + +A _type lambda_ lets one express a higher-kinded type directly, without +a type definition. + +```scala +[X, Y] =>> Map[Y, X] +``` + +For instance, the type above defines a binary type constructor, which maps arguments `X` and `Y` to `Map[Y, X]`. +Type parameters of type lambdas can have bounds, but they cannot carry `+` or `-` variance annotations. + +[More details](./type-lambdas-spec.md) diff --git a/docs/_spec/TODOreference/new-types/union-types-spec.md b/docs/_spec/TODOreference/new-types/union-types-spec.md new file mode 100644 index 000000000000..d250d3f11713 --- /dev/null +++ b/docs/_spec/TODOreference/new-types/union-types-spec.md @@ -0,0 +1,172 @@ +--- +layout: doc-page +title: "Union Types - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/union-types-spec.html +--- + +## Syntax + +Syntactically, unions follow the same rules as intersections, but have a lower precedence, see +[Intersection Types - More Details](./intersection-types-spec.md). + +### Interaction with pattern matching syntax +`|` is also used in pattern matching to separate pattern alternatives and has +lower precedence than `:` as used in typed patterns, this means that: + +```scala +case _: A | B => ... +``` + +is still equivalent to: + +```scala +case (_: A) | B => ... +``` + +and not to: + +```scala +case _: (A | B) => ... +``` + +## Subtyping Rules + +- `A` is always a subtype of `A | B` for all `A`, `B`. +- If `A <: C` and `B <: C` then `A | B <: C` +- Like `&`, `|` is commutative and associative: + + ```scala + A | B =:= B | A + A | (B | C) =:= (A | B) | C + ``` + +- `&` is distributive over `|`: + + ```scala + A & (B | C) =:= A & B | A & C + ``` + +From these rules it follows that the _least upper bound_ (LUB) of a set of types +is the union of these types. This replaces the +[definition of least upper bound in the Scala 2 specification](https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#least-upper-bounds-and-greatest-lower-bounds). + +## Motivation + +The primary reason for introducing union types in Scala is that they allow us to +guarantee that for every set of types, we can always form a finite LUB. This is +both useful in practice (infinite LUBs in Scala 2 were approximated in an ad-hoc +way, resulting in imprecise and sometimes incredibly long types) and in theory +(the type system of Scala 3 is based on the +[DOT calculus](https://infoscience.epfl.ch/record/227176/files/soundness_oopsla16.pdf), +which has union types). + +Additionally, union types are a useful construct when trying to give types to existing +dynamically typed APIs, this is why they're [an integral part of TypeScript](https://www.typescriptlang.org/docs/handbook/advanced-types.html#union-types) +and have even been [partially implemented in Scala.js](https://github.com/scala-js/scala-js/blob/master/library/src/main/scala/scala/scalajs/js/Union.scala). + +## Join of a union type + +In some situation described below, a union type might need to be widened to +a non-union type, for this purpose we define the _join_ of a union type `T1 | +... | Tn` as the smallest intersection type of base class instances of +`T1`,...,`Tn`. Note that union types might still appear as type arguments in the +resulting type, this guarantees that the join is always finite. + +### Example + +Given + +```scala +trait C[+T] +trait D +trait E +class A extends C[A] with D +class B extends C[B] with D with E +``` + +The join of `A | B` is `C[A | B] & D` + +## Type inference + +When inferring the result type of a definition (`val`, `var`, or `def`) and the +type we are about to infer is a union type, then we replace it by its join. +Similarly, when instantiating a type argument, if the corresponding type +parameter is not upper-bounded by a union type and the type we are about to +instantiate is a union type, we replace it by its join. This mirrors the +treatment of singleton types which are also widened to their underlying type +unless explicitly specified. The motivation is the same: inferring types +which are "too precise" can lead to unintuitive typechecking issues later on. + +**Note:** Since this behavior limits the usability of union types, it might +be changed in the future. For example by not widening unions that have been +explicitly written down by the user and not inferred, or by not widening a type +argument when the corresponding type parameter is covariant. + +See [PR #2330](https://github.com/lampepfl/dotty/pull/2330) and +[Issue #4867](https://github.com/lampepfl/dotty/issues/4867) for further discussions. + +### Example + +```scala +import scala.collection.mutable.ListBuffer +val x = ListBuffer(Right("foo"), Left(0)) +val y: ListBuffer[Either[Int, String]] = x +``` + +This code typechecks because the inferred type argument to `ListBuffer` in the +right-hand side of `x` was `Left[Int, Nothing] | Right[Nothing, String]` which +was widened to `Either[Int, String]`. If the compiler hadn't done this widening, +the last line wouldn't typecheck because `ListBuffer` is invariant in its +argument. + + +## Members + +The members of a union type are the members of its join. + +### Example + +The following code does not typecheck, because method `hello` is not a member of +`AnyRef` which is the join of `A | B`. + +```scala +trait A { def hello: String } +trait B { def hello: String } + +def test(x: A | B) = x.hello // error: value `hello` is not a member of A | B +``` + +On the other hand, the following would be allowed + +```scala +trait C { def hello: String } +trait A extends C with D +trait B extends C with E + +def test(x: A | B) = x.hello // ok as `hello` is a member of the join of A | B which is C +``` + +## Exhaustivity checking + +If the selector of a pattern match is a union type, the match is considered +exhaustive if all parts of the union are covered. + +## Erasure + +The erased type for `A | B` is the _erased least upper bound_ of the erased +types of `A` and `B`. Quoting from the documentation of `TypeErasure#erasedLub`, +the erased LUB is computed as follows: + +- if both argument are arrays of objects, an array of the erased LUB of the element types +- if both arguments are arrays of same primitives, an array of this primitive +- if one argument is array of primitives and the other is array of objects, + [`Object`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html) +- if one argument is an array, [`Object`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html) +- otherwise a common superclass or trait S of the argument classes, with the + following two properties: + * S is minimal: no other common superclass or trait derives from S + * S is last : in the linearization of the first argument type `|A|` + there are no minimal common superclasses or traits that + come after S. + The reason to pick last is that we prefer classes over traits that way, + which leads to more predictable bytecode and (?) faster dynamic dispatch. diff --git a/docs/_spec/TODOreference/new-types/union-types.md b/docs/_spec/TODOreference/new-types/union-types.md new file mode 100644 index 000000000000..ebc4565e36fb --- /dev/null +++ b/docs/_spec/TODOreference/new-types/union-types.md @@ -0,0 +1,46 @@ +--- +layout: doc-page +title: "Union Types" +nightlyOf: https://docs.scala-lang.org/scala3/reference/new-types/union-types.html +--- + +A union type `A | B` has as values all values of type `A` and also all values of type `B`. + + +```scala +case class UserName(name: String) +case class Password(hash: Hash) + +def help(id: UserName | Password) = + val user = id match + case UserName(name) => lookupName(name) + case Password(hash) => lookupPassword(hash) + ... +``` + +Union types are duals of intersection types. `|` is _commutative_: +`A | B` is the same type as `B | A`. + +The compiler will assign a union type to an expression only if such a +type is explicitly given. This can be seen in the following [REPL](https://docs.scala-lang.org/overviews/repl/overview.html) transcript: + +```scala +scala> val password = Password(123) +val password: Password = Password(123) + +scala> val name = UserName("Eve") +val name: UserName = UserName(Eve) + +scala> if true then name else password +val res2: Object = UserName(Eve) + +scala> val either: Password | UserName = if true then name else password +val either: Password | UserName = UserName(Eve) +``` + +The type of `res2` is `Object & Product`, which is a supertype of +`UserName` and `Password`, but not the least supertype `Password | +UserName`. If we want the least supertype, we have to give it +explicitly, as is done for the type of `either`. + +[More details](./union-types-spec.md) diff --git a/docs/_spec/TODOreference/other-new-features/control-syntax.md b/docs/_spec/TODOreference/other-new-features/control-syntax.md new file mode 100644 index 000000000000..92204690f0b7 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/control-syntax.md @@ -0,0 +1,47 @@ +--- +layout: doc-page +title: New Control Syntax +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/control-syntax.html +--- + +Scala 3 has a new "quiet" syntax for control expressions that does not rely on +enclosing the condition in parentheses, and also allows to drop parentheses or braces +around the generators of a `for`-expression. Examples: +```scala +if x < 0 then + "negative" +else if x == 0 then + "zero" +else + "positive" + +if x < 0 then -x else x + +while x >= 0 do x = f(x) + +for x <- xs if x > 0 +yield x * x + +for + x <- xs + y <- ys +do + println(x + y) + +try body +catch case ex: IOException => handle +``` + +The rules in detail are: + + - The condition of an `if`-expression can be written without enclosing parentheses if it is followed by a `then`. + - The condition of a `while`-loop can be written without enclosing parentheses if it is followed by a `do`. + - The enumerators of a `for`-expression can be written without enclosing parentheses or braces if they are followed by a `yield` or `do`. + - A `do` in a `for`-expression expresses a `for`-loop. + - A `catch` can be followed by a single case on the same line. + If there are multiple cases, these have to appear within braces (just like in Scala 2) + or an indented block. +## Rewrites + +The Scala 3 compiler can rewrite source code from old syntax to new syntax and back. +When invoked with options `-rewrite -new-syntax` it will rewrite from old to new syntax, dropping parentheses and braces in conditions and enumerators. When invoked with options `-rewrite -old-syntax` it will rewrite in the reverse direction, inserting parentheses and braces as needed. diff --git a/docs/_spec/TODOreference/other-new-features/creator-applications.md b/docs/_spec/TODOreference/other-new-features/creator-applications.md new file mode 100644 index 000000000000..81f09d897955 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/creator-applications.md @@ -0,0 +1,57 @@ +--- +layout: doc-page +title: "Universal Apply Methods" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/creator-applications.html +--- + +Scala case classes generate apply methods, so that values of case classes can be created using simple +function application, without needing to write `new`. + +Scala 3 generalizes this scheme to all concrete classes. Example: + +```scala +class StringBuilder(s: String): + def this() = this("") + +StringBuilder("abc") // old: new StringBuilder("abc") +StringBuilder() // old: new StringBuilder() +``` + +This works since a companion object with two `apply` methods +is generated together with the class. The object looks like this: + +```scala +object StringBuilder: + inline def apply(s: String): StringBuilder = new StringBuilder(s) + inline def apply(): StringBuilder = new StringBuilder() +``` + +The synthetic object `StringBuilder` and its `apply` methods are called _constructor proxies_. +Constructor proxies are generated even for Java classes and classes coming from Scala 2. +The precise rules are as follows: + + 1. A constructor proxy companion object `object C` is created for a concrete class `C`, + provided the class does not have already a companion, and there is also no other value + or method named `C` defined or inherited in the scope where `C` is defined. + + 2. Constructor proxy `apply` methods are generated for a concrete class provided + + - the class has a companion object (which might have been generated in step 1), and + - that companion object does not already define a member named `apply`. + + Each generated `apply` method forwards to one constructor of the class. It has the + same type and value parameters as the constructor. + +Constructor proxy companions cannot be used as values by themselves. A proxy companion object must +be selected with `apply` (or be applied to arguments, in which case the `apply` is implicitly +inserted). + +Constructor proxies are also not allowed to shadow normal definitions. That is, +if an identifier resolves to a constructor proxy, and the same identifier is also +defined or imported in some other scope, an ambiguity is reported. + +## Motivation + +Leaving out `new` hides an implementation detail and makes code more pleasant to read. Even though +it requires a new rule, it will likely increase the perceived regularity of the language, since case +classes already provide function call creation syntax (and are often defined for this reason alone). diff --git a/docs/_spec/TODOreference/other-new-features/experimental-defs.md b/docs/_spec/TODOreference/other-new-features/experimental-defs.md new file mode 100644 index 000000000000..225b61161652 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/experimental-defs.md @@ -0,0 +1,318 @@ +--- +layout: doc-page +title: "Experimental Definitions" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/experimental-defs.html +--- + +The [`@experimental`](https://scala-lang.org/api/3.x/scala/annotation/experimental.html) annotation allows the definition of an API that is not guaranteed backward binary or source compatibility. +This annotation can be placed on term or type definitions. + +## References to experimental definitions + +Experimental definitions can only be referenced in an experimental scope. Experimental scopes are defined as follows: + +1. The RHS of an experimental `def`, `val`, `var`, `given` or `type` is an experimental scope. Examples: + +
      + Example 1 + + ```scala + import scala.annotation.experimental + + @experimental + def x = () + + def d1 = x // error: value x is marked @experimental and therefore ... + @experimental def d2 = x + + val v1 = x // error: value x is marked @experimental and therefore ... + @experimental val v2 = x + + var vr1 = x // error: value x is marked @experimental and therefore ... + @experimental var vr2 = x + + lazy val lv1 = x // error: value x is marked @experimental and therefore ... + @experimental lazy val lv2 = x + ``` +
      + +
      + Example 2 + + ```scala + import scala.annotation.experimental + + @experimental + val x = () + + @experimental + def f() = () + + @experimental + object X: + def fx() = 1 + + def test1: Unit = + f() // error: def f is marked @experimental and therefore ... + x // error: value x is marked @experimental and therefore ... + X.fx() // error: object X is marked @experimental and therefore ... + import X.fx + fx() // error: object X is marked @experimental and therefore ... + + @experimental + def test2: Unit = + // references to f, x and X are ok because `test2` is experimental + f() + x + X.fx() + import X.fx + fx() + ``` +
      + +
      + Example 3 + + ```scala + import scala.annotation.experimental + + @experimental type E + + type A = E // error type E is marked @experimental and therefore ... + @experimental type B = E + ``` +
      + +
      + Example 4 + + ```scala + import scala.annotation.experimental + + @experimental class A + @experimental type X + @experimental type Y = Int + @experimental opaque type Z = Int + + def test: Unit = + new A // error: class A is marked @experimental and therefore ... + val i0: A = ??? // error: class A is marked @experimental and therefore ... + val i1: X = ??? // error: type X is marked @experimental and therefore ... + val i2: Y = ??? // error: type Y is marked @experimental and therefore ... + val i2: Z = ??? // error: type Y is marked @experimental and therefore ... + () + ``` +
      + +
      + Example 5 + + ```scala + @experimental + trait ExpSAM { + def foo(x: Int): Int + } + def bar(f: ExpSAM): Unit = {} // error: error form rule 2 + + def test: Unit = + bar(x => x) // error: reference to experimental SAM + () + ``` +
      + +2. The signatures of an experimental `def`, `val`, `var`, `given` and `type`, or constructors of `class` and `trait` are experimental scopes. Examples: + +
      + Example 1 + + ```scala + import scala.annotation.experimental + + @experimental def x = 2 + @experimental class A + @experimental type X + @experimental type Y = Int + @experimental opaque type Z = Int + + def test1( + p1: A, // error: class A is marked @experimental and therefore ... + p2: List[A], // error: class A is marked @experimental and therefore ... + p3: X, // error: type X is marked @experimental and therefore ... + p4: Y, // error: type Y is marked @experimental and therefore ... + p5: Z, // error: type Z is marked @experimental and therefore ... + p6: Any = x // error: def x is marked @experimental and therefore ... + ): A = ??? // error: class A is marked @experimental and therefore ... + + @experimental def test2( + p1: A, + p2: List[A], + p3: X, + p4: Y, + p5: Z, + p6: Any = x + ): A = ??? + + class Test1( + p1: A, // error + p2: List[A], // error + p3: X, // error + p4: Y, // error + p5: Z, // error + p6: Any = x // error + ) {} + + @experimental class Test2( + p1: A, + p2: List[A], + p3: X, + p4: Y, + p5: Z, + p6: Any = x + ) {} + + trait Test1( + p1: A, // error + p2: List[A], // error + p3: X, // error + p4: Y, // error + p5: Z, // error + p6: Any = x // error + ) {} + + @experimental trait Test2( + p1: A, + p2: List[A], + p3: X, + p4: Y, + p5: Z, + p6: Any = x + ) {} + ``` +
      + +3. The `extends` clause of an experimental `class`, `trait` or `object` is an experimental scope. Examples: + +
      + Example 1 + + ```scala + import scala.annotation.experimental + + @experimental def x = 2 + + @experimental class A1(x: Any) + class A2(x: Any) + + + @experimental class B1 extends A1(1) + class B2 extends A1(1) // error: class A1 is marked @experimental and therefore marked @experimental and therefore ... + + @experimental class C1 extends A2(x) + class C2 extends A2(x) // error def x is marked @experimental and therefore + ``` +
      + +4. The body of an experimental `class`, `trait` or `object` is an experimental scope. Examples: + +
      + Example 1 + + ```scala + import scala.annotation.experimental + + @experimental def x = 2 + + @experimental class A { + def f = x // ok because A is experimental + } + + @experimental class B { + def f = x // ok because A is experimental + } + + @experimental object C { + def f = x // ok because A is experimental + } + + @experimental class D { + def f = { + object B { + x // ok because A is experimental + } + } + } + ``` + +
      + +5. Annotations of an experimental definition are in experimental scopes. Examples: + +
      + Example 1 + + ```scala + import scala.annotation.experimental + + @experimental class myExperimentalAnnot extends scala.annotation.Annotation + + @myExperimentalAnnot // error + def test: Unit = () + + @experimental + @myExperimentalAnnot + def test: Unit = () + ``` + +
      + +6. Any code compiled using a [_Nightly_](https://search.maven.org/artifact/org.scala-lang/scala3-compiler_3) or _Snapshot_ version of the compiler is considered to be in an experimental scope. +Can use the `-Yno-experimental` compiler flag to disable it and run as a proper release. + +In any other situation, a reference to an experimental definition will cause a compilation error. + +## Experimental inheritance + +All subclasses of an experimental `class` or `trait` must be marked as [`@experimental`](https://scala-lang.org/api/3.x/scala/annotation/experimental.html) even if they are in an experimental scope. +Anonymous classes and SAMs of experimental classes are considered experimental. + +We require explicit annotations to make sure we do not have completion or cycles issues with nested classes. This restriction could be relaxed in the future. + +## Experimental overriding + +For an overriding member `M` and overridden member `O`, if `O` is non-experimental then `M` must be non-experimental. + +This makes sure that we cannot have accidental binary incompatibilities such as the following change. +```diff +class A: + def f: Any = 1 +class B extends A: +- @experimental def f: Int = 2 +``` + +## Test frameworks + +Tests can be defined as experimental. Tests frameworks can execute tests using reflection even if they are in an experimental class, object or method. Examples: + +
      +Example 1 + +Test that touch experimental APIs can be written as follows + +```scala +import scala.annotation.experimental + +@experimental def x = 2 + +class MyTests { + /*@Test*/ def test1 = x // error + @experimental /*@Test*/ def test2 = x +} + +@experimental +class MyExperimentalTests { + /*@Test*/ def test1 = x + /*@Test*/ def test2 = x +} +``` + +
      diff --git a/docs/_spec/TODOreference/other-new-features/export.md b/docs/_spec/TODOreference/other-new-features/export.md new file mode 100644 index 000000000000..40e2ad9df248 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/export.md @@ -0,0 +1,234 @@ +--- +layout: doc-page +title: "Export Clauses" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/export.html +--- + +An export clause defines aliases for selected members of an object. Example: + +```scala +class BitMap +class InkJet + +class Printer: + type PrinterType + def print(bits: BitMap): Unit = ??? + def status: List[String] = ??? + +class Scanner: + def scan(): BitMap = ??? + def status: List[String] = ??? + +class Copier: + private val printUnit = new Printer { type PrinterType = InkJet } + private val scanUnit = new Scanner + + export scanUnit.scan + export printUnit.{status as _, *} + + def status: List[String] = printUnit.status ++ scanUnit.status +``` + +The two `export` clauses define the following _export aliases_ in class `Copier`: + +```scala +final def scan(): BitMap = scanUnit.scan() +final def print(bits: BitMap): Unit = printUnit.print(bits) +final type PrinterType = printUnit.PrinterType +``` + +They can be accessed inside `Copier` as well as from outside: + +```scala +val copier = new Copier +copier.print(copier.scan()) +``` + +An `export` clause has the same format as an import clause. Its general form is: + +```scala +export path . { sel_1, ..., sel_n } +``` + +It consists of a qualifier expression `path`, which must be a stable identifier, followed by +one or more selectors `sel_i` that identify what gets an alias. Selectors can be +of one of the following forms: + + - A _simple selector_ `x` creates aliases for all eligible members of `path` that are named `x`. + - A _renaming selector_ `x as y` creates aliases for all eligible members of `path` that are named `x`, but the alias is named `y` instead of `x`. + - An _omitting selector_ `x as _` prevents `x` from being aliased by a subsequent + wildcard selector. + - A _given selector_ `given x` has an optional type bound `x`. It creates aliases for all eligible given instances that conform to either `x`, or `Any` if `x` is omitted, except for members that are named by a previous simple, renaming, or omitting selector. + - A _wildcard selector_ `*` creates aliases for all eligible members of `path` except for given instances, + synthetic members generated by the compiler and those members that are named by a previous simple, renaming, or omitting selector. + \ + Notes: + - eligible construtor proxies are also included, even though they are synthetic members. + - members created by an export are also included. They are created by the compiler, but are not considered synthetic. + +A member is _eligible_ if all of the following holds: + + - its owner is not a base class of the class[(\*)](#note_class) containing the export clause, + - the member does not override a concrete definition that has as owner + a base class of the class containing the export clause. + - it is accessible at the export clause, + - it is not a constructor, nor the (synthetic) class part of an object, + - it is a given instance (declared with `given`) if and only if the export is from a _given selector_. + +It is a compile-time error if a simple or renaming selector does not identify +any eligible members. + +It is a compile-time error if a simple or renaming selector does not identify any eligible members. + +Type members are aliased by type definitions, and term members are aliased by method definitions. For instance: +```scala +object O: + class C(val x: Int) + def m(c: C): Int = c.x + 1 +export O.* + // generates + // type C = O.C + // def m(c: O.C): Int = O.m(c) +``` + +Export aliases copy the type and value parameters of the members they refer to. +Export aliases are always `final`. Aliases of given instances are again defined as givens (and aliases of old-style implicits are `implicit`). Aliases of extensions are again defined as extensions. Aliases of inline methods or values are again defined `inline`. There are no other modifiers that can be given to an alias. This has the following consequences for overriding: + + - Export aliases cannot be overridden, since they are final. + - Export aliases cannot override concrete members in base classes, since they are + not marked `override`. + - However, export aliases can implement deferred members of base classes. + +Export aliases for public value definitions that are accessed without +referring to private values in the qualifier path +are marked by the compiler as "stable" and their result types are the singleton types of the aliased definitions. This means that they can be used as parts of stable identifier paths, even though they are technically methods. For instance, the following is OK: +```scala +class C { type T } +object O { val c: C = ... } +export O.c +def f: c.T = ... +``` + + +**Restrictions:** + + 1. Export clauses can appear in classes or they can appear at the top-level. An export clause cannot appear as a statement in a block. + 1. If an export clause contains a wildcard or given selector, it is forbidden for its qualifier path to refer to a package. This is because it is not yet known how to safely track wildcard dependencies to a package for the purposes of incremental compilation. + 1. An export renaming hides un-renamed exports matching the target name. For instance, the following + clause would be invalid since `B` is hidden by the renaming `A as B`. + ```scala + export {A as B, B} // error: B is hidden + ``` + + 1. Renamings in an export clause must have pairwise different target names. For instance, the following clause would be invalid: + ```scala + export {A as C, B as C} // error: duplicate renaming + + 1. Simple renaming exports like + ```scala + export status as stat + ``` + are not supported yet. They would run afoul of the restriction that the + exported `a` cannot be already a member of the object containing the export. + This restriction might be lifted in the future. + + +(\*) **Note:** Unless otherwise stated, the term "class" in this discussion also includes object and trait definitions. + +## Motivation + +It is a standard recommendation to prefer composition over inheritance. This is really an application of the principle of least power: Composition treats components as blackboxes whereas inheritance can affect the internal workings of components through overriding. Sometimes the close coupling implied by inheritance is the best solution for a problem, but where this is not necessary the looser coupling of composition is better. + +So far, object-oriented languages including Scala made it much easier to use inheritance than composition. Inheritance only requires an `extends` clause whereas composition required a verbose elaboration of a sequence of forwarders. So in that sense, object-oriented languages are pushing +programmers to a solution that is often too powerful. Export clauses redress the balance. They make composition relationships as concise and easy to express as inheritance relationships. Export clauses also offer more flexibility than extends clauses since members can be renamed or omitted. + +Export clauses also fill a gap opened by the shift from package objects to top-level definitions. One occasionally useful idiom that gets lost in this shift is a package object inheriting from some class. The idiom is often used in a facade like pattern, to make members +of internal compositions available to users of a package. Top-level definitions are not wrapped in a user-defined object, so they can't inherit anything. However, top-level definitions can be export clauses, which supports the facade design pattern in a safer and +more flexible way. + +## Export Clauses in Extensions + +An export clause may also appear in an extension. + +Example: +```scala +class StringOps(x: String): + def *(n: Int): String = ... + def capitalize: String = ... + +extension (x: String) + def take(n: Int): String = x.substring(0, n) + def drop(n: Int): String = x.substring(n) + private def moreOps = new StringOps(x) + export moreOps.* +``` +In this case the qualifier expression must be an identifier that refers to a unique parameterless extension method in the same extension clause. The export will create +extension methods for all accessible term members +in the result of the qualifier path. For instance, the extension above would be expanded to +```scala +extension (x: String) + def take(n: Int): String = x.substring(0, n) + def drop(n: Int): String = x.substring(n) + private def moreOps = StringOps(x) + def *(n: Int): String = moreOps.*(n) + def capitalize: String = moreOps.capitalize +``` + +## Syntax changes: + +``` +TemplateStat ::= ... + | Export +TopStat ::= ... + | Export +ExtMethod ::= ... + | Export +Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} +ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec +ImportSpec ::= NamedSelector + | WildcardSelector + | ‘{’ ImportSelectors) ‘}’ +NamedSelector ::= id [‘as’ (id | ‘_’)] +WildCardSelector ::= ‘*’ | ‘given’ [InfixType] +ImportSelectors ::= NamedSelector [‘,’ ImportSelectors] + | WildCardSelector {‘,’ WildCardSelector} +``` + +## Elaboration of Export Clauses + +Export clauses raise questions about the order of elaboration during type checking. +Consider the following example: + +```scala +class B { val c: Int } +object a { val b = new B } +export a.* +export b.* +``` + +Is the `export b.*` clause legal? If yes, what does it export? Is it equivalent to `export a.b.*`? What about if we swap the last two clauses? + +``` +export b.* +export a.* +``` + +To avoid tricky questions like these, we fix the elaboration order of exports as follows. + +Export clauses are processed when the type information of the enclosing object or class is completed. Completion so far consisted of the following steps: + + 1. Elaborate any annotations of the class. + 2. Elaborate the parameters of the class. + 3. Elaborate the self type of the class, if one is given. + 4. Enter all definitions of the class as class members, with types to be completed + on demand. + 5. Determine the types of all parents of the class. + + With export clauses, the following steps are added: + + 6. Compute the types of all paths in export clauses. + 7. Enter export aliases for the eligible members of all paths in export clauses. + +It is important that steps 6 and 7 are done in sequence: We first compute the types of _all_ +paths in export clauses and only after this is done we enter any export aliases as class members. This means that a path of an export clause cannot refer to an alias made available +by another export clause of the same class. diff --git a/docs/_spec/TODOreference/other-new-features/indentation.md b/docs/_spec/TODOreference/other-new-features/indentation.md new file mode 100644 index 000000000000..e931030ab696 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/indentation.md @@ -0,0 +1,509 @@ +--- +layout: doc-page +title: "Optional Braces" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/indentation.html +--- + +Scala 3 enforces some rules on indentation and allows some occurrences of braces `{...}` to be optional: + +- First, some badly indented programs are flagged with warnings. +- Second, some occurrences of braces `{...}` are made optional. Generally, the rule + is that adding a pair of optional braces will not change the meaning of a well-indented program. + +These changes can be turned off with the compiler flag `-no-indent`. + +## Indentation Rules + +The compiler enforces two rules for well-indented programs, flagging violations as warnings. + + 1. In a brace-delimited region, no statement is allowed to start to the left + of the first statement after the opening brace that starts a new line. + + This rule is helpful for finding missing closing braces. It prevents errors like: + + ```scala + if (x < 0) { + println(1) + println(2) + + println("done") // error: indented too far to the left + ``` + + 2. If significant indentation is turned off (i.e. under Scala 2 mode or under `-no-indent`) and we are at the start of an indented sub-part of an expression, and the indented part ends in a newline, the next statement must start at an indentation width less than the sub-part. This prevents errors where an opening brace was forgotten, as in + + ```scala + if (x < 0) + println(1) + println(2) // error: missing `{` + ``` + +These rules still leave a lot of leeway how programs should be indented. For instance, they do not impose +any restrictions on indentation within expressions, nor do they require that all statements of an indentation block line up exactly. + +The rules are generally helpful in pinpointing the root cause of errors related to missing opening or closing braces. These errors are often quite hard to diagnose, in particular in large programs. + +## Optional Braces + +The compiler will insert `` or `` +tokens at certain line breaks. Grammatically, pairs of `` and `` tokens have the same effect as pairs of braces `{` and `}`. + +The algorithm makes use of a stack `IW` of previously encountered indentation widths. The stack initially holds a single element with a zero indentation width. The _current indentation width_ is the indentation width of the top of the stack. + +There are two rules: + + 1. An `` is inserted at a line break, if + + - An indentation region can start at the current position in the source, and + - the first token on the next line has an indentation width strictly greater + than the current indentation width + + An indentation region can start + + - after the leading parameters of an `extension`, or + - after a `with` in a given instance, or + - after a `:` at the start of a template body (see discussion of `` below), or + - after one of the following tokens: + + ``` + = => ?=> <- catch do else finally for + if match return then throw try while yield + ``` + + - after the closing `)` of a condition in an old-style `if` or `while`. + - after the closing `)` or `}` of the enumerations of an old-style `for` loop without a `do`. + + If an `` is inserted, the indentation width of the token on the next line + is pushed onto `IW`, which makes it the new current indentation width. + + 2. An `` is inserted at a line break, if + + - the first token on the next line has an indentation width strictly less + than the current indentation width, and + - the last token on the previous line is not one of the following tokens + which indicate that the previous statement continues: + ``` + then else do catch finally yield match + ``` + - if the first token on the next line is a + [leading infix operator](../changed-features/operators.md). + then its indentation width is less then the current indentation width, + and it either matches a previous indentation width or is also less + than the enclosing indentation width. + + If an `` is inserted, the top element is popped from `IW`. + If the indentation width of the token on the next line is still less than the new current indentation width, step (2) repeats. Therefore, several `` tokens + may be inserted in a row. + + The following two additional rules support parsing of legacy code with ad-hoc layout. They might be withdrawn in future language versions: + + - An `` is also inserted if the next token following a statement sequence starting with an `` closes an indentation region, i.e. is one of `then`, `else`, `do`, `catch`, `finally`, `yield`, `}`, `)`, `]` or `case`. + + - An `` is finally inserted in front of a comma that follows a statement sequence starting with an `` if the indented region is itself enclosed in parentheses. + +It is an error if the indentation width of the token following an `` does not match the indentation of some previous line in the enclosing indentation region. For instance, the following would be rejected. + +```scala +if x < 0 then + -x + else // error: `else` does not align correctly + x +``` + +Indentation tokens are only inserted in regions where newline statement separators are also inferred: +at the top-level, inside braces `{...}`, but not inside parentheses `(...)`, patterns or types. + +**Note:** The rules for leading infix operators above are there to make sure that +```scala + one + + two.match + case 1 => b + case 2 => c + + three +``` +is parsed as `one + (two.match ...) + three`. Also, that +```scala +if x then + a + + b + + c +else d +``` +is parsed as `if x then a + b + c else d`. + +## Optional Braces Around Template Bodies + +The Scala grammar uses the term _template body_ for the definitions of a class, trait, or object that are normally enclosed in braces. The braces around a template body can also be omitted by means of the following rule. + +A template body can alternatively consist of a colon followed by one or more indented statements. To this purpose we introduce a new `` token that reads as +the standard colon "`:`" but is generated instead of it where `` +is legal according to the context free syntax, but only if the previous token +is an alphanumeric identifier, a backticked identifier, or one of the tokens `this`, `super`, "`)`", and "`]`". + +An indentation region can start after a ``. A template body may be either enclosed in braces, or it may start with +` ` and end with ``. +Analogous rules apply for enum bodies, type refinements, and local packages containing nested definitions. + +With these new rules, the following constructs are all valid: + +```scala +trait A: + def f: Int + +class C(x: Int) extends A: + def f = x + +object O: + def f = 3 + +enum Color: + case Red, Green, Blue + +new A: + def f = 3 + +package p: + def a = 1 + +package q: + def b = 2 +``` + +In each case, the `:` at the end of line can be replaced without change of meaning by a pair of braces that enclose the following indented definition(s). + +The syntax changes allowing this are as follows: + +Define for an arbitrary sequence of tokens or non-terminals `TS`: + +``` +:<<< TS >>> ::= ‘{’ TS ‘}’ + | +``` +Then the grammar changes as follows: +``` +TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> +EnumBody ::= :<<< [SelfType] EnumStat {semi EnumStat} >>> +Refinement ::= :<<< [RefineDcl] {semi [RefineDcl]} >>> +Packaging ::= ‘package’ QualId :<<< TopStats >>> +``` + +## Spaces vs Tabs + +Indentation prefixes can consist of spaces and/or tabs. Indentation widths are the indentation prefixes themselves, ordered by the string prefix relation. So, so for instance "2 tabs, followed by 4 spaces" is strictly less than "2 tabs, followed by 5 spaces", but "2 tabs, followed by 4 spaces" is incomparable to "6 tabs" or to "4 spaces, followed by 2 tabs". It is an error if the indentation width of some line is incomparable with the indentation width of the region that's current at that point. To avoid such errors, it is a good idea not to mix spaces and tabs in the same source file. + +## Indentation and Braces + +Indentation can be mixed freely with braces `{...}`, as well as brackets `[...]` and parentheses `(...)`. For interpreting indentation inside such regions, the following rules apply. + + 1. The assumed indentation width of a multiline region enclosed in braces is the + indentation width of the first token that starts a new line after the opening brace. + + 2. The assumed indentation width of a multiline region inside brackets or parentheses is: + + - if the opening bracket or parenthesis is at the end of a line, the indentation width of token following it, + - otherwise, the indentation width of the enclosing region. + + 3. On encountering a closing brace `}`, bracket `]` or parenthesis `)`, as many `` tokens as necessary are inserted to close all open nested indentation regions. + +For instance, consider: +```scala +{ + val x = f(x: Int, y => + x * ( + y + 1 + ) + + (x + + x) + ) +} +``` + - Here, the indentation width of the region enclosed by the braces is 3 (i.e. the indentation width of the +statement starting with `val`). + - The indentation width of the region in parentheses that follows `f` is also 3, since the opening + parenthesis is not at the end of a line. + - The indentation width of the region in parentheses around `y + 1` is 9 + (i.e. the indentation width of `y + 1`). + - Finally, the indentation width of the last region in parentheses starting with `(x` is 6 (i.e. the indentation width of the indented region following the `=>`. + +## Special Treatment of Case Clauses + +The indentation rules for `match` expressions and `catch` clauses are refined as follows: + +- An indentation region is opened after a `match` or `catch` also if the following `case` + appears at the indentation width that's current for the `match` itself. +- In that case, the indentation region closes at the first token at that + same indentation width that is not a `case`, or at any token with a smaller + indentation width, whichever comes first. + +The rules allow to write `match` expressions where cases are not indented themselves, as in the example below: + +```scala +x match +case 1 => print("I") +case 2 => print("II") +case 3 => print("III") +case 4 => print("IV") +case 5 => print("V") + +println(".") +``` + +## Using Indentation to Signal Statement Continuation + +Indentation is used in some situations to decide whether to insert a virtual semicolon between +two consecutive lines or to treat them as one statement. Virtual semicolon insertion is +suppressed if the second line is indented more relative to the first one, and either the second line +starts with "`(`", "`[`", or "`{`" or the first line ends with `return`. Examples: + +```scala +f(x + 1) + (2, 3) // equivalent to `f(x + 1)(2, 3)` + +g(x + 1) +(2, 3) // equivalent to `g(x + 1); (2, 3)` + +h(x + 1) + {} // equivalent to `h(x + 1){}` + +i(x + 1) +{} // equivalent to `i(x + 1); {}` + +if x < 0 then return + a + b // equivalent to `if x < 0 then return a + b` + +if x < 0 then return +println(a + b) // equivalent to `if x < 0 then return; println(a + b)` +``` +In Scala 2, a line starting with "`{`" always continues the function call on the preceding line, +irrespective of indentation, whereas a virtual semicolon is inserted in all other cases. +The Scala-2 behavior is retained under source `-no-indent` or `-source 3.0-migration`. + + + +## The End Marker + +Indentation-based syntax has many advantages over other conventions. But one possible problem is that it makes it hard to discern when a large indentation region ends, since there is no specific token that delineates the end. Braces are not much better since a brace by itself also contains no information about what region is closed. + +To solve this problem, Scala 3 offers an optional `end` marker. Example: + +```scala +def largeMethod(...) = + ... + if ... then ... + else + ... // a large block + end if + ... // more code +end largeMethod +``` + +An `end` marker consists of the identifier `end` and a follow-on specifier token that together constitute all the tokes of a line. Possible specifier tokens are +identifiers or one of the following keywords + +```scala +if while for match try new this val given +``` + +End markers are allowed in statement sequences. The specifier token `s` of an end marker must correspond to the statement that precedes it. This means: + +- If the statement defines a member `x` then `s` must be the same identifier `x`. +- If the statement defines a constructor then `s` must be `this`. +- If the statement defines an anonymous given, then `s` must be `given`. +- If the statement defines an anonymous extension, then `s` must be `extension`. +- If the statement defines an anonymous class, then `s` must be `new`. +- If the statement is a `val` definition binding a pattern, then `s` must be `val`. +- If the statement is a package clause that refers to package `p`, then `s` must be the same identifier `p`. +- If the statement is an `if`, `while`, `for`, `try`, or `match` statement, then `s` must be that same token. + +For instance, the following end markers are all legal: + +```scala +package p1.p2: + + abstract class C(): + + def this(x: Int) = + this() + if x > 0 then + val a :: b = + x :: Nil + end val + var y = + x + end y + while y > 0 do + println(y) + y -= 1 + end while + try + x match + case 0 => println("0") + case _ => + end match + finally + println("done") + end try + end if + end this + + def f: String + end C + + object C: + given C = + new C: + def f = "!" + end f + end new + end given + end C + + extension (x: C) + def ff: String = x.f ++ x.f + end extension + +end p2 +``` + +### When to Use End Markers + +It is recommended that `end` markers are used for code where the extent of an indentation region is not immediately apparent "at a glance". People will have different preferences what this means, but one can nevertheless give some guidelines that stem from experience. An end marker makes sense if + +- the construct contains blank lines, or +- the construct is long, say 15-20 lines or more, +- the construct ends heavily indented, say 4 indentation levels or more. + +If none of these criteria apply, it's often better to not use an end marker since the code will be just as clear and more concise. If there are several ending regions that satisfy one of the criteria above, we usually need an end marker only for the outermost closed region. So cascades of end markers as in the example above are usually better avoided. + +### Syntax + +``` +EndMarker ::= ‘end’ EndMarkerTag -- when followed by EOL +EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ | ‘try’ + | ‘new’ | ‘this’ | ‘given’ | ‘extension’ | ‘val’ +BlockStat ::= ... | EndMarker +TemplateStat ::= ... | EndMarker +TopStat ::= ... | EndMarker +``` + +## Example + +Here is a (somewhat meta-circular) example of code using indentation. It provides a concrete representation of indentation widths as defined above together with efficient operations for constructing and comparing indentation widths. + +```scala +enum IndentWidth: + case Run(ch: Char, n: Int) + case Conc(l: IndentWidth, r: Run) + + def <= (that: IndentWidth): Boolean = this match + case Run(ch1, n1) => + that match + case Run(ch2, n2) => n1 <= n2 && (ch1 == ch2 || n1 == 0) + case Conc(l, r) => this <= l + case Conc(l1, r1) => + that match + case Conc(l2, r2) => l1 == l2 && r1 <= r2 + case _ => false + + def < (that: IndentWidth): Boolean = + this <= that && !(that <= this) + + override def toString: String = + this match + case Run(ch, n) => + val kind = ch match + case ' ' => "space" + case '\t' => "tab" + case _ => s"'$ch'-character" + val suffix = if n == 1 then "" else "s" + s"$n $kind$suffix" + case Conc(l, r) => + s"$l, $r" + +object IndentWidth: + private inline val MaxCached = 40 + + private val spaces = IArray.tabulate(MaxCached + 1)(new Run(' ', _)) + private val tabs = IArray.tabulate(MaxCached + 1)(new Run('\t', _)) + + def Run(ch: Char, n: Int): Run = + if n <= MaxCached && ch == ' ' then + spaces(n) + else if n <= MaxCached && ch == '\t' then + tabs(n) + else + new Run(ch, n) + end Run + + val Zero = Run(' ', 0) +end IndentWidth +``` + +## Settings and Rewrites + +Significant indentation is enabled by default. It can be turned off by giving any of the options `-no-indent`, `-old-syntax` and `-source 3.0-migration`. If indentation is turned off, it is nevertheless checked that indentation conforms to the logical program structure as defined by braces. If that is not the case, the compiler issues a warning. + +The Scala 3 compiler can rewrite source code to indented code and back. +When invoked with options `-rewrite -indent` it will rewrite braces to +indented regions where possible. When invoked with options `-rewrite -no-indent` it will rewrite in the reverse direction, inserting braces for indentation regions. +The `-indent` option only works on [new-style syntax](./control-syntax.md). So to go from old-style syntax to new-style indented code one has to invoke the compiler twice, first with options `-rewrite -new-syntax`, then again with options +`-rewrite -indent`. To go in the opposite direction, from indented code to old-style syntax, it's `-rewrite -no-indent`, followed by `-rewrite -old-syntax`. + +## Variant: Indentation Marker `:` for Arguments + +Generally, the possible indentation regions coincide with those regions where braces `{...}` are also legal, no matter whether the braces enclose an expression or a set of definitions. There is one exception, though: Arguments to functions can be enclosed in braces but they cannot be simply indented instead. Making indentation always significant for function arguments would be too restrictive and fragile. + +To allow such arguments to be written without braces, a variant of the indentation scheme is implemented under language import +```scala +import language.experimental.fewerBraces +``` +In this variant, a `` token is also recognized where function argument would be expected. Examples: + +```scala +times(10): + println("ah") + println("ha") +``` + +or + +```scala +credentials `++`: + val file = Path.userHome / ".credentials" + if file.exists + then Seq(Credentials(file)) + else Seq() +``` + +or + +```scala +xs.map: + x => + val y = x - 1 + y * y +``` +What's more, a `:` in these settings can also be followed on the same line by the parameter part and arrow of a lambda. So the last example could be compressed to this: + +```scala +xs.map: x => + val y = x - 1 + y * y +``` +and the following would also be legal: +```scala +xs.foldLeft(0): (x, y) => + x + y +``` + +The grammar changes for this variant are as follows. + +``` +SimpleExpr ::= ... + | SimpleExpr ColonArgument +InfixExpr ::= ... + | InfixExpr id ColonArgument +ColonArgument ::= colon [LambdaStart] + indent (CaseClauses | Block) outdent +LambdaStart ::= FunParams (‘=>’ | ‘?=>’) + | HkTypeParamClause ‘=>’ +``` \ No newline at end of file diff --git a/docs/_spec/TODOreference/other-new-features/kind-polymorphism.md b/docs/_spec/TODOreference/other-new-features/kind-polymorphism.md new file mode 100644 index 000000000000..8f0172c4c04b --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/kind-polymorphism.md @@ -0,0 +1,47 @@ +--- +layout: doc-page +title: "Kind Polymorphism" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/kind-polymorphism.html +--- + +Normally type parameters in Scala are partitioned into _kinds_. First-level types are types of values. Higher-kinded types are type constructors +such as `List` or `Map`. The kind of a type is indicated by the top type of which it is a subtype. Normal types are subtypes of `Any`, +covariant single argument type constructors such as `List` are subtypes of `[+X] =>> Any`, and the `Map` type constructor is +a subtype of `[X, +Y] =>> Any`. + +A type can be used only as prescribed by its kind. Subtypes of `Any` cannot be applied to type arguments whereas subtypes of `[X] =>> Any` +_must_ be applied to a type argument, unless they are passed to type parameters of the same kind. + +Sometimes we would like to have type parameters that can have more than one kind, for instance to define an implicit +value that works for parameters of any kind. This is now possible through a form of (_subtype_) kind polymorphism. +Kind polymorphism relies on the special type [`scala.AnyKind`](https://scala-lang.org/api/3.x/scala/AnyKind.html) that can be used as an upper bound of a type. + +```scala +def f[T <: AnyKind] = ... +``` + +The actual type arguments of `f` can then be types of arbitrary kinds. So the following would all be legal: + +```scala +f[Int] +f[List] +f[Map] +f[[X] =>> String] +``` + +We call type parameters and abstract types with an `AnyKind` upper bound _any-kinded types_. +Since the actual kind of an any-kinded type is unknown, its usage must be heavily restricted: An any-kinded type +can be neither the type of a value, nor can it be instantiated with type parameters. So about the only +thing one can do with an any-kinded type is to pass it to another any-kinded type argument. +Nevertheless, this is enough to achieve some interesting generalizations that work across kinds, typically +through advanced uses of implicits. + +(todo: insert good concise example) + +Some technical details: [`AnyKind`](https://scala-lang.org/api/3.x/scala/AnyKind.html) is a synthesized class just like `Any`, but without any members. It extends no other class. +It is declared `abstract` and `final`, so it can be neither instantiated nor extended. + +`AnyKind` plays a special role in Scala's subtype system: It is a supertype of all other types no matter what their kind is. It is also assumed to be kind-compatible with all other types. Furthermore, `AnyKind` is treated as a higher-kinded type (so it cannot be used as a type of values), but at the same time it has no type parameters (so it cannot be instantiated). + +**Note**: This feature is considered experimental but stable and it can be disabled under compiler flag +(i.e. `-Yno-kind-polymorphism`). diff --git a/docs/_spec/TODOreference/other-new-features/matchable.md b/docs/_spec/TODOreference/other-new-features/matchable.md new file mode 100644 index 000000000000..234fdf03220c --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/matchable.md @@ -0,0 +1,141 @@ +--- +layout: doc-page +title: "The Matchable Trait" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/matchable.html +--- + +A new trait [`Matchable`](https://scala-lang.org/api/3.x/scala/Matchable.html) controls the ability to pattern match. + +## The Problem + +The Scala 3 standard library has a type [`IArray`](https://scala-lang.org/api/3.x/scala.html#IArray-0) for immutable +arrays that is defined like this: + +```scala + opaque type IArray[+T] = Array[_ <: T] +``` + +The `IArray` type offers extension methods for `length` and `apply`, but not for `update`; hence it seems values of type `IArray` cannot be updated. + +However, there is a potential hole due to pattern matching. Consider: + +```scala +val imm: IArray[Int] = ... +imm match + case a: Array[Int] => a(0) = 1 +``` + +The test will succeed at runtime since [`IArray`](https://scala-lang.org/api/3.x/scala.html#IArray-0)s _are_ represented as +`Array`s at runtime. But if we allowed it, it would break the fundamental abstraction of immutable arrays. + +__Aside:__ One could also achieve the same by casting: + +```scala +imm.asInstanceOf[Array[Int]](0) = 1 +``` + +But that is not as much of a problem since in Scala `asInstanceOf` is understood to be low-level and unsafe. By contrast, a pattern match that compiles without warning or error should not break abstractions. + +Note also that the problem is not tied to [opaque types](opaques.md) as match selectors. The following slight variant with a value of parametric +type `T` as match selector leads to the same problem: + +```scala +def f[T](x: T) = x match + case a: Array[Int] => a(0) = 0 +f(imm) +``` + +Finally, note that the problem is not linked to just [opaque types](opaques.md). No unbounded type parameter or abstract type should be decomposable with a pattern match. + +## The Solution + +There is a new type [`scala.Matchable`](https://scala-lang.org/api/3.x/scala/Matchable.html) that controls pattern matching. When typing a pattern match of a constructor pattern `C(...)` or +a type pattern `_: C` it is required that the selector type conforms +to `Matchable`. If that's not the case a warning is issued. For instance when compiling the example at the start of this section we get: + +``` +> sc ../new/test.scala -source future +-- Warning: ../new/test.scala:4:12 --------------------------------------------- +4 | case a: Array[Int] => a(0) = 0 + | ^^^^^^^^^^ + | pattern selector should be an instance of Matchable, + | but it has unmatchable type IArray[Int] instead +``` + +To allow migration from Scala 2 and cross-compiling +between Scala 2 and 3 the warning is turned on only for `-source future-migration` or higher. + +[`Matchable`](https://scala-lang.org/api/3.x/scala/Matchable.html) is a universal trait with `Any` as its parent class. It is extended by both [`AnyVal`](https://scala-lang.org/api/3.x/scala/AnyVal.html) and [`AnyRef`](https://scala-lang.org/api/3.x/scala/AnyRef.html). Since `Matchable` is a supertype of every concrete value or reference class it means that instances of such classes can be matched as before. However, match selectors of the following types will produce a warning: + +- Type `Any`: if pattern matching is required one should use `Matchable` instead. +- Unbounded type parameters and abstract types: If pattern matching is required they should have an upper bound `Matchable`. +- Type parameters and abstract types that are only bounded by some + universal trait: Again, `Matchable` should be added as a bound. + +Here is the hierarchy of top-level classes and traits with their defined methods: + +```scala +abstract class Any: + def getClass + def isInstanceOf + def asInstanceOf + def == + def != + def ## + def equals + def hashCode + def toString + +trait Matchable extends Any + +class AnyVal extends Any, Matchable +class Object extends Any, Matchable +``` + +[`Matchable`](https://scala-lang.org/api/3.x/scala/Matchable.html) is currently a marker trait without any methods. Over time +we might migrate methods `getClass` and `isInstanceOf` to it, since these are closely related to pattern-matching. + +## `Matchable` and Universal Equality + +Methods that pattern-match on selectors of type `Any` will need a cast once the +Matchable warning is turned on. The most common such method is the universal +`equals` method. It will have to be written as in the following example: + +```scala +class C(val x: String): + + override def equals(that: Any): Boolean = + that.asInstanceOf[Matchable] match + case that: C => this.x == that.x + case _ => false +``` + +The cast of `that` to [`Matchable`](https://scala-lang.org/api/3.x/scala/Matchable.html) serves as an indication that universal equality +is unsafe in the presence of abstract types and opaque types since it cannot properly distinguish the meaning of a type from its representation. The cast +is guaranteed to succeed at run-time since `Any` and [`Matchable`](https://scala-lang.org/api/3.x/scala/Matchable.html) both erase to +`Object`. + +For instance, consider the definitions + +```scala +opaque type Meter = Double +def Meter(x: Double): Meter = x + +opaque type Second = Double +def Second(x: Double): Second = x +``` + +Here, universal `equals` will return true for + +```scala + Meter(10).equals(Second(10)) +``` + +even though this is clearly false mathematically. With [multiversal equality](../contextual/multiversal-equality.md) one can mitigate that problem somewhat by turning + +```scala + import scala.language.strictEquality + Meter(10) == Second(10) +``` + +into a type error. diff --git a/docs/_spec/TODOreference/other-new-features/opaques-details.md b/docs/_spec/TODOreference/other-new-features/opaques-details.md new file mode 100644 index 000000000000..d7305a249089 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/opaques-details.md @@ -0,0 +1,126 @@ +--- +layout: doc-page +title: "Opaque Type Aliases: More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/opaques-details.html +--- + +## Syntax + +``` +Modifier ::= ... + | ‘opaque’ +``` + +`opaque` is a [soft modifier](../soft-modifier.md). It can still be used as a normal identifier when it is not in front of a definition keyword. + +Opaque type aliases must be members of classes, traits, or objects, or they are defined +at the top-level. They cannot be defined in local blocks. + +## Type Checking + +The general form of a (monomorphic) opaque type alias is + +```scala +opaque type T >: L <: U = R +``` + +where the lower bound `L` and the upper bound `U` may be missing, in which case they are assumed to be [`scala.Nothing`](https://scala-lang.org/api/3.x/scala/Nothing.html) and [`scala.Any`](https://scala-lang.org/api/3.x/scala/Any.html), respectively. If bounds are given, it is checked that the right-hand side `R` conforms to them, i.e. `L <: R` and `R <: U`. F-bounds are not supported for opaque type aliases: `T` is not allowed to appear in `L` or `U`. + +Inside the scope of the alias definition, the alias is transparent: `T` is treated +as a normal alias of `R`. Outside its scope, the alias is treated as the abstract type +```scala +type T >: L <: U +``` +A special case arises if the opaque type alias is defined in an object. Example: + +```scala +object o: + opaque type T = R +``` + +In this case we have inside the object (also for non-opaque types) that `o.T` is equal to +`T` or its expanded form `o.this.T`. Equality is understood here as mutual subtyping, i.e. +`o.T <: o.this.T` and `o.this.T <: T`. Furthermore, we have by the rules of opaque type aliases +that `o.this.T` equals `R`. The two equalities compose. That is, inside `o`, it is +also known that `o.T` is equal to `R`. This means the following code type-checks: + +```scala +object o: + opaque type T = Int + val x: Int = id(2) +def id(x: o.T): o.T = x +``` + +Opaque type aliases cannot be `private` and cannot be overridden in subclasses. +Opaque type aliases cannot have a context function type as right-hand side. + +## Type Parameters of Opaque Types + +Opaque type aliases can have a single type parameter list. The following aliases +are well-formed +```scala +opaque type F[T] = (T, T) +opaque type G = [T] =>> List[T] +``` +but the following are not: +```scala +opaque type BadF[T] = [U] =>> (T, U) +opaque type BadG = [T] =>> [U] => (T, U) +``` + +## Translation of Equality + +Comparing two values of opaque type with `==` or `!=` normally uses universal equality, +unless another overloaded `==` or `!=` operator is defined for the type. To avoid +boxing, the operation is mapped after type checking to the (in-)equality operator +defined on the underlying type. For instance, +```scala + opaque type T = Int + + ... + val x: T + val y: T + x == y // uses Int equality for the comparison. +``` + +## Top-level Opaque Types + +An opaque type alias on the top-level is transparent in all other top-level definitions in the sourcefile where it appears, but is opaque in nested +objects and classes and in all other source files. Example: +```scala +// in test1.scala +opaque type A = String +val x: A = "abc" + +object obj: + val y: A = "abc" // error: found: "abc", required: A + +// in test2.scala +def z: String = x // error: found: A, required: String +``` +This behavior becomes clear if one recalls that top-level definitions are placed in their own synthetic object. For instance, the code in `test1.scala` would expand to +```scala +object test1$package: + opaque type A = String + val x: A = "abc" + +object obj: + val y: A = "abc" // error: cannot assign "abc" to opaque type alias A +``` +The opaque type alias `A` is transparent in its scope, which includes the definition of `x`, but not the definitions of `obj` and `y`. + + +## Relationship to SIP 35 + +Opaque types in Scala 3 are an evolution from what is described in +[Scala SIP 35](https://docs.scala-lang.org/sips/opaque-types.html). + +The differences compared to the state described in this SIP are: + + 1. Opaque type aliases cannot be defined anymore in local statement sequences. + 2. The scope where an opaque type alias is visible is now the whole scope where + it is defined, instead of just a companion object. + 3. The notion of a companion object for opaque type aliases has been dropped. + 4. Opaque type aliases can have bounds. + 5. The notion of type equality involving opaque type aliases has been clarified. It was + strengthened with respect to the previous implementation of SIP 35. diff --git a/docs/_spec/TODOreference/other-new-features/opaques.md b/docs/_spec/TODOreference/other-new-features/opaques.md new file mode 100644 index 000000000000..d8c4d37bcb3b --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/opaques.md @@ -0,0 +1,179 @@ +--- +layout: doc-page +title: "Opaque Type Aliases" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/opaques.html +--- + +Opaque types aliases provide type abstraction without any overhead. Example: + +```scala +object MyMath: + + opaque type Logarithm = Double + + object Logarithm: + + // These are the two ways to lift to the Logarithm type + + def apply(d: Double): Logarithm = math.log(d) + + def safe(d: Double): Option[Logarithm] = + if d > 0.0 then Some(math.log(d)) else None + + end Logarithm + + // Extension methods define opaque types' public APIs + extension (x: Logarithm) + def toDouble: Double = math.exp(x) + def + (y: Logarithm): Logarithm = Logarithm(math.exp(x) + math.exp(y)) + def * (y: Logarithm): Logarithm = x + y + +end MyMath +``` + +This introduces `Logarithm` as a new abstract type, which is implemented as `Double`. +The fact that `Logarithm` is the same as `Double` is only known in the scope where +`Logarithm` is defined, which in the above example corresponds to the object `MyMath`. +Or in other words, within the scope, it is treated as a type alias, but this is opaque to the outside world +where, in consequence, `Logarithm` is seen as an abstract type that has nothing to do with `Double`. + +The public API of `Logarithm` consists of the `apply` and `safe` methods defined in the companion object. +They convert from `Double`s to `Logarithm` values. Moreover, an operation `toDouble` that converts the other way, and operations `+` and `*` are defined as extension methods on `Logarithm` values. +The following operations would be valid because they use functionality implemented in the `MyMath` object. + +```scala +import MyMath.Logarithm + +val l = Logarithm(1.0) +val l2 = Logarithm(2.0) +val l3 = l * l2 +val l4 = l + l2 +``` + +But the following operations would lead to type errors: + +```scala +val d: Double = l // error: found: Logarithm, required: Double +val l2: Logarithm = 1.0 // error: found: Double, required: Logarithm +l * 2 // error: found: Int(2), required: Logarithm +l / l2 // error: `/` is not a member of Logarithm +``` + +## Bounds For Opaque Type Aliases + +Opaque type aliases can also come with bounds. Example: + +```scala +object Access: + + opaque type Permissions = Int + opaque type PermissionChoice = Int + opaque type Permission <: Permissions & PermissionChoice = Int + + extension (x: PermissionChoice) + def | (y: PermissionChoice): PermissionChoice = x | y + extension (x: Permissions) + def & (y: Permissions): Permissions = x | y + extension (granted: Permissions) + def is(required: Permissions) = (granted & required) == required + def isOneOf(required: PermissionChoice) = (granted & required) != 0 + + val NoPermission: Permission = 0 + val Read: Permission = 1 + val Write: Permission = 2 + val ReadWrite: Permissions = Read | Write + val ReadOrWrite: PermissionChoice = Read | Write + +end Access +``` + +The `Access` object defines three opaque type aliases: + +- `Permission`, representing a single permission, +- `Permissions`, representing a set of permissions with the meaning "all of these permissions granted", +- `PermissionChoice`, representing a set of permissions with the meaning "at least one of these permissions granted". + +Outside the `Access` object, values of type `Permissions` may be combined using the `&` operator, +where `x & y` means "all permissions in `x` *and* in `y` granted". +Values of type `PermissionChoice` may be combined using the `|` operator, +where `x | y` means "a permission in `x` *or* in `y` granted". + +Note that inside the `Access` object, the `&` and `|` operators always resolve to the corresponding methods of `Int`, +because members always take precedence over extension methods. +For that reason, the `|` extension method in `Access` does not cause infinite recursion. + +In particular, the definition of `ReadWrite` must use `|`, the bitwise operator for `Int`, +even though client code outside `Access` would use `&`, the extension method on `Permissions`. +The internal representations of `ReadWrite` and `ReadOrWrite` are identical, but this is not visible to the client, +which is interested only in the semantics of `Permissions`, as in the example below. + +All three opaque type aliases have the same underlying representation type `Int`. The +`Permission` type has an upper bound `Permissions & PermissionChoice`. This makes +it known outside the `Access` object that `Permission` is a subtype of the other +two types. Hence, the following usage scenario type-checks. + +```scala +object User: + import Access.* + + case class Item(rights: Permissions) + extension (item: Item) + def +(other: Item): Item = Item(item.rights & other.rights) + + val roItem = Item(Read) // OK, since Permission <: Permissions + val woItem = Item(Write) + val rwItem = Item(ReadWrite) + val noItem = Item(NoPermission) + + assert(!roItem.rights.is(ReadWrite)) + assert(roItem.rights.isOneOf(ReadOrWrite)) + + assert(rwItem.rights.is(ReadWrite)) + assert(rwItem.rights.isOneOf(ReadOrWrite)) + + assert(!noItem.rights.is(ReadWrite)) + assert(!noItem.rights.isOneOf(ReadOrWrite)) + + assert((roItem + woItem).rights.is(ReadWrite)) +end User +``` +On the other hand, the call `roItem.rights.isOneOf(ReadWrite)` would give a type error: +```scala + assert(roItem.rights.isOneOf(ReadWrite)) + ^^^^^^^^^ + Found: (Access.ReadWrite : Access.Permissions) + Required: Access.PermissionChoice +``` +`Permissions` and `PermissionChoice` are different, unrelated types outside `Access`. + + +## Opaque Type Members on Classes +While typically, opaque types are used together with objects to hide implementation details of a module, they can also be used with classes. + +For example, we can redefine the above example of Logarithms as a class. +```scala +class Logarithms: + + opaque type Logarithm = Double + + def apply(d: Double): Logarithm = math.log(d) + + def safe(d: Double): Option[Logarithm] = + if d > 0.0 then Some(math.log(d)) else None + + def mul(x: Logarithm, y: Logarithm) = x + y +``` + +Opaque type members of different instances are treated as different: +```scala +val l1 = new Logarithms +val l2 = new Logarithms +val x = l1(1.5) +val y = l1(2.6) +val z = l2(3.1) +l1.mul(x, y) // type checks +l1.mul(x, z) // error: found l2.Logarithm, required l1.Logarithm +``` +In general, one can think of an opaque type as being only transparent in the scope of `private[this]`. + +[More details](opaques-details.md) diff --git a/docs/_spec/TODOreference/other-new-features/open-classes.md b/docs/_spec/TODOreference/other-new-features/open-classes.md new file mode 100644 index 000000000000..764c234df599 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/open-classes.md @@ -0,0 +1,80 @@ +--- +layout: doc-page +title: "Open Classes" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/open-classes.html +--- + +An `open` modifier on a class signals that the class is planned for extensions. Example: +```scala +// File Writer.scala +package p + +open class Writer[T]: + + /** Sends to stdout, can be overridden */ + def send(x: T) = println(x) + + /** Sends all arguments using `send` */ + def sendAll(xs: T*) = xs.foreach(send) +end Writer + +// File EncryptedWriter.scala +package p + +class EncryptedWriter[T: Encryptable] extends Writer[T]: + override def send(x: T) = super.send(encrypt(x)) +``` +An open class typically comes with some documentation that describes +the internal calling patterns between methods of the class as well as hooks that can be overridden. We call this the _extension contract_ of the class. It is different from the _external contract_ between a class and its users. + +Classes that are not open can still be extended, but only if at least one of two alternative conditions is met: + + - The extending class is in the same source file as the extended class. In this case, the extension is usually an internal implementation matter. + + - The language feature [`adhocExtensions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$adhocExtensions$.html) is enabled for the extending class. This is typically enabled by an import clause in the source file of the extension: + ```scala + import scala.language.adhocExtensions + ``` + Alternatively, the feature can be enabled by the compiler option `-language:adhocExtensions`. + If the feature is not enabled, the compiler will issue a "feature" warning. For instance, if the `open` modifier on class `Writer` is dropped, compiling `EncryptedWriter` would produce a warning: + ``` + -- Feature Warning: EncryptedWriter.scala:6:14 ---- + |class EncryptedWriter[T: Encryptable] extends Writer[T] + | ^ + |Unless class Writer is declared 'open', its extension + | in a separate file should be enabled + |by adding the import clause 'import scala.language.adhocExtensions' + |or by setting the compiler option -language:adhocExtensions. + ``` + +## Motivation + +When writing a class, there are three possible expectations of extensibility: + +1. The class is intended to allow extensions. This means one should expect +a carefully worked out and documented extension contract for the class. + +2. Extensions of the class are forbidden, for instance to make correctness or security guarantees. + +3. There is no firm decision either way. The class is not _a priori_ intended for extensions, but if others find it useful to extend on an _ad-hoc_ basis, let them go ahead. However, they are on their own in this case. There is no documented extension contract, and future versions of the class might break the extensions (by rearranging internal call patterns, for instance). + +The three cases are clearly distinguished by using `open` for (1), `final` for (2) and no modifier for (3). + +It is good practice to avoid _ad-hoc_ extensions in a code base, since they tend to lead to fragile systems that are hard to evolve. But there +are still some situations where these extensions are useful: for instance, +to mock classes in tests, or to apply temporary patches that add features or fix bugs in library classes. That's why _ad-hoc_ extensions are permitted, but only if there is an explicit opt-in via a language feature import. + +## Details + + - `open` is a soft modifier. It is treated as a normal identifier + unless it is in modifier position. + - An `open` class cannot be `final` or `sealed`. + - Traits or `abstract` classes are always `open`, so `open` is redundant for them. + +## Relationship with `sealed` + +A class that is neither `abstract` nor `open` is similar to a `sealed` class: it can still be extended, but only in the same source file. The difference is what happens if an extension of the class is attempted in another source file. For a `sealed` class, this is an error, whereas for a simple non-open class, this is still permitted provided the [`adhocExtensions`](https://scala-lang.org/api/3.x/scala/runtime/stdLibPatches/language$$adhocExtensions$.html) feature is enabled, and it gives a warning otherwise. + +## Migration + +`open` is a new modifier in Scala 3. To allow cross compilation between Scala 2.13 and Scala 3.0 without warnings, the feature warning for ad-hoc extensions is produced only under `-source future`. It will be produced by default from Scala 3.1 on. diff --git a/docs/_spec/TODOreference/other-new-features/other-new-features.md b/docs/_spec/TODOreference/other-new-features/other-new-features.md new file mode 100644 index 000000000000..974a8548cb68 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/other-new-features.md @@ -0,0 +1,7 @@ +--- +layout: index +title: "Other New Features" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features.html +--- + +The following pages document new features of Scala 3. diff --git a/docs/_spec/TODOreference/other-new-features/parameter-untupling-spec.md b/docs/_spec/TODOreference/other-new-features/parameter-untupling-spec.md new file mode 100644 index 000000000000..e5165550fc0d --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/parameter-untupling-spec.md @@ -0,0 +1,89 @@ +--- +layout: doc-page +title: "Parameter Untupling - More Details" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/parameter-untupling-spec.html +--- + +## Motivation + +Say you have a list of pairs + +```scala +val xs: List[(Int, Int)] +``` + +and you want to map `xs` to a list of `Int`s so that each pair of numbers is mapped to their sum. +Previously, the best way to do this was with a pattern-matching decomposition: + +```scala +xs.map { + case (x, y) => x + y +} +``` +While correct, this is inconvenient. Instead, we propose to write it the following way: + +```scala +xs.map { + (x, y) => x + y +} +``` + +or, equivalently: + +```scala +xs.map(_ + _) +``` + +Generally, a function value with `n > 1` parameters can be converted to a function with tupled arguments if the expected type is a unary function type of the form `((T_1, ..., T_n)) => U`. + +## Type Checking + +The type checking happens in two steps: + +1. Check whether parameter untupling is feasible +2. Adapt the function and type check it + +### Feasibility Check + +Suppose a function `f` of the form `(p1, ..., pn) => e` (where `n > 1`), with `p1, ..., pn` as parameters and `e` as function body. + +If the expected type for checking `f` is a fully defined function type of the form `TupleN[T1, ..., Tn] => R` (or an equivalent SAM-type), where each type `Ti` fits the corresponding parameter `pi`. Then `f` is feasible for parameter untupling with the expected type `TupleN[T1, ..., Tn] => R`. + +A type `Ti` fits a parameter `pi` if one of the following two cases is `true`: + +* `pi` comes without a type, i.e. it is a simple identifier or `_`. +* `pi` is of the form `x: Ui` or `_: Ui` and `Ti <: Ui`. + +Parameter untupling composes with eta-expansion. That is, an n-ary function generated by eta-expansion can in turn be adapted to the expected type with parameter untupling. + +### Term adaptation + +If the function + +```scala +(p1, ..., pn) => e +``` + +is feasible for parameter untupling with the expected type `TupleN[T1, ..., Tn] => Te`, then continue to type check the following adapted function + +```scala +(x: TupleN[T1, ..., Tn]) => + def p1: T1 = x._1 + ... + def pn: Tn = x._n + e +``` + +with the same expected type. +## Migration + +Code like this could not be written before, hence the new notation is not ambiguous after adoption. + +It is possible that someone has written an implicit conversion from `(T1, ..., Tn) => R` to `TupleN[T1, ..., Tn] => R` for some `n`. +Such a conversion is now only useful for general conversions of function values, when parameter untupling is not applicable. +Some care is required to implement the conversion efficiently. +Obsolete conversions could be detected and fixed by [`Scalafix`](https://scalacenter.github.io/scalafix/). + +## Reference + +For more information, see [Issue #897](https://github.com/lampepfl/dotty/issues/897). diff --git a/docs/_spec/TODOreference/other-new-features/parameter-untupling.md b/docs/_spec/TODOreference/other-new-features/parameter-untupling.md new file mode 100644 index 000000000000..fcc1fa11d519 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/parameter-untupling.md @@ -0,0 +1,77 @@ +--- +layout: doc-page +title: "Parameter Untupling" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/parameter-untupling.html +--- + +Say you have a list of pairs + +```scala +val xs: List[(Int, Int)] +``` + +and you want to map `xs` to a list of `Int`s so that each pair of numbers is mapped to +their sum. Previously, the best way to do this was with a pattern-matching decomposition: + +```scala +xs map { + case (x, y) => x + y +} +``` + +While correct, this is also inconvenient and confusing, since the `case` +suggests that the pattern match could fail. As a shorter and clearer alternative Scala 3 now allows + +```scala +xs.map { + (x, y) => x + y +} +``` + +or, equivalently: + +```scala +xs.map(_ + _) +``` +and +```scala +def combine(i: Int, j: Int) = i + j +xs.map(combine) +``` + +Generally, a function value with `n > 1` parameters is wrapped in a +function type of the form `((T_1, ..., T_n)) => U` if that is the expected type. +The tuple parameter is decomposed and its elements are passed directly to the underlying function. + +More specifically, the adaptation is applied to the mismatching formal +parameter list. In particular, the adaptation is not a conversion +between function types. That is why the following is not accepted: + +```scala +val combiner: (Int, Int) => Int = _ + _ +xs.map(combiner) // Type Mismatch +``` + +The function value must be explicitly tupled, rather than the parameters untupled: +```scala +xs.map(combiner.tupled) +``` + +A conversion may be provided in user code: + +```scala +import scala.language.implicitConversions +transparent inline implicit def `fallback untupling`(f: (Int, Int) => Int): ((Int, Int)) => Int = + p => f(p._1, p._2) // use specialized apply instead of unspecialized `tupled` +xs.map(combiner) +``` + +Parameter untupling is attempted before conversions are applied, so that a conversion in scope +cannot subvert untupling. + +## Reference + +For more information see: + +* [More details](./parameter-untupling-spec.md) +* [Issue #897](https://github.com/lampepfl/dotty/issues/897). diff --git a/docs/_spec/TODOreference/other-new-features/safe-initialization.md b/docs/_spec/TODOreference/other-new-features/safe-initialization.md new file mode 100644 index 000000000000..757038eac786 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/safe-initialization.md @@ -0,0 +1,343 @@ +--- +layout: doc-page +title: "Safe Initialization" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/safe-initialization.html +--- + +Scala 3 implements experimental safe initialization check, which can be enabled by the compiler option `-Ysafe-init`. + +The design and implementation of the initialization checker is described in the +paper _Safe object initialization, abstractly_ [3]. + +## A Quick Glance + +To get a feel of how it works, we first show several examples below. + +### Parent-Child Interaction + +Given the following code snippet: + +``` scala +abstract class AbstractFile: + def name: String + val extension: String = name.substring(4) + +class RemoteFile(url: String) extends AbstractFile: + val localFile: String = s"${url.##}.tmp" // error: usage of `localFile` before it's initialized + def name: String = localFile +``` + +The checker will report: + +``` scala +-- Warning: tests/init/neg/AbstractFile.scala:7:4 ------------------------------ +7 | val localFile: String = s"${url.##}.tmp" // error: usage of `localFile` before it's initialized + | ^ + | Access non-initialized field value localFile. Calling trace: + | -> val extension: String = name.substring(4) [ AbstractFile.scala:3 ] + | -> def name: String = localFile [ AbstractFile.scala:8 ] +``` + +### Inner-Outer Interaction + +Given the code below: + +``` scala +object Trees: + class ValDef { counter += 1 } + class EmptyValDef extends ValDef + val theEmptyValDef = new EmptyValDef + private var counter = 0 // error +``` + +The checker will report: + +``` scala +-- Warning: tests/init/neg/trees.scala:5:14 ------------------------------------ +5 | private var counter = 0 // error + | ^ + | Access non-initialized field variable counter. Calling trace: + | -> val theEmptyValDef = new EmptyValDef [ trees.scala:4 ] + | -> class EmptyValDef extends ValDef [ trees.scala:3 ] + | -> class ValDef { counter += 1 } [ trees.scala:2 ] +``` + +### Functions + +Given the code below: + +``` scala +abstract class Parent: + val f: () => String = () => this.message + def message: String + +class Child extends Parent: + val a = f() + val b = "hello" // error + def message: String = b +``` + +The checker reports: + +``` scala +-- Warning: tests/init/neg/features-high-order.scala:7:6 ----------------------- +7 | val b = "hello" // error + | ^ + |Access non-initialized field value b. Calling trace: + | -> val a = f() [ features-high-order.scala:6 ] + | -> val f: () => String = () => this.message [ features-high-order.scala:2 ] + | -> def message: String = b [ features-high-order.scala:8 ] +``` +## Design Goals + +We establish the following design goals: + +- __Sound__: checking always terminates, and is sound for common and reasonable usage (over-approximation) +- __Expressive__: support common and reasonable initialization patterns +- __Friendly__: simple rules, minimal syntactic overhead, informative error messages +- __Modular__: modular checking, no analysis beyond project boundary +- __Fast__: instant feedback +- __Simple__: no changes to core type system, explainable by a simple theory + +By _reasonable usage_, we include the following use cases (but not restricted to them): + +- Access fields on `this` and outer `this` during initialization +- Call methods on `this` and outer `this` during initialization +- Instantiate inner class and call methods on such instances during initialization +- Capture fields in functions + +## Principles + +To achieve the goals, we uphold the following fundamental principles: +_stackability_, _monotonicity_, _scopability_ and _authority_. + +Stackability means that all fields of a class are initialized at the end of the +class body. Scala enforces this property in syntax by demanding that all fields +are initialized at the end of the primary constructor, except for the language +feature below: + +``` scala +var x: T = _ +``` + +Control effects such as exceptions may break this property, as the +following example shows: + +``` scala +class MyException(val b: B) extends Exception("") +class A: + val b = try { new B } catch { case myEx: MyException => myEx.b } + println(b.a) + +class B: + throw new MyException(this) + val a: Int = 1 +``` + +In the code above, the control effect teleport the uninitialized value +wrapped in an exception. In the implementation, we avoid the problem +by ensuring that the values that are thrown must be transitively initialized. + +Monotonicity means that the initialization status of an object should +not go backward: initialized fields continue to be initialized, a +field points to an initialized object may not later point to an +object under initialization. As an example, the following code will be rejected: + +``` scala +trait Reporter: + def report(msg: String): Unit + +class FileReporter(ctx: Context) extends Reporter: + ctx.typer.reporter = this // ctx now reaches an uninitialized object + val file: File = new File("report.txt") + def report(msg: String) = file.write(msg) +``` + +In the code above, suppose `ctx` points to a transitively initialized +object. Now the assignment at line 3 makes `this`, which is not fully +initialized, reachable from `ctx`. This makes field usage dangerous, +as it may indirectly reach uninitialized fields. + +Monotonicity is based on a well-known technique called _heap monotonic +typestate_ to ensure soundness in the presence of aliasing +[1]. Roughly speaking, it means initialization state should not go backwards. + +Scopability means that there are no side channels to access to partially +constructed objects. Control effects like coroutines, delimited +control, resumable exceptions may break the property, as they can transport a +value upper in the stack (not in scope) to be reachable from the current scope. +Static fields can also serve as a teleport thus breaks this property. In the +implementation, we need to enforce that teleported values are transitively +initialized. + +The three principles above contribute to _local reasoning about initialization_, +which means: + +> An initialized environment can only produce initialized values. + +For example, if the arguments to an `new`-expression are transitively +initialized, so is the result. If the receiver and arguments in a method call +are transitively initialized, so is the result. + +Local reasoning about initialization gives rise to a fast initialization +checker, as it avoids whole-program analysis. + +The principle of authority goes hand-in-hand with monotonicity: the principle +of monotonicity stipulates that initialization states cannot go backwards, while +the principle of authority stipulates that the initialization states may not +go forward at arbitrary locations due to aliasing. In Scala, we may only +advance initialization states of objects in the class body when a field is +defined with a mandatory initializer or at local reasoning points when the object +becomes transitively initialized. + +## Abstract Values + +There are three fundamental abstractions for initialization states of objects: + +- __Cold__: A cold object may have uninitialized fields. +- __Warm__: A warm object has all its fields initialized but may reach _cold_ objects. +- __Hot__: A hot object is transitively initialized, i.e., it only reaches warm objects. + +In the initialization checker, the abstraction `Warm` is refined to handle inner +classes and multiple constructors: + +- __Warm[C] { outer = V, ctor, args = Vs }__: A warm object of class `C`, where the immediate outer of `C` is `V`, the constructor is `ctor` and constructor arguments are `Vs`. + +The initialization checker checks each concrete class separately. The abstraction `ThisRef` +represents the current object under initialization: + +- __ThisRef[C]__: The current object of class `C` under initialization. + +The initialization state of the current object is stored in the abstract heap as an +abstract object. The abstract heap also serves as a cache for the field values +of warm objects. `Warm` and `ThisRef` are "addresses" of the abstract objects stored +in the abstract heap. + +Two more abstractions are introduced to support functions and conditional +expressions: + +- __Fun(e, V, C)__: An abstract function value where `e` is the code, `V` is the + abstract value for `this` inside the function body and the function is located + inside the class `C`. + +- __Refset(Vs)__: A set of abstract values `Vs`. + +A value `v` is _effectively hot_ if any of the following is true: + +- `v` is `Hot`. +- `v` is `ThisRef` and all fields of the underlying object are assigned. +- `v` is `Warm[C] { ... }` and + 1. `C` does not contain inner classes; and + 2. Calling any method on `v` encounters no initialization errors and the method return value is _effectively hot_; and + 3. Each field of `v` is _effectively hot_. +- `v` is `Fun(e, V, C)` and calling the function encounters no errors and the + function return value is _effectively hot_. +- The root object (refered by `ThisRef`) is _effectively hot_. + +An effectively hot value can be regarded as transitively initialized thus can +be safely leaked via method arguments or as RHS of reassignment. +The initialization checker tries to promote non-hot values to effectively hot +whenenver possible. + +## Rules + +With the established principles and design goals, the following rules are imposed: + +1. The field access `e.f` or method call `e.m()` is illegal if `e` is _cold_. + + A cold value should not be used. + +2. The field access `e.f` is invalid if `e` has the value `ThisRef` and `f` is not initialized. + +3. In an assignment `o.x = e`, the expression `e` must be _effectively hot_. + + This is how monotonicity is enforced in the system. Note that in an + initialization `val f: T = e`, the expression `e` may point to a non-hot + value. + +4. Arguments to method calls must be _effectively hot_. + + Escape of `this` in the constructor is commonly regarded as an anti-pattern. + + However, passing non-hot values as argument to another constructor is allowed, to support + creation of cyclic data structures. The checker will ensure that the escaped + non-initialized object is not used, i.e. calling methods or accessing fields + on the escaped object is not allowed. + + An exception is for calling synthetic `apply`s of case classes. For example, + the method call `Some.apply(e)` will be interpreted as `new Some(e)`, thus + is valid even if `e` is not hot. + + Another exception to this rule is parametric method calls. For example, in + `List.apply(e)`, the argument `e` may be non-hot. If that is the case, the + result value of the parametric method call is taken as _cold_. + +5. Method calls on hot values with effectively hot arguments produce hot results. + + This rule is assured by local reasoning about initialization. + +6. Method calls on `ThisRef` and warm values will be resolved statically and the + corresponding method bodies are checked. + +7. In a new expression `new p.C(args)`, if the values of `p` and `args` are + effectively hot, then the result value is also hot. + + This is assured by local reasoning about initialization. + +8. In a new expression `new p.C(args)`, if any value of `p` and `args` is not + effectively hot, then the result value takes the form `Warm[C] { outer = Vp, args = Vargs }`. The initialization code for the class `C` is checked again to make + sure the non-hot values are used properly. + + In the above, `Vp` is the widened value of `p` --- the widening happens if `p` + is a warm value `Warm[D] { outer = V, args }` and we widen it to + `Warm[D] { outer = Cold, args }`. + + The variable `Vargs` represents values of `args` with non-hot values widened + to `Cold`. + + The motivation for the widening is to finitize the abstract domain and ensure + termination of the initialization check. + +9. The scrutinee in a pattern match and the values in return and throw statements must be _effectively hot_. + +## Modularity + +The analysis takes the primary constructor of concrete classes as entry points. +It follows the constructors of super classes, which might be defined in another project. +The analysis takes advantage of TASTy for analyzing super classes defined in another project. + +The crossing of project boundary raises a concern about modularity. It is +well-known in object-oriented programming that superclass and subclass are +tightly coupled. For example, adding a method in the superclass requires +recompiling the child class for checking safe overriding. + +Initialization is no exception in this respect. The initialization of an object +essentially involves close interaction between subclass and superclass. If the +superclass is defined in another project, the crossing of project boundary +cannot be avoided for soundness of the analysis. + +Meanwhile, inheritance across project boundary has been under scrutiny and the +introduction of [open classes](./open-classes.md) mitigate the concern here. +For example, the initialization check could enforce that the constructors of +open classes may not contain method calls on `this` or introduce annotations as +a contract. + +The feedback from the community on the topic is welcome. + +## Back Doors + +Occasionally you may want to suppress warnings reported by the +checker. You can either write `e: @unchecked` to tell the checker to +skip checking for the expression `e`, or you may use the old trick: +mark some fields as lazy. + +## Caveats + +- The system cannot provide safety guarantee when extending Java or Scala 2 classes. +- Safe initialization of global objects is only partially checked. + +## References + +1. Fähndrich, M. and Leino, K.R.M., 2003, July. [_Heap monotonic typestates_](https://www.microsoft.com/en-us/research/publication/heap-monotonic-typestate/). In International Workshop on Aliasing, Confinement and Ownership in object-oriented programming (IWACO). +2. Fengyun Liu, Ondřej Lhoták, Aggelos Biboudis, Paolo G. Giarrusso, and Martin Odersky. [_A type-and-effect system for object initialization_](https://dl.acm.org/doi/10.1145/3428243). OOPSLA, 2020. +3. Fengyun Liu, Ondřej Lhoták, Enze Xing, Nguyen Cao Pham. [_Safe object initialization, abstractly_](https://dl.acm.org/doi/10.1145/3486610.3486895). Scala 2021. diff --git a/docs/_spec/TODOreference/other-new-features/targetName.md b/docs/_spec/TODOreference/other-new-features/targetName.md new file mode 100644 index 000000000000..63c4cf1ec0df --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/targetName.md @@ -0,0 +1,118 @@ +--- +layout: doc-page +title: "The @targetName annotation" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/targetName.html +--- + +A [`@targetName`](https://scala-lang.org/api/3.x/scala/annotation/targetName.html) annotation on a definition defines an alternate name for the implementation of that definition. Example: + +```scala +import scala.annotation.targetName + +object VecOps: + extension [T](xs: Vec[T]) + @targetName("append") + def ++= [T] (ys: Vec[T]): Vec[T] = ... +``` + +Here, the `++=` operation is implemented (in Byte code or native code) under the name `append`. The implementation name affects the code that is generated, and is the name under which code from other languages can call the method. For instance, `++=` could be invoked from Java like this: + +```java +VecOps.append(vec1, vec2) +``` + +The [`@targetName`](https://scala-lang.org/api/3.x/scala/annotation/targetName.html) annotation has no bearing on Scala usages. Any application of that method in Scala has to use `++=`, not `append`. + +## Details + + 1. `@targetName` is defined in package `scala.annotation`. It takes a single argument + of type `String`. That string is called the _external name_ of the definition + that's annotated. + + 2. A `@targetName` annotation can be given for all kinds of definitions except a top-level `class`, `trait`, or `object`. + + 3. The name given in a [`@targetName`](https://scala-lang.org/api/3.x/scala/annotation/targetName.html) annotation must be a legal name + for the defined entities on the host platform. + + 4. It is recommended that definitions with symbolic names have a [`@targetName`](https://scala-lang.org/api/3.x/scala/annotation/targetName.html) annotation. This will establish an alternate name that is easier to search for and + will avoid cryptic encodings in runtime diagnostics. + + 5. Definitions with names in backticks that are not legal host platform names + should also have a [`@targetName`](https://scala-lang.org/api/3.x/scala/annotation/targetName.html) annotation. + +## Relationship with Overriding + +[`@targetName`](https://scala-lang.org/api/3.x/scala/annotation/targetName.html) annotations are significant for matching two method definitions to decide whether they conflict or override each other. Two method definitions match if they have the same name, signature, and erased name. Here, + +- The _signature_ of a definition consists of the names of the erased types of all (value-) parameters and the method's result type. +- The _erased name_ of a method definition is its target name if a [`@targetName`](https://scala-lang.org/api/3.x/scala/annotation/targetName.html) annotation is given and its defined name otherwise. + +This means that `@targetName` annotations can be used to disambiguate two method definitions that would otherwise clash. For instance. + +```scala +def f(x: => String): Int = x.length +def f(x: => Int): Int = x + 1 // error: double definition +``` + +The two definitions above clash since their erased parameter types are both [`Function0`](https://scala-lang.org/api/3.x/scala/Function0.html), which is the type of the translation of a by-name-parameter. Hence they have the same names and signatures. But we can avoid the clash by adding a [`@targetName`](https://scala-lang.org/api/3.x/scala/annotation/targetName.html) annotation to either method or to both of them. Example: + +```scala +@targetName("f_string") +def f(x: => String): Int = x.length +def f(x: => Int): Int = x + 1 // OK +``` + +This will produce methods `f_string` and `f` in the generated code. + +However, [`@targetName`](https://scala-lang.org/api/3.x/scala/annotation/targetName.html) annotations are not allowed to break overriding relationships +between two definitions that have otherwise the same names and types. So the following would be in error: + +```scala +import annotation.targetName +class A: + def f(): Int = 1 +class B extends A: + @targetName("g") def f(): Int = 2 +``` + +The compiler reports here: + +``` +-- Error: test.scala:6:23 ------------------------------------------------------ +6 | @targetName("g") def f(): Int = 2 + | ^ + |error overriding method f in class A of type (): Int; + | method f of type (): Int should not have a @targetName + | annotation since the overridden member hasn't one either +``` + +The relevant overriding rules can be summarized as follows: + +- Two members can override each other if their names and signatures are the same, + and they either have the same erased names or the same types. +- If two members override, then both their erased names and their types must be the same. + +As usual, any overriding relationship in the generated code must also +be present in the original code. So the following example would also be in error: + +```scala +import annotation.targetName +class A: + def f(): Int = 1 +class B extends A: + @targetName("f") def g(): Int = 2 +``` + +Here, the original methods `g` and `f` do not override each other since they have +different names. But once we switch to target names, there is a clash that is reported by the compiler: + +``` +-- [E120] Naming Error: test.scala:4:6 ----------------------------------------- +4 |class B extends A: + | ^ + | Name clash between defined and inherited member: + | def f(): Int in class A at line 3 and + | def g(): Int in class B at line 5 + | have the same name and type after erasure. +1 error found +``` diff --git a/docs/_spec/TODOreference/other-new-features/threadUnsafe-annotation.md b/docs/_spec/TODOreference/other-new-features/threadUnsafe-annotation.md new file mode 100644 index 000000000000..ae1af1e4b671 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/threadUnsafe-annotation.md @@ -0,0 +1,18 @@ +--- +layout: doc-page +title: "The @threadUnsafe annotation" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/threadUnsafe-annotation.html +--- + +A new annotation [`@threadUnsafe`](https://scala-lang.org/api/3.x/scala/annotation/threadUnsafe.html) can be used on a field which defines +a `lazy val`. When this annotation is used, the initialization of the +[`lazy val`](../changed-features/lazy-vals-init.md) will use a faster mechanism which is not thread-safe. + +## Example + +```scala +import scala.annotation.threadUnsafe + +class Hello: + @threadUnsafe lazy val x: Int = 1 +``` diff --git a/docs/_spec/TODOreference/other-new-features/trait-parameters.md b/docs/_spec/TODOreference/other-new-features/trait-parameters.md new file mode 100644 index 000000000000..c704e73ce9b8 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/trait-parameters.md @@ -0,0 +1,88 @@ +--- +layout: doc-page +title: "Trait Parameters" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/trait-parameters.html +--- + +Scala 3 allows traits to have parameters, just like classes have parameters. + +```scala +trait Greeting(val name: String): + def msg = s"How are you, $name" + +class C extends Greeting("Bob"): + println(msg) +``` + +Arguments to a trait are evaluated immediately before the trait is initialized. + +One potential issue with trait parameters is how to prevent +ambiguities. For instance, you might try to extend `Greeting` twice, +with different parameters. + +```scala +class D extends C, Greeting("Bill") // error: parameter passed twice +``` + +Should this print "Bob" or "Bill"? In fact this program is illegal, +because it violates the second rule of the following for trait parameters: + + 1. If a class `C` extends a parameterized trait `T`, and its superclass does not, `C` _must_ pass arguments to `T`. + + 2. If a class `C` extends a parameterized trait `T`, and its superclass does as well, `C` _must not_ pass arguments to `T`. + + 3. Traits must never pass arguments to parent traits. + +Here's a trait extending the parameterized trait `Greeting`. + +```scala +trait FormalGreeting extends Greeting: + override def msg = s"How do you do, $name" +``` +As is required, no arguments are passed to `Greeting`. However, this poses an issue +when defining a class that extends `FormalGreeting`: + +```scala +class E extends FormalGreeting // error: missing arguments for `Greeting`. +``` + +The correct way to write `E` is to extend both `Greeting` and +`FormalGreeting` (in either order): + +```scala +class E extends Greeting("Bob"), FormalGreeting +``` + +## Traits With Context Parameters + +This "explicit extension required" rule is relaxed if the missing trait contains only +[context parameters](../contextual/using-clauses.md). In that case the trait reference is +implicitly inserted as an additional parent with inferred arguments. For instance, +here's a variant of greetings where the addressee is a context parameter of type +`ImpliedName`: + +```scala +case class ImpliedName(name: String): + override def toString = name + +trait ImpliedGreeting(using val iname: ImpliedName): + def msg = s"How are you, $iname" + +trait ImpliedFormalGreeting extends ImpliedGreeting: + override def msg = s"How do you do, $iname" + +class F(using iname: ImpliedName) extends ImpliedFormalGreeting +``` + +The definition of `F` in the last line is implicitly expanded to +```scala +class F(using iname: ImpliedName) extends + Object, + ImpliedGreeting(using iname), + ImpliedFormalGreeting(using iname) +``` +Note the inserted reference to the super trait `ImpliedGreeting`, which was not mentioned explicitly. + +## Reference + +For more information, see [Scala SIP 25](http://docs.scala-lang.org/sips/pending/trait-parameters.html). diff --git a/docs/_spec/TODOreference/other-new-features/transparent-traits.md b/docs/_spec/TODOreference/other-new-features/transparent-traits.md new file mode 100644 index 000000000000..699ce0b9ddd8 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/transparent-traits.md @@ -0,0 +1,70 @@ +--- +layout: doc-page +title: "Transparent Traits" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/transparent-traits.html +--- + +Traits are used in two roles: + + 1. As mixins for other classes and traits + 2. As types of vals, defs, or parameters + +Some traits are used primarily in the first role, and we usually do not want to see them in inferred types. An example is the [`Product`](https://scala-lang.org/api/3.x/scala/Product.html) trait that the compiler adds as a mixin trait to every case class or case object. In Scala 2, this parent trait sometimes makes inferred types more complicated than they should be. Example: + +```scala +trait Kind +case object Var extends Kind +case object Val extends Kind +val x = Set(if condition then Val else Var) +``` + +Here, the inferred type of `x` is `Set[Kind & Product & Serializable]` whereas one would have hoped it to be `Set[Kind]`. The reasoning for this particular type to be inferred is as follows: + +- The type of the conditional above is the [union type](../new-types/union-types.md) `Val | Var`. +- A union type is widened in type inference to the least supertype that is not a union type. + In the example, this type is `Kind & Product & Serializable` since all three traits are traits of both `Val` and `Var`. + So that type becomes the inferred element type of the set. + +Scala 3 allows one to mark a mixin trait as `transparent`, which means that it can be suppressed in type inference. Here's an example that follows the lines of the code above, but now with a new transparent trait `S` instead of `Product`: + +```scala +transparent trait S +trait Kind +object Var extends Kind, S +object Val extends Kind, S +val x = Set(if condition then Val else Var) +``` + +Now `x` has inferred type `Set[Kind]`. The common transparent trait `S` does not +appear in the inferred type. + +## Transparent Traits + +The traits [`scala.Product`](https://scala-lang.org/api/3.x/scala/Product.html), [`java.io.Serializable`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/Serializable.html) and [`java.lang.Comparable`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Comparable.html) +are treated automatically as transparent. Other traits are turned into transparent traits using the modifier `transparent`. Scala 2 traits can also be made transparent +by adding a [`@transparentTrait`](https://scala-lang.org/api/3.x/scala/annotation/transparentTrait.html) annotation. This annotation is defined in [`scala.annotation`](https://scala-lang.org/api/3.x/scala/annotation.html). It will be deprecated and phased out once Scala 2/3 interoperability is no longer needed. + +Typically, transparent traits are traits +that influence the implementation of inheriting classes and traits that are not usually used as types by themselves. Two examples from the standard collection library are: + +- [`IterableOps`](https://scala-lang.org/api/3.x/scala/collection/IterableOps.html), which provides method implementations for an [`Iterable`](https://scala-lang.org/api/3.x/scala/collection/Iterable.html). +- [`StrictOptimizedSeqOps`](https://scala-lang.org/api/3.x/scala/collection/StrictOptimizedSeqOps.html), which optimises some of these implementations for sequences with efficient indexing. + +Generally, any trait that is extended recursively is a good candidate to be +declared transparent. + +## Rules for Inference + +Transparent traits can be given as explicit types as usual. But they are often elided when types are inferred. Roughly, the rules for type inference say that transparent traits are dropped from intersections where possible. + +The precise rules are as follows: + +- When inferring a type of a type variable, or the type of a val, or the return type of a def, +- where that type is not higher-kinded, +- and where `B` is its known upper bound or `Any` if none exists: +- If the type inferred so far is of the form `T1 & ... & Tn` where + `n >= 1`, replace the maximal number of transparent `Ti`s by `Any`, while ensuring that + the resulting type is still a subtype of the bound `B`. +- However, do not perform this widening if all transparent traits `Ti` can get replaced in that way. + +The last clause ensures that a single transparent trait instance such as [`Product`](https://scala-lang.org/api/3.x/scala/Product.html) is not widened to [`Any`](https://scala-lang.org/api/3.x/scala/Any.html). Transparent trait instances are only dropped when they appear in conjunction with some other type. diff --git a/docs/_spec/TODOreference/other-new-features/type-test.md b/docs/_spec/TODOreference/other-new-features/type-test.md new file mode 100644 index 000000000000..ec7a87230753 --- /dev/null +++ b/docs/_spec/TODOreference/other-new-features/type-test.md @@ -0,0 +1,181 @@ +--- +layout: doc-page +title: "TypeTest" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/type-test.html +--- + +## TypeTest + +When pattern matching there are two situations where a runtime type test must be performed. +The first case is an explicit type test using the ascription pattern notation. + +```scala +(x: X) match + case y: Y => +``` + +The second case is when an extractor takes an argument that is not a subtype of the scrutinee type. + +```scala +(x: X) match + case y @ Y(n) => + +object Y: + def unapply(x: Y): Some[Int] = ... +``` + +In both cases, a class test will be performed at runtime. +But when the type test is on an abstract type (type parameter or type member), the test cannot be performed because the type is erased at runtime. + +A [`TypeTest`](https://scala-lang.org/api/3.x/scala/reflect/TypeTest.html) can be provided to make this test possible. + +```scala +package scala.reflect + +trait TypeTest[-S, T]: + def unapply(s: S): Option[s.type & T] +``` + +It provides an extractor that returns its argument typed as a `T` if the argument is a `T`. +It can be used to encode a type test. + +```scala +def f[X, Y](x: X)(using tt: TypeTest[X, Y]): Option[Y] = x match + case tt(x @ Y(1)) => Some(x) + case tt(x) => Some(x) + case _ => None +``` + +To avoid the syntactic overhead the compiler will look for a type test automatically if it detects that the type test is on abstract types. +This means that `x: Y` is transformed to `tt(x)` and `x @ Y(_)` to `tt(x @ Y(_))` if there is a contextual `TypeTest[X, Y]` in scope. +The previous code is equivalent to + +```scala +def f[X, Y](x: X)(using TypeTest[X, Y]): Option[Y] = x match + case x @ Y(1) => Some(x) + case x: Y => Some(x) + case _ => None +``` + +We could create a type test at call site where the type test can be performed with runtime class tests directly as follows + +```scala +val tt: TypeTest[Any, String] = + new TypeTest[Any, String]: + def unapply(s: Any): Option[s.type & String] = s match + case s: String => Some(s) + case _ => None + +f[AnyRef, String]("acb")(using tt) +``` + +The compiler will synthesize a new instance of a type test if none is found in scope as: + +```scala +new TypeTest[A, B]: + def unapply(s: A): Option[s.type & B] = s match + case s: B => Some(s) + case _ => None +``` + +If the type tests cannot be done there will be an unchecked warning that will be raised on the `case s: B =>` test. + +The most common [`TypeTest`](https://scala-lang.org/api/3.x/scala/reflect/TypeTest.html) instances are the ones that take any parameters (i.e. `TypeTest[Any, T]`). +To make it possible to use such instances directly in context bounds we provide the alias + +```scala +package scala.reflect + +type Typeable[T] = TypeTest[Any, T] +``` + +This alias can be used as + +```scala +def f[T: Typeable]: Boolean = + "abc" match + case x: T => true + case _ => false + +f[String] // true +f[Int] // false +``` + +## TypeTest and ClassTag + +[`TypeTest`](https://scala-lang.org/api/3.x/scala/reflect/TypeTest.html) is a replacement for functionality provided previously by `ClassTag.unapply`. +Using [`ClassTag`](https://scala-lang.org/api/3.x/scala/reflect/ClassTag.html) instances was unsound since classtags can check only the class component of a type. +[`TypeTest`](https://scala-lang.org/api/3.x/scala/reflect/TypeTest.html) fixes that unsoundness. +[`ClassTag`](https://scala-lang.org/api/3.x/scala/reflect/ClassTag.html) type tests are still supported but a warning will be emitted after 3.0. + + +## Example + +Given the following abstract definition of Peano numbers that provides two given instances of types `TypeTest[Nat, Zero]` and `TypeTest[Nat, Succ]` + +```scala +import scala.reflect.* + +trait Peano: + type Nat + type Zero <: Nat + type Succ <: Nat + + def safeDiv(m: Nat, n: Succ): (Nat, Nat) + + val Zero: Zero + + val Succ: SuccExtractor + trait SuccExtractor: + def apply(nat: Nat): Succ + def unapply(succ: Succ): Some[Nat] + + given typeTestOfZero: TypeTest[Nat, Zero] + given typeTestOfSucc: TypeTest[Nat, Succ] +``` + +together with an implementation of Peano numbers based on type `Int` + +```scala +object PeanoInt extends Peano: + type Nat = Int + type Zero = Int + type Succ = Int + + def safeDiv(m: Nat, n: Succ): (Nat, Nat) = (m / n, m % n) + + val Zero: Zero = 0 + + val Succ: SuccExtractor = new: + def apply(nat: Nat): Succ = nat + 1 + def unapply(succ: Succ) = Some(succ - 1) + + def typeTestOfZero: TypeTest[Nat, Zero] = new: + def unapply(x: Nat): Option[x.type & Zero] = + if x == 0 then Some(x) else None + + def typeTestOfSucc: TypeTest[Nat, Succ] = new: + def unapply(x: Nat): Option[x.type & Succ] = + if x > 0 then Some(x) else None +``` + +it is possible to write the following program + +```scala +@main def test = + import PeanoInt.* + + def divOpt(m: Nat, n: Nat): Option[(Nat, Nat)] = + n match + case Zero => None + case s @ Succ(_) => Some(safeDiv(m, s)) + + val two = Succ(Succ(Zero)) + val five = Succ(Succ(Succ(two))) + + println(divOpt(five, two)) // prints "Some((2,1))" + println(divOpt(two, five)) // prints "Some((0,2))" + println(divOpt(two, Zero)) // prints "None" +``` + +Note that without the `TypeTest[Nat, Succ]` the pattern `Succ.unapply(nat: Succ)` would be unchecked. diff --git a/docs/_spec/TODOreference/overview.md b/docs/_spec/TODOreference/overview.md new file mode 100644 index 000000000000..b1e8281dfc16 --- /dev/null +++ b/docs/_spec/TODOreference/overview.md @@ -0,0 +1,155 @@ +--- +layout: doc-page +title: "Reference" +nightlyOf: https://docs.scala-lang.org/scala3/reference/overview.html +redirectFrom: overview.html +--- + +Scala 3 implements many language changes and improvements over Scala 2. +In this reference, we discuss design decisions and present important differences compared to Scala 2. + +## Goals + +The language redesign was guided by three main goals: + +- Strengthen Scala's foundations. + Make the full programming language compatible with the foundational work on the + [DOT calculus](https://infoscience.epfl.ch/record/227176/files/soundness_oopsla16.pdf) + and apply the lessons learned from that work. +- Make Scala easier and safer to use. + Tame powerful constructs such as implicits to provide a gentler learning curve. Remove warts and puzzlers. +- Further improve the consistency and expressiveness of Scala's language constructs. + +Corresponding to these goals, the language changes fall into seven categories: +(1) Core constructs to strengthen foundations, (2) simplifications and (3) [restrictions](#restrictions), to make the language easier and safer to use, (4) [dropped constructs](#dropped-constructs) to make the language smaller and more regular, (5) [changed constructs](#changes) to remove warts, and increase consistency and usability, (6) [new constructs](#new-constructs) to fill gaps and increase expressiveness, (7) a new, principled approach to metaprogramming that replaces [Scala 2 experimental macros](https://docs.scala-lang.org/overviews/macros/overview.html). + +## Essential Foundations + +These new constructs directly model core features of DOT, higher-kinded types, and the [SI calculus for implicit resolution](https://infoscience.epfl.ch/record/229878/files/simplicitly_1.pdf). + +- [Intersection types](new-types/intersection-types.md), replacing compound types, +- [Union types](new-types/union-types.md), +- [Type lambdas](new-types/type-lambdas.md), replacing encodings using structural types and type projection. +- [Context functions](contextual/context-functions.md), offering abstraction over given parameters. + +## Simplifications + +These constructs replace existing constructs with the aim of making the language safer and simpler to use, and to promote uniformity in code style. + +- [Trait parameters](other-new-features/trait-parameters.md) + replace [early initializers](dropped-features/early-initializers.md) with a more generally useful construct. +- [Given instances](contextual/givens.md) + replace implicit objects and defs, focussing on intent over mechanism. +- [Using clauses](contextual/using-clauses.md) + replace implicit parameters, avoiding their ambiguities. +- [Extension methods](contextual/extension-methods.md) + replace implicit classes with a clearer and simpler mechanism. +- [Opaque type aliases](other-new-features/opaques.md) + replace most uses of value classes while guaranteeing the absence of boxing. +- [Top-level definitions](dropped-features/package-objects.md) + replace package objects, dropping syntactic boilerplate. +- [Export clauses](other-new-features/export.md) + provide a simple and general way to express aggregation, which can replace + the previous facade pattern of package objects inheriting from classes. +- [Vararg splices](changed-features/vararg-splices.md) + now use the form `xs*` in function arguments and patterns instead of `xs: _*` and `xs @ _*`, +- [Universal apply methods](other-new-features/creator-applications.md) + allow using simple function call syntax instead of `new` expressions. `new` expressions stay around + as a fallback for the cases where creator applications cannot be used. + +With the exception of [early initializers](dropped-features/early-initializers.md) and old-style vararg patterns, all superseded constructs continue to be available in Scala 3.0. The plan is to deprecate and phase them out later. + +Value classes (superseded by opaque type aliases) are a special case. There are currently no deprecation plans for value classes, since we might bring them back in a more general form if they are supported natively by the JVM as is planned by [project Valhalla](https://openjdk.java.net/projects/valhalla/). + +## Restrictions + +These constructs are restricted to make the language safer. + +- [Implicit Conversions](contextual/conversions.md): + there is only one way to define implicit conversions instead of many, and potentially surprising implicit conversions require a language import. +- [Given Imports](contextual/given-imports.md): + implicits now require a special form of import, to make the import clearly visible. +- [Type Projection](dropped-features/type-projection.md): + only classes can be used as prefix `C` of a type projection `C#A`. Type projection on abstract types is no longer supported since it is unsound. +- [Multiversal Equality](contextual/multiversal-equality.md): + implement an "opt-in" scheme to rule out nonsensical comparisons with `==` and `!=`. +- [infix](changed-features/operators.md): + make method application syntax uniform across code bases. + +Unrestricted implicit conversions continue to be available in Scala 3.0, but will be deprecated and removed later. Unrestricted versions of the other constructs in the list above are available only under `-source 3.0-migration`. + +## Dropped Constructs + +These constructs are proposed to be dropped without a new construct replacing them. The motivation for dropping these constructs is to simplify the language and its implementation. + +- [DelayedInit](dropped-features/delayed-init.md), +- [Existential types](dropped-features/existential-types.md), +- [Procedure syntax](dropped-features/procedure-syntax.md), +- [Class shadowing](dropped-features/class-shadowing.md), +- [XML literals](dropped-features/xml.md), +- [Symbol literals](dropped-features/symlits.md), +- [Auto application](dropped-features/auto-apply.md), +- [Weak conformance](dropped-features/weak-conformance.md), +- Compound types (replaced by [Intersection types](new-types/intersection-types.md)), +- [Auto tupling](https://github.com/lampepfl/dotty/pull/4311) (implemented, but not merged). + +The date when these constructs are dropped varies. The current status is: + +- Not implemented at all: + - DelayedInit, existential types, weak conformance. +- Supported under `-source 3.0-migration`: + - procedure syntax, class shadowing, symbol literals, auto application, auto tupling in a restricted form. +- Supported in 3.0, to be deprecated and phased out later: + - [XML literals](dropped-features/xml.md), compound types. + +## Changes + +These constructs have undergone changes to make them more regular and useful. + +- [Structural Types](changed-features/structural-types.md): + They now allow pluggable implementations, which greatly increases their usefulness. Some usage patterns are restricted compared to the status quo. +- [Name-based pattern matching](changed-features/pattern-matching.md): + The existing undocumented Scala 2 implementation has been codified in a slightly simplified form. +- [Automatic Eta expansion](changed-features/eta-expansion.md): + Eta expansion is now performed universally also in the absence of an expected type. The postfix `_` operator is thus made redundant. It will be deprecated and dropped after Scala 3.0. +- [Implicit Resolution](changed-features/implicit-resolution.md): + The implicit resolution rules have been cleaned up to make them more useful and less surprising. Implicit scope is restricted to no longer include package prefixes. + +Most aspects of old-style implicit resolution are still available under `-source 3.0-migration`. The other changes in this list are applied unconditionally. + +## New Constructs + +These are additions to the language that make it more powerful or pleasant to use. + +- [Enums](enums/enums.md) provide concise syntax for enumerations and [algebraic data types](enums/adts.md). +- [Parameter untupling](other-new-features/parameter-untupling.md) avoids having to use `case` for tupled parameter destructuring. +- [Dependent function types](new-types/dependent-function-types.md) generalize dependent methods to dependent function values and types. +- [Polymorphic function types](new-types/polymorphic-function-types.md) generalize polymorphic methods to polymorphic function values and types. + _Current status_: There is a proposal and a merged prototype implementation, but the implementation has not been finalized (it is notably missing type inference support). +- [Kind polymorphism](other-new-features/kind-polymorphism.md) allows the definition of operators working equally on types and type constructors. +- [`@targetName` annotations](other-new-features/targetName.md) make it easier to interoperate with code written in other languages and give more flexibility for avoiding name clashes. + +## Metaprogramming + +The following constructs together aim to put metaprogramming in Scala on a new basis. So far, metaprogramming was achieved by a combination of macros and libraries such as [Shapeless](https://github.com/milessabin/shapeless) that were in turn based on some key macros. Current Scala 2 macro mechanisms are a thin veneer on top of the current Scala 2 compiler, which makes them fragile and in many cases impossible to port to Scala 3. + +It's worth noting that macros were never included in the [Scala 2 language specification](https://scala-lang.org/files/archive/spec/2.13/) and were so far made available only under an `-experimental` flag. This has not prevented their widespread usage. + +To enable porting most uses of macros, we are experimenting with the advanced language constructs listed below. These designs are more provisional than the rest of the proposed language constructs for Scala 3.0. There might still be some changes until the final release. Stabilizing the feature set needed for metaprogramming is our first priority. + +- [Match Types](new-types/match-types.md) + allow computation on types. +- [Inline](metaprogramming/inline.md) + provides by itself a straightforward implementation of some simple macros and is at the same time an essential building block for the implementation of complex macros. +- [Quotes and Splices](metaprogramming/macros.md) + provide a principled way to express macros and staging with a unified set of abstractions. +- [Type class derivation](contextual/derivation.md) + provides an in-language implementation of the `Gen` macro in Shapeless and other foundational libraries. The new implementation is more robust, efficient and easier to use than the macro. +- [By-name context parameters](contextual/by-name-context-parameters.md) + provide a more robust in-language implementation of the `Lazy` macro in [Shapeless](https://github.com/milessabin/shapeless). + +## See Also + +[A classification of proposed language features](./features-classification.md) is +an expanded version of this page that adds the status (i.e. relative importance to be a part of Scala 3, and relative urgency when to decide this) and expected migration cost +of each language construct. diff --git a/docs/_spec/TODOreference/soft-modifier.md b/docs/_spec/TODOreference/soft-modifier.md new file mode 100644 index 000000000000..c1329ebab1f0 --- /dev/null +++ b/docs/_spec/TODOreference/soft-modifier.md @@ -0,0 +1,27 @@ +--- +layout: doc-page +title: "Soft Keywords" +nightlyOf: https://docs.scala-lang.org/scala3/reference/soft-modifier.html +--- + +A soft modifier is one of the identifiers `infix`, `inline`, `opaque`, `open` and `transparent`. + +A soft keyword is a soft modifier, or one of `as`, `derives`, `end`, `extension`, `throws`, `using`, `|`, `+`, `-`, `*` + +A soft modifier is treated as potential modifier of a definition if it is followed by a hard modifier or a keyword combination starting a definition (`def`, `val`, `var`, `type`, `given`, `class`, `trait`, `object`, `enum`, `case class`, `case object`). Between the two words there may be a sequence of newline tokens and soft modifiers. + +Otherwise, soft keywords are treated specially in the following situations: + + - `inline`, if it is followed by any token that can start an expression. + - `derives`, if it appears after an extension clause or after + the name and possibly parameters of a class, trait, object, or enum definition. + - `end`, if it appears at the start of a line following a statement (i.e. definition or toplevel expression) + - `extension`, if it appears at the start of a statement and is followed by `(` or `[`. + - `using`, if it appears at the start of a parameter or argument list. + - `as`, in a renaming import clause + - `|`, if it separates two patterns in an alternative. + - `+`, `-`, if they appear in front of a type parameter. + - `*`, in a wildcard import, or it follows the type of a parameter, or if it appears in + a vararg splice `x*`. + +Everywhere else a soft keyword is treated as a normal identifier. diff --git a/docs/_spec/TODOreference/syntax.md b/docs/_spec/TODOreference/syntax.md new file mode 100644 index 000000000000..e11629c8eaf9 --- /dev/null +++ b/docs/_spec/TODOreference/syntax.md @@ -0,0 +1,472 @@ +--- +layout: doc-page +title: "Scala 3 Syntax Summary" +nightlyOf: https://docs.scala-lang.org/scala3/reference/syntax.html +--- + + + +The following description of Scala tokens uses literal characters `‘c’` when +referring to the ASCII fragment `\u0000` – `\u007F`. + +_Unicode escapes_ are used to represent the [Unicode character](https://www.w3.org/International/articles/definitions-characters/) with the given +hexadecimal code: + +``` +UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit +hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ +``` + +Informal descriptions are typeset as `“some comment”`. + +## Lexical Syntax + +The lexical syntax of Scala is given by the following grammar in EBNF +form. + +``` +whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ +upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and Unicode category Lu” +lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll” +letter ::= upper | lower “… and Unicode categories Lo, Lt, Nl” +digit ::= ‘0’ | … | ‘9’ +paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ +delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ +opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | + ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ + “… and Unicode categories Sm, So” +printableChar ::= “all characters in [\u0020, \u007E] inclusive” +charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) + +op ::= opchar {opchar} +varid ::= lower idrest +alphaid ::= upper idrest + | varid +plainid ::= alphaid + | op +id ::= plainid + | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’ +idrest ::= {letter | digit} [‘_’ op] +quoteId ::= ‘'’ alphaid +spliceId ::= ‘$’ alphaid ; + +integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] +decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] +hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] +nonZeroDigit ::= ‘1’ | … | ‘9’ + +floatingPointLiteral + ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] + | decimalNumeral exponentPart [floatType] + | decimalNumeral floatType +exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit [{digit | ‘_’} digit] +floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ + +booleanLiteral ::= ‘true’ | ‘false’ + +characterLiteral ::= ‘'’ (printableChar | charEscapeSeq) ‘'’ + +stringLiteral ::= ‘"’ {stringElement} ‘"’ + | ‘"""’ multiLineChars ‘"""’ +stringElement ::= printableChar \ (‘"’ | ‘\’) + | UnicodeEscape + | charEscapeSeq +multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} +processedStringLiteral + ::= alphaid ‘"’ {[‘\’] processedStringPart | ‘\\’ | ‘\"’} ‘"’ + | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ +processedStringPart + ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape +escape ::= ‘$$’ + | ‘$’ letter { letter | digit } + | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ +stringFormat ::= {printableChar \ (‘"’ | ‘}’ | ‘ ’ | ‘\t’ | ‘\n’)} + +symbolLiteral ::= ‘'’ plainid // until 2.13 + +comment ::= ‘/*’ “any sequence of characters; nested comments are allowed” ‘*/’ + | ‘//’ “any sequence of characters up to end of line” + +nl ::= “new line character” +semi ::= ‘;’ | nl {nl} +``` + +## Optional Braces + +The lexical analyzer also inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](./other-new-features/indentation.md). + +In the context-free productions below we use the notation `<<< ts >>>` +to indicate a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent`. Analogously, the +notation `:<<< ts >>>` indicates a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent` that follows +a `colon` token. + +A `colon` token reads as the standard colon "`:`" but is generated instead of it where `colon` is legal according to the context free syntax, but only if the previous token +is an alphanumeric identifier, a backticked identifier, or one of the tokens `this`, `super`, `new`, "`)`", and "`]`". + +``` +colon ::= ':' -- with side conditions explained above + <<< ts >>> ::= ‘{’ ts ‘}’ + | indent ts outdent +:<<< ts >>> ::= [nl] ‘{’ ts ‘}’ + | colon indent ts outdent +``` + +## Keywords + +### Regular keywords + +``` +abstract case catch class def do else +enum export extends false final finally for +given if implicit import lazy match new +null object override package private protected return +sealed super then throw trait true try +type val var while with yield +: = <- => <: >: # +@ =>> ?=> +``` + +### Soft keywords + +``` +as derives end extension infix inline opaque open transparent using | * + - +``` + +See the [separate section on soft keywords](./soft-modifier.md) for additional +details on where a soft keyword is recognized. + +## Context-free Syntax + +The context-free syntax of Scala is given by the following EBNF +grammar: + +### Literals and Paths +``` +SimpleLiteral ::= [‘-’] integerLiteral + | [‘-’] floatingPointLiteral + | booleanLiteral + | characterLiteral + | stringLiteral +Literal ::= SimpleLiteral + | processedStringLiteral + | symbolLiteral + | ‘null’ + +QualId ::= id {‘.’ id} +ids ::= id {‘,’ id} + +SimpleRef ::= id + | [id ‘.’] ‘this’ + | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id + +ClassQualifier ::= ‘[’ id ‘]’ +``` + +### Types +``` +Type ::= FunType + | HkTypeParamClause ‘=>>’ Type + | FunParamClause ‘=>>’ Type + | MatchType + | InfixType +FunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type + | HKTypeParamClause '=>' Type +FunTypeArgs ::= InfixType + | ‘(’ [ FunArgTypes ] ‘)’ + | FunParamClause +FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ +TypedFunParam ::= id ‘:’ Type +MatchType ::= InfixType `match` <<< TypeCaseClauses >>> +InfixType ::= RefinedType {id [nl] RefinedType} +RefinedType ::= AnnotType {[nl] Refinement} +AnnotType ::= SimpleType {Annotation} + +SimpleType ::= SimpleLiteral + | ‘?’ TypeBounds + | id + | Singleton ‘.’ id + | Singleton ‘.’ ‘type’ + | ‘(’ Types ‘)’ + | Refinement + | SimpleType1 TypeArgs + | SimpleType1 ‘#’ id +Singleton ::= SimpleRef + | SimpleLiteral + | Singleton ‘.’ id + +FunArgType ::= Type + | ‘=>’ Type +FunArgTypes ::= FunArgType { ‘,’ FunArgType } +ParamType ::= [‘=>’] ParamValueType +ParamValueType ::= Type [‘*’] +TypeArgs ::= ‘[’ Types ‘]’ +Refinement ::= :<<< [RefineDcl] {semi [RefineDcl]} >>> +TypeBounds ::= [‘>:’ Type] [‘<:’ Type] +TypeParamBounds ::= TypeBounds {‘:’ Type} +Types ::= Type {‘,’ Type} +``` + +### Expressions +``` +Expr ::= FunParams (‘=>’ | ‘?=>’) Expr + | HkTypeParamClause ‘=>’ Expr + | Expr1 +BlockResult ::= FunParams (‘=>’ | ‘?=>’) Block + | HkTypeParamClause ‘=>’ Block + | Expr1 +FunParams ::= Bindings + | id + | ‘_’ +Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] + | [‘inline’] ‘if’ Expr ‘then’ Expr [[semi] ‘else’ Expr] + | ‘while’ ‘(’ Expr ‘)’ {nl} Expr + | ‘while’ Expr ‘do’ Expr + | ‘try’ Expr Catches [‘finally’ Expr] + | ‘try’ Expr [‘finally’ Expr] + | ‘throw’ Expr + | ‘return’ [Expr] + | ForExpr + | [SimpleExpr ‘.’] id ‘=’ Expr + | PrefixOperator SimpleExpr ‘=’ Expr + | SimpleExpr ArgumentExprs ‘=’ Expr + | PostfixExpr [Ascription] + | ‘inline’ InfixExpr MatchClause +Ascription ::= ‘:’ InfixType + | ‘:’ Annotation {Annotation} +Catches ::= ‘catch’ (Expr | ExprCaseClause) +PostfixExpr ::= InfixExpr [id] -- only if language.postfixOperators is enabled +InfixExpr ::= PrefixExpr + | InfixExpr id [nl] InfixExpr + | InfixExpr MatchClause +MatchClause ::= ‘match’ <<< CaseClauses >>> +PrefixExpr ::= [PrefixOperator] SimpleExpr +PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ -- unless backquoted +SimpleExpr ::= SimpleRef + | Literal + | ‘_’ + | BlockExpr + | ExprSplice + | Quoted + | quoteId -- only inside splices + | ‘new’ ConstrApp {‘with’ ConstrApp} [TemplateBody] + | ‘new’ TemplateBody + | ‘(’ ExprsInParens ‘)’ + | SimpleExpr ‘.’ id + | SimpleExpr ‘.’ MatchClause + | SimpleExpr TypeArgs + | SimpleExpr ArgumentExprs +Quoted ::= ‘'’ ‘{’ Block ‘}’ + | ‘'’ ‘[’ Type ‘]’ +ExprSplice ::= spliceId -- if inside quoted block + | ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern + | ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern +ExprsInParens ::= ExprInParens {‘,’ ExprInParens} +ExprInParens ::= PostfixExpr ‘:’ Type + | Expr +ParArgumentExprs ::= ‘(’ [ExprsInParens] ‘)’ + | ‘(’ ‘using’ ExprsInParens ‘)’ + | ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ ‘)’ +ArgumentExprs ::= ParArgumentExprs + | BlockExpr +BlockExpr ::= <<< (CaseClauses | Block) >>> +Block ::= {BlockStat semi} [BlockResult] +BlockStat ::= Import + | {Annotation {nl}} {LocalModifier} Def + | Extension + | Expr1 + | EndMarker + +ForExpr ::= ‘for’ ‘(’ Enumerators0 ‘)’ {nl} [‘do‘ | ‘yield’] Expr + | ‘for’ ‘{’ Enumerators0 ‘}’ {nl} [‘do‘ | ‘yield’] Expr + | ‘for’ Enumerators0 (‘do‘ | ‘yield’) Expr +Enumerators0 ::= {nl} Enumerators [semi] +Enumerators ::= Generator {semi Enumerator | Guard} +Enumerator ::= Generator + | Guard {Guard} + | Pattern1 ‘=’ Expr +Generator ::= [‘case’] Pattern1 ‘<-’ Expr +Guard ::= ‘if’ PostfixExpr + +CaseClauses ::= CaseClause { CaseClause } +CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block +ExprCaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Expr +TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } +TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] + +Pattern ::= Pattern1 { ‘|’ Pattern1 } +Pattern1 ::= Pattern2 [‘:’ RefinedType] +Pattern2 ::= [id ‘@’] InfixPattern [‘*’] +InfixPattern ::= SimplePattern { id [nl] SimplePattern } +SimplePattern ::= PatVar + | Literal + | ‘(’ [Patterns] ‘)’ + | Quoted + | SimplePattern1 [TypeArgs] [ArgumentPatterns] + | ‘given’ RefinedType +SimplePattern1 ::= SimpleRef + | SimplePattern1 ‘.’ id +PatVar ::= varid + | ‘_’ +Patterns ::= Pattern {‘,’ Pattern} +ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ + | ‘(’ [Patterns ‘,’] PatVar ‘*’ ‘)’ +``` + +### Type and Value Parameters +``` +ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ +ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] id [HkTypeParamClause] TypeParamBounds + +DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ +DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds + +TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ +TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds + +HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’ +HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (id [HkTypeParamClause] | ‘_’) TypeBounds + +ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] +ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ + | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ +ClsParams ::= ClsParam {‘,’ ClsParam} +ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param +Param ::= id ‘:’ ParamType [‘=’ Expr] + +DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] +DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause +UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’ +DefParams ::= DefParam {‘,’ DefParam} +DefParam ::= {Annotation} [‘inline’] Param +``` + +### Bindings and Imports +``` +Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’ +Binding ::= (id | ‘_’) [‘:’ Type] + +Modifier ::= LocalModifier + | AccessModifier + | ‘override’ + | ‘opaque’ +LocalModifier ::= ‘abstract’ + | ‘final’ + | ‘sealed’ + | ‘open’ + | ‘implicit’ + | ‘lazy’ + | ‘inline’ +AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] +AccessQualifier ::= ‘[’ id ‘]’ + +Annotation ::= ‘@’ SimpleType1 {ParArgumentExprs} + +Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} +Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} +ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec + | SimpleRef ‘as’ id +ImportSpec ::= NamedSelector + | WildcardSelector + | ‘{’ ImportSelectors) ‘}’ +NamedSelector ::= id [‘as’ (id | ‘_’)] +WildCardSelector ::= ‘*' | ‘given’ [InfixType] +ImportSelectors ::= NamedSelector [‘,’ ImportSelectors] + | WildCardSelector {‘,’ WildCardSelector} + +EndMarker ::= ‘end’ EndMarkerTag -- when followed by EOL +EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ | ‘try’ + | ‘new’ | ‘this’ | ‘given’ | ‘extension’ | ‘val’ +``` + +### Declarations and Definitions +``` +RefineDcl ::= ‘val’ ValDcl + | ‘def’ DefDcl + | ‘type’ {nl} TypeDcl +Dcl ::= RefineDcl + | ‘var’ VarDcl +ValDcl ::= ids ‘:’ Type +VarDcl ::= ids ‘:’ Type +DefDcl ::= DefSig ‘:’ Type +DefSig ::= id [DefTypeParamClause] DefParamClauses +TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type] + +Def ::= ‘val’ PatDef + | ‘var’ PatDef + | ‘def’ DefDef + | ‘type’ {nl} TypeDcl + | TmplDef +PatDef ::= ids [‘:’ Type] ‘=’ Expr + | Pattern2 [‘:’ Type] ‘=’ Expr +DefDef ::= DefSig [‘:’ Type] ‘=’ Expr + | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr + +TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef + | [‘case’] ‘object’ ObjectDef + | ‘enum’ EnumDef + | ‘given’ GivenDef +ClassDef ::= id ClassConstr [Template] +ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses +ConstrMods ::= {Annotation} [AccessModifier] +ObjectDef ::= id [Template] +EnumDef ::= id ClassConstr InheritClauses EnumBody +GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) +GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present +StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody] +Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} + ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods +ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> +ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef + | Export +Template ::= InheritClauses [TemplateBody] +InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] +ConstrApps ::= ConstrApp ({‘,’ ConstrApp} | {‘with’ ConstrApp}) +ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} +ConstrExpr ::= SelfInvocation + | <<< SelfInvocation {semi BlockStat} >>> +SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} + +WithTemplateBody ::= <<< [SelfType] TemplateStat {semi TemplateStat} >>> +TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> +TemplateStat ::= Import + | Export + | {Annotation [nl]} {Modifier} Def + | {Annotation [nl]} {Modifier} Dcl + | Extension + | Expr1 + | EndMarker + | +SelfType ::= id [‘:’ InfixType] ‘=>’ + | ‘this’ ‘:’ InfixType ‘=>’ + +EnumBody ::= :<<< [SelfType] EnumStat {semi EnumStat} >>> +EnumStat ::= TemplateStat + | {Annotation [nl]} {Modifier} EnumCase +EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps]] | ids) + +TopStats ::= TopStat {semi TopStat} +TopStat ::= Import + | Export + | {Annotation [nl]} {Modifier} Def + | Extension + | Packaging + | PackageObject + | EndMarker + | +Packaging ::= ‘package’ QualId :<<< TopStats >>> +PackageObject ::= ‘package’ ‘object’ ObjectDef + +CompilationUnit ::= {‘package’ QualId semi} TopStats +``` From 074b3b8f44f3411357b8bde68c5d9acdd2524aaf Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 13 Oct 2022 14:34:09 +0200 Subject: [PATCH 102/657] Add docker for website generation --- .gitignore | 2 ++ docs/_spec/Dockerfile | 24 +++++++++++++++ docs/_spec/Gemfile | 8 +++++ docs/_spec/Gemfile.lock | 57 +++++++++++++++++++++++++++++++++++ docs/_spec/README.md | 11 +++++++ docs/_spec/docker-compose.yml | 11 +++++++ 6 files changed, 113 insertions(+) create mode 100644 docs/_spec/Dockerfile create mode 100644 docs/_spec/Gemfile create mode 100644 docs/_spec/Gemfile.lock create mode 100644 docs/_spec/docker-compose.yml diff --git a/.gitignore b/.gitignore index 4ac67ddfbb06..5240662741bb 100644 --- a/.gitignore +++ b/.gitignore @@ -93,3 +93,5 @@ compiler/test-coursier/run/*.jar # docs related contributors.js content-contributors.css +docs/_spec/_site/ +docs/_spec/.jekyll-metadata diff --git a/docs/_spec/Dockerfile b/docs/_spec/Dockerfile new file mode 100644 index 000000000000..a08c5d0e7f98 --- /dev/null +++ b/docs/_spec/Dockerfile @@ -0,0 +1,24 @@ +FROM ruby:2.7 + +RUN apt-get install -y curl \ + && curl -sL https://deb.nodesource.com/setup_18.x | bash - \ + && apt-get install -y nodejs \ + && curl -L https://www.npmjs.com/install.sh | sh + +RUN gem install bundler:1.17.2 jekyll + +WORKDIR /srv/jekyll + +COPY Gemfile . +COPY Gemfile.lock . + + +RUN echo -n "bundle version: " && bundle --version +RUN bundle install +RUN mkdir /opt/npm-global +RUN npm config set prefix '/opt/npm-global' +RUN npm config set global true +RUN npm install bower +RUN echo -n "npm version: " && npm --version +RUN chmod u+s /bin/chown +RUN date diff --git a/docs/_spec/Gemfile b/docs/_spec/Gemfile new file mode 100644 index 000000000000..bc45dc84db8c --- /dev/null +++ b/docs/_spec/Gemfile @@ -0,0 +1,8 @@ +# To build the spec on Travis CI +source "https://rubygems.org" + +gem "jekyll", "3.6.3" +gem "webrick" +gem "rouge" +# gem 's3_website' +gem "redcarpet", "3.5.1" diff --git a/docs/_spec/Gemfile.lock b/docs/_spec/Gemfile.lock new file mode 100644 index 000000000000..48efd373725e --- /dev/null +++ b/docs/_spec/Gemfile.lock @@ -0,0 +1,57 @@ +GEM + remote: https://rubygems.org/ + specs: + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) + colorator (1.1.0) + ffi (1.15.5) + forwardable-extended (2.6.0) + jekyll (3.6.3) + addressable (~> 2.4) + colorator (~> 1.0) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 1.1) + kramdown (~> 1.14) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (>= 1.7, < 3) + safe_yaml (~> 1.0) + jekyll-sass-converter (1.5.2) + sass (~> 3.4) + jekyll-watch (1.5.1) + listen (~> 3.0) + kramdown (1.17.0) + liquid (4.0.3) + listen (3.7.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + mercenary (0.3.6) + pathutil (0.16.2) + forwardable-extended (~> 2.6) + public_suffix (5.0.0) + rb-fsevent (0.11.2) + rb-inotify (0.10.1) + ffi (~> 1.0) + redcarpet (3.5.1) + rouge (2.2.1) + safe_yaml (1.0.5) + sass (3.7.4) + sass-listen (~> 4.0.0) + sass-listen (4.0.0) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + webrick (1.7.0) + +PLATFORMS + ruby + x86_64-linux + +DEPENDENCIES + jekyll (= 3.6.3) + redcarpet (= 3.5.1) + rouge + webrick + +BUNDLED WITH + 2.3.5 diff --git a/docs/_spec/README.md b/docs/_spec/README.md index d748dddedfd2..e1c7e3497601 100644 --- a/docs/_spec/README.md +++ b/docs/_spec/README.md @@ -16,11 +16,22 @@ We aim to track the configuration GitHub Pages uses but differences may arise as ## Building + Travis CI builds the spec automatically after every merged pull release and publishes to https://www.scala-lang.org/files/archive/spec/2.13/. + +To preview locally, run the following commands in the docs/_spec subfolder: + +``` +env UID="$(id -u)" GID="$(id -g)" docker-compose up +``` + +and open http://0.0.0.0:4000/files/archive/spec/2.13/ to view the spec. Jekyll will rebuild as you edit the markdown, but make sure to restart it when you change `_config.yml`. + ## General Advice for editors diff --git a/docs/_spec/docker-compose.yml b/docs/_spec/docker-compose.yml new file mode 100644 index 000000000000..3eadc939ed40 --- /dev/null +++ b/docs/_spec/docker-compose.yml @@ -0,0 +1,11 @@ +version: '2' + +services: + jekyll: + user: "${UID}:${GID}" + build: . + command: sh -c "chown $UID / && bundle exec jekyll serve --incremental --host=0.0.0.0 " + ports: + - '4000:4000' + volumes: + - .:/srv/jekyll From d557687147e99d77fba24b98031af1ab1528ad91 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 14:35:16 +0200 Subject: [PATCH 103/657] Drop hypersections 14 and 15 14: Empty list of references (there is content in the comments however) 15: Out of date changelog --- docs/_spec/14-references.md | 207 --------- docs/_spec/15-changelog.md | 847 ------------------------------------ 2 files changed, 1054 deletions(-) delete mode 100644 docs/_spec/14-references.md delete mode 100644 docs/_spec/15-changelog.md diff --git a/docs/_spec/14-references.md b/docs/_spec/14-references.md deleted file mode 100644 index cc088dfcae11..000000000000 --- a/docs/_spec/14-references.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -title: References -layout: default -chapter: 14 ---- - -# References - -TODO (see comments in markdown source) - - diff --git a/docs/_spec/15-changelog.md b/docs/_spec/15-changelog.md deleted file mode 100644 index 5d24511ff1cb..000000000000 --- a/docs/_spec/15-changelog.md +++ /dev/null @@ -1,847 +0,0 @@ ---- -title: Changelog -layout: default -chapter: 15 ---- - -# Changelog - -This changelog was no longer maintained after version 2.8.0. - -A pull request updating this chapter to list the most significant -changes made in more recent Scala versions would be highly welcome. - -Many language changes, especially larger ones, are documented in SIP -(Scala Improvement Process) proposals. Most proposals that were -accepted and implemented have not merged into the main spec. Pull -requests that merge SIPs into the main spec are also highly welcome. - -To find out what has changed in Scala 2 since 2.8.0, you can consult -the following sources: - -* Scala release notes (recent versions): https://github.com/scala/scala/releases -* Scala release notes (older versions): https://scala-lang.org/blog/announcements/ -* Scala release notes (even older versions): presumably findable via search engine -* Spec changelog in version control: https://github.com/scala/scala/commits/2.13.x/spec -* SIPs: https://docs.scala-lang.org/sips/all.html - -## Changes in Version 2.8.0 - -Trailing commas in expression, argument, type or pattern sequences are -no longer supported. - -Changed visibility rules for nested packages (where done?) - -Changed [visibility rules](02-identifiers-names-and-scopes.html) -so that packages are no longer treated specially. - -Added section on [weak conformance](03-types.html#weak-conformance). -Relaxed type rules for conditionals, -match expressions, try expressions to compute their result type using -least upper bound wrt weak conformance. Relaxed type rule for local type -inference so that argument types need only weekly conform to inferred -formal parameter types. Added section on -[numeric widening](06-expressions.html#numeric-widening) to support -weak conformance. - -Tightened rules to avoid accidental [overrides](05-classes-and-objects.html#overriding). - -Removed class literals. - -Added section on [context bounds](07-implicits.html#context-bounds-and-view-bounds). - -Clarified differences between [`isInstanceOf` and pattern matches](12-the-scala-standard-library.html#root-classes). - -Allowed [`implicit` modifier on function literals](06-expressions.html#anonymous-functions) with a single parameter. - -## Changes in Version 2.7.2 - -_(10-Nov-2008)_ - -#### Precedence of Assignment Operators - -The [precedence of assignment operators](06-expressions.html#prefix,-infix,-and-postfix-operations) -has been brought in line with. From now on `+=`, has the same precedence as `=`. - -#### Wildcards as function parameters - -A formal parameter to an anonymous function may now be a -[wildcard represented by an underscore](06-expressions.html#placeholder-syntax-for-anonymous-functions). - -> _ => 7 // The function that ignores its argument -> // and always returns 7. - -#### Unicode alternative for left arrow - -The Unicode glyph ‘´\leftarrow´’ ´`\u2190`´ is now treated as a reserved -identifier, equivalent to the ASCII symbol ‘`<-`’. - -## Changes in Version 2.7.1 - -_(09-April-2008)_ - -#### Change in Scoping Rules for Wildcard Placeholders in Types - -A wildcard in a type now binds to the closest enclosing type -application. For example `List[List[_]]` is now equivalent to this -existential type: - - List[List[t] forSome { type t }] - -In version 2.7.0, the type expanded instead to: - - List[List[t]] forSome { type t } - -The new convention corresponds exactly to the way wildcards in Java are -interpreted. - -#### No Contractiveness Requirement for Implicits - -The contractiveness requirement for -[implicit method definitions](07-implicits.html#implicit-parameters) -has been dropped. Instead, it is checked for each implicit expansion individually -that the expansion does not result in a cycle or a tree of infinitely -growing types. - -## Changes in Version 2.7.0 - -_(07-Feb-2008)_ - -#### Java Generics - -Scala now supports Java generic types by default: - -- A generic type in Java such as `ArrayList` is translated to - a generic type in Scala: `ArrayList[String]`. - -- A wildcard type such as `ArrayList` is translated - to `ArrayList[_ <: Number]`. This is itself a shorthand for the - existential type `ArrayList[T] forSome { type T <: Number }`. - -- A raw type in Java such as `ArrayList` is translated to - `ArrayList[_]`, which is a shorthand for - `ArrayList[T] forSome { type T }`. - -This translation works if `-target:jvm-1.5` is specified, which is the -new default. For any other target, Java generics are not recognized. To -ensure upgradability of Scala codebases, extraneous type parameters for -Java classes under `-target:jvm-1.4` are simply ignored. For instance, -when compiling with `-target:jvm-1.4`, a Scala type such as -`ArrayList[String]` is simply treated as the unparameterized type -`ArrayList`. - -#### Changes to Case Classes - -The Scala compiler generates a [companion extractor object for every case class] -(05-classes-and-objects.html#case-classes) now. For instance, given the case class: - - case class X(elem: String) - -the following companion object is generated: - - object X { - def unapply(x: X): Some[String] = Some(x.elem) - def apply(s: String): X = new X(s) - } - -If the object exists already, only the `apply` and `unapply` methods are -added to it. - -Three restrictions on case classes have been removed. - -1. Case classes can now inherit from other case classes. - -2. Case classes may now be `abstract`. - -3. Case classes may now come with companion objects. - -## Changes in Version 2.6.1 - -_(30-Nov-2007)_ - -#### Mutable variables introduced by pattern binding - -[Mutable variables can now be introduced by a pattern matching definition] -(04-basic-declarations-and-definitions.html#variable-declarations-and-definitions), -just like values can. Examples: - - var (x, y) = if (positive) (1, 2) else (-1, -3) - var hd :: tl = mylist - -#### Self-types - -Self types can now be introduced without defining an alias name for -[`this`](05-classes-and-objects.html#templates). Example: - - class C { - type T <: Trait - trait Trait { this: T => ... } - } - -## Changes in Version 2.6 - -_(27-July-2007)_ - -#### Existential types - -It is now possible to define [existential types](03-types.html#existential-types). -An existential type has the form `T forSome {Q}` where `Q` is a sequence of value and/or -type declarations. Given the class definitions - - class Ref[T] - abstract class Outer { type T } - -one may for example write the following existential types - - Ref[T] forSome { type T <: java.lang.Number } - Ref[x.T] forSome { val x: Outer } - -#### Lazy values - -It is now possible to define lazy value declarations using the new modifier -[`lazy`](04-basic-declarations-and-definitions.html#value-declarations-and-definitions). -A `lazy` value definition evaluates its right hand -side ´e´ the first time the value is accessed. Example: - - import compat.Platform._ - val t0 = currentTime - lazy val t1 = currentTime - val t2 = currentTime - - println("t0 <= t2: " + (t0 <= t2)) //true - println("t1 <= t2: " + (t1 <= t2)) //false (lazy evaluation of t1) - -#### Structural types - -It is now possible to declare structural types using [type refinements] -(03-types.html#compound-types). For example: - - class File(name: String) { - def getName(): String = name - def open() { /*..*/ } - def close() { println("close file") } - } - def test(f: { def getName(): String }) { println(f.getName) } - - test(new File("test.txt")) - test(new java.io.File("test.txt")) - -There’s also a shorthand form for creating values of structural types. -For instance, - - new { def getName() = "aaron" } - -is a shorthand for - - new AnyRef{ def getName() = "aaron" } - -## Changes in Version 2.5 - -_(02-May-2007)_ - -#### Type constructor polymorphism - -_Implemented by Adriaan Moors_ - -[Type parameters](04-basic-declarations-and-definitions.html#type-parameters) -and abstract -[type members](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases) can now also abstract over [type constructors](03-types.html#type-constructors). - -This allows a more precise `Iterable` interface: - - trait Iterable[+T] { - type MyType[+T] <: Iterable[T] // MyType is a type constructor - - def filter(p: T => Boolean): MyType[T] = ... - def map[S](f: T => S): MyType[S] = ... - } - - abstract class List[+T] extends Iterable[T] { - type MyType[+T] = List[T] - } - -This definition of `Iterable` makes explicit that mapping a function -over a certain structure (e.g., a `List`) will yield the same structure -(containing different elements). - -#### Early object initialization - -[Early object initialization](05-classes-and-objects.html#early-definitions) -makes it possible to initialize some fields of an object before any -parent constructors are called. This is particularly useful for -traits, which do not have normal constructor parameters. Example: - - trait Greeting { - val name: String - val msg = "How are you, "+name - } - class C extends { - val name = "Bob" - } with Greeting { - println(msg) - } - -In the code above, the field is initialized before the constructor of is -called. Therefore, field `msg` in class is properly initialized to . - -#### For-comprehensions, revised - -The syntax of [for-comprehensions](06-expressions.html#for-comprehensions-and-for-loops) -has changed. -In the new syntax, generators do not start with a `val` anymore, but filters -start with an `if` (and are called guards). -A semicolon in front of a guard is optional. For example: - - for (val x <- List(1, 2, 3); x % 2 == 0) println(x) - -is now written - - for (x <- List(1, 2, 3) if x % 2 == 0) println(x) - -The old syntax is still available but will be deprecated in the future. - -#### Implicit anonymous functions - -It is now possible to define [anonymous functions using underscores] -(06-expressions.html#placeholder-syntax-for-anonymous-functions) in -parameter position. For instance, the expressions in the left column -are each function values which expand to the anonymous functions on -their right. - - _ + 1 x => x + 1 - _ * _ (x1, x2) => x1 * x2 - (_: int) * 2 (x: int) => (x: int) * 2 - if (_) x else y z => if (z) x else y - _.map(f) x => x.map(f) - _.map(_ + 1) x => x.map(y => y + 1) - -As a special case, a [partially unapplied method](06-expressions.html#method-values) -is now designated `m _`   instead of the previous notation  `&m`. - -The new notation will displace the special syntax forms `.m()` for -abstracting over method receivers and `&m` for treating an unapplied -method as a function value. For the time being, the old syntax forms are -still available, but they will be deprecated in the future. - -#### Pattern matching anonymous functions, refined - -It is now possible to use [case clauses to define a function value] -(08-pattern-matching.html#pattern-matching-anonymous-functions) -directly for functions of arities greater than one. Previously, only -unary functions could be defined that way. Example: - - def scalarProduct(xs: Array[Double], ys: Array[Double]) = - (xs zip ys).foldLeft(0.0) { - case (a, (b, c)) => a + b * c - } - -## Changes in Version 2.4 - -_(09-Mar-2007)_ - -#### Object-local private and protected - -The `private` and `protected` modifiers now accept a -[`[this]` qualifier](05-classes-and-objects.html#modifiers). -A definition ´M´ which is labelled `private[this]` is private, -and in addition can be accessed only from within the current object. -That is, the only legal prefixes for ´M´ are `this` or `´C´.this`. -Analogously, a definition ´M´ which is labelled `protected[this]` is -protected, and in addition can be accessed only from within the current -object. - -#### Tuples, revised - -The syntax for [tuples](06-expressions.html#tuples) has been changed from ´\\{…\\}´ to -´(…)´. For any sequence of types ´T_1 , … , T_n´, - -´(T_1 , … , T_n)´ is a shorthand for `Tuple´n´[´T_1 , … , T_n´]`. - -Analogously, for any sequence of expressions or patterns ´x_1 -, … , x_n´, - -´(x_1 , … , x_n)´ is a shorthand for `Tuple´n´(´x_1 , … , x_n´)`. - -#### Access modifiers for primary constructors - -The primary constructor of a class can now be marked [`private` or `protected`] -(05-classes-and-objects.html#class-definitions). -If such an access modifier is given, it comes between the name of the class and its -value parameters. Example: - - class C[T] private (x: T) { ... } - -#### Annotations - -The support for attributes has been extended and its syntax changed. -Attributes are now called [*annotations*](11-annotations.html). The syntax has -been changed to follow Java’s conventions, e.g. `@attribute` instead of -`[attribute]`. The old syntax is still available but will be deprecated -in the future. - -Annotations are now serialized so that they can be read by compile-time -or run-time tools. Class has two sub-traits which are used to indicate -how annotations are retained. Instances of an annotation class -inheriting from trait will be stored in the generated class files. -Instances of an annotation class inheriting from trait will be visible -to the Scala type-checker in every compilation unit where the annotated -symbol is accessed. - -#### Decidable subtyping - -The implementation of subtyping has been changed to prevent infinite -recursions. -[Termination of subtyping](05-classes-and-objects.html#inheritance-closure) -is now ensured by a new restriction of class graphs to be finitary. - -#### Case classes cannot be abstract - -It is now explicitly ruled out that case classes can be abstract. The -specification was silent on this point before, but did not explain how -abstract case classes were treated. The Scala compiler allowed the -idiom. - -#### New syntax for self aliases and self types - -It is now possible to give an explicit alias name and/or type for the -[self reference](05-classes-and-objects.html#templates) `this`. For instance, in - - class C { self: D => - ... - } - -the name `self` is introduced as an alias for `this` within `C` and the -[self type](05-classes-and-objects.html#class-definitions) of `C` is -assumed to be `D`. This construct is introduced now in order to replace -eventually both the qualified this construct and the clause in Scala. - -#### Assignment Operators - -It is now possible to [combine operators with assignments] -(06-expressions.html#assignment-operators). Example: - - var x: int = 0 - x += 1 - -## Changes in Version 2.3.2 - -_(23-Jan-2007)_ - -#### Extractors - -It is now possible to define patterns independently of case classes, using -`unapply` methods in [extractor objects](08-pattern-matching.html#extractor-patterns). -Here is an example: - - object Twice { - def apply(x:Int): int = x*2 - def unapply(z:Int): Option[int] = if (z%2==0) Some(z/2) else None - } - val x = Twice(21) - x match { case Twice(n) => Console.println(n) } // prints 21 - -In the example, `Twice` is an extractor object with two methods: - -- The `apply` method is used to build even numbers. - -- The `unapply` method is used to decompose an even number; it is in a sense - the reverse of `apply`. `unapply` methods return option types: - `Some(...)` for a match that succeeds, `None` for a match that fails. - Pattern variables are returned as the elements of `Some`. - If there are several variables, they are grouped in a tuple. - -In the second-to-last line, `Twice`’s method is used to construct a number `x`. -In the last line, `x` is tested against the pattern `Twice(n)`. -This pattern succeeds for even numbers and assigns to the variable `n` one half -of the number that was tested. -The pattern match makes use of the `unapply` method of object `Twice`. -More details on extractors can be found in the paper “Matching Objects with -Patterns” by Emir, Odersky and Williams. - -#### Tuples - -A new [lightweight syntax for tuples](06-expressions.html#tuples) has been introduced. -For any sequence of types ´T_1 , … , T_n´, - -´\{T_1 , … , T_n \}´ is a shorthand for `Tuple´n´[´T_1 , … , T_n´]`. - -Analogously, for any sequence of expressions or patterns ´x_1, … , x_n´, - -´\{x_1 , … , x_n \}´ is a shorthand for `Tuple´n´(´x_1 , … , x_n´)`. - -#### Infix operators of greater arities - -It is now possible to use methods which have more than one parameter as -[infix operators](06-expressions.html#infix-operations). In this case, all -method arguments are written as a normal parameter list in parentheses. Example: - - class C { - def +(x: int, y: String) = ... - } - val c = new C - c + (1, "abc") - -#### Deprecated attribute - -A new standard attribute [`deprecated`](11-annotations.html#deprecation-annotations) -is available. If a member definition is marked with this attribute, any -reference to the member will cause a “deprecated” warning message to be emitted. - -## Changes in Version 2.3 - -_(23-Nov-2006)_ - -#### Procedures - -A simplified syntax for [methods returning `unit`] -(04-basic-declarations-and-definitions.html#procedures) has been introduced. -Scala now allows the following shorthands: - -`def f(params)` **for** `def f(params): unit` -`def f(params) { ... }` **for** `def f(params): unit = { ... }` - -#### Type Patterns - -The [syntax of types in patterns](08-pattern-matching.html#type-patterns) has -been refined. -Scala now distinguishes between type variables (starting with a lower case -letter) and types as type arguments in patterns. -Type variables are bound in the pattern. -Other type arguments are, as in previous versions, erased. -The Scala compiler will now issue an “unchecked” warning at places where type -erasure might compromise type-safety. - -#### Standard Types - -The recommended names for the two bottom classes in Scala’s type -hierarchy have changed as follows: - - All ==> Nothing - AllRef ==> Null - -The old names are still available as type aliases. - -## Changes in Version 2.1.8 - -_(23-Aug-2006)_ - -#### Visibility Qualifier for protected - -Protected members can now have a visibility qualifier, e.g. -[`protected[]`](05-classes-and-objects.html#protected). -In particular, one can now simulate package protected access as in Java writing - - protected[P] def X ... - -where would name the package containing `X`. - -#### Relaxation of Private Access - -[Private members of a class](05-classes-and-objects.html#private) can now be -referenced from the companion module of the class and vice versa. - -#### Implicit Lookup - -The lookup method for [implicit definitions](07-implicits.html#implicit-parameters) -has been generalized. -When searching for an implicit definition matching a type ´T´, now are considered - -1. all identifiers accessible without prefix, and - -2. all members of companion modules of classes associated with ´T´. - -(The second clause is more general than before). Here, a class is _associated_ -with a type ´T´ if it is referenced by some part of ´T´, or if it is a -base class of some part of ´T´. -For instance, to find implicit members corresponding to the type - - HashSet[List[Int], String] - -one would now look in the companion modules (aka static parts) of `HashSet`, -`List`, `Int`, and `String`. Before, it was just the static part of . - -#### Tightened Pattern Match - -A typed [pattern match with a singleton type `p.type`](08-pattern-matching.html#type-patterns) -now tests whether the selector value is reference-equal to `p`. Example: - - val p = List(1, 2, 3) - val q = List(1, 2) - val r = q - r match { - case _: p.type => Console.println("p") - case _: q.type => Console.println("q") - } - -This will match the second case and hence will print “q”. Before, the -singleton types were erased to `List`, and therefore the first case would have -matched, which is non-sensical. - -## Changes in Version 2.1.7 - -_(19-Jul-2006)_ - -#### Multi-Line string literals - -It is now possible to write [multi-line string-literals] -(01-lexical-syntax.html#string-literals) enclosed in triple quotes. Example: - - """this is a - multi-line - string literal""" - -No escape substitutions except for unicode escapes are performed in such -string literals. - -#### Closure Syntax - -The syntax of [closures](06-expressions.html#anonymous-functions) -has been slightly restricted. The form - - x: T => E - -is valid only when enclosed in braces, i.e.  `{ x: T => E }`. The -following is illegal, because it might be read as the value x typed with -the type `T => E`: - - val f = x: T => E - -Legal alternatives are: - - val f = { x: T => E } - val f = (x: T) => E - -## Changes in Version 2.1.5 - -_(24-May-2006)_ - -#### Class Literals - -There is a new syntax for [class literals](06-expressions.html#literals): -For any class type ´C´, `classOf[´C´]` designates the run-time -representation of ´C´. - -## Changes in Version 2.0 - -_(12-Mar-2006)_ - -Scala in its second version is different in some details from the first -version of the language. There have been several additions and some old -idioms are no longer supported. This appendix summarizes the main -changes. - -#### New Keywords - -The following three words are now reserved; they cannot be used as -[identifiers](01-lexical-syntax.html#identifiers): - - implicit match requires - -#### Newlines as Statement Separators - -[Newlines](https://www.scala-lang.org/files/archive/spec/2.11/) -can now be used as statement separators in place of semicolons. - -#### Syntax Restrictions - -There are some other situations where old constructs no longer work: - -##### *Pattern matching expressions* - -The `match` keyword now appears only as infix operator between a -selector expression and a number of cases, as in: - - expr match { - case Some(x) => ... - case None => ... - } - -Variants such as ` expr.match {...} ` or just ` match {...} ` are no -longer supported. - -##### *“With” in extends clauses* - -The idiom - - class C with M { ... } - -is no longer supported. A `with` connective is only allowed following an -`extends` clause. For instance, the line above would have to be written - - class C extends AnyRef with M { ... } . - -However, assuming `M` is a [trait](05-classes-and-objects.html#traits), -it is also legal to write - - class C extends M { ... } - -The latter expression is treated as equivalent to - - class C extends S with M { ... } - -where `S` is the superclass of `M`. - -##### *Regular Expression Patterns* - -The only form of regular expression pattern that is currently supported -is a sequence pattern, which might end in a sequence wildcard . Example: - - case List(1, 2, _*) => ... // will match all lists starting with 1, 2, ... - -It is at current not clear whether this is a permanent restriction. We -are evaluating the possibility of re-introducing full regular expression -patterns in Scala. - -#### Selftype Annotations - -The recommended syntax of selftype annotations has changed. - - class C: T extends B { ... } - -becomes - - class C requires T extends B { ... } - -That is, selftypes are now indicated by the new `requires` keyword. The -old syntax is still available but is considered deprecated. - -#### For-comprehensions - -[For-comprehensions](06-expressions.html#for-comprehensions-and-for-loops) -now admit value and pattern definitions. Example: - - for { - val x <- List.range(1, 100) - val y <- List.range(1, x) - val z = x + y - isPrime(z) - } yield Pair(x, y) - -Note the definition  `val z = x + y` as the third item in the -for-comprehension. - -#### Conversions - -The rules for [implicit conversions of methods to functions] -(06-expressions.html#method-conversions) have been tightened. -Previously, a parameterized method used as a value was always -implicitly converted to a function. This could lead to unexpected -results when method arguments where forgotten. Consider for instance the -statement below: - - show(x.toString) - -where `show` is defined as follows: - - def show(x: String) = Console.println(x) . - -Most likely, the programmer forgot to supply an empty argument list `()` -to `toString`. The previous Scala version would treat this code as a -partially applied method, and expand it to: - - show(() => x.toString()) - -As a result, the address of a closure would be printed instead of the -value of `s`. - -Scala version 2.0 will apply a conversion from partially applied method -to function value only if the expected type of the expression is indeed -a function type. For instance, the conversion would not be applied in -the code above because the expected type of `show`’s parameter is -`String`, not a function type. - -The new convention disallows some previously legal code. Example: - - def sum(f: int => double)(a: int, b: int): double = - if (a > b) 0 else f(a) + sum(f)(a + 1, b) - - val sumInts = sum(x => x) // error: missing arguments - -The partial application of `sum` in the last line of the code above will -not be converted to a function type. Instead, the compiler will produce -an error message which states that arguments for method `sum` are -missing. The problem can be fixed by providing an expected type for the -partial application, for instance by annotating the definition of -`sumInts` with its type: - - val sumInts: (int, int) => double = sum(x => x) // OK - -On the other hand, Scala version 2.0 now automatically applies methods -with empty parameter lists to `()` argument lists when necessary. For -instance, the `show` expression above will now be expanded to - - show(x.toString()) . - -Scala version 2.0 also relaxes the rules of overriding with respect to -empty parameter lists. The revised definition of -[_matching members_](05-classes-and-objects.html#class-members) -makes it now possible to override a method with an -explicit, but empty parameter list `()` with a parameterless method, and -_vice versa_. For instance, the following class definition -is now legal: - - class C { - override def toString: String = ... - } - -Previously this definition would have been rejected, because the -`toString` method as inherited from `java.lang.Object` takes an empty -parameter list. - -#### Class Parameters - -A [class parameter](05-classes-and-objects.html#class-definitions) -may now be prefixed by `val` or `var`. - -#### Private Qualifiers - -Previously, Scala had three levels of visibility: -*private*, *protected* and -*public*. There was no way to restrict accesses to members -of the current package, as in Java. - -Scala 2 now defines [access qualifiers](05-classes-and-objects.html#modifiers) -that let one express this level of visibility, among others. In the definition - - private[C] def f(...) - -access to `f` is restricted to all code within the class or package `C` -(which must contain the definition of `f`). - -#### Changes in the Mixin Model - -The model which details [mixin composition of classes] -(05-classes-and-objects.html#templates) has changed significantly. -The main differences are: - -1. We now distinguish between *traits* that are used as - mixin classes and normal classes. The syntax of traits has been - generalized from version 1.0, in that traits are now allowed to have - mutable fields. However, as in version 1.0, traits still may not - have constructor parameters. - -2. Member resolution and super accesses are now both defined in terms - of a *class linearization*. - -3. Scala’s notion of method overloading has been generalized; in - particular, it is now possible to have overloaded variants of the - same method in a subclass and in a superclass, or in several - different mixins. This makes method overloading in Scala - conceptually the same as in Java. - -#### Implicit Parameters - -Views in Scala 1.0 have been replaced by the more general concept of -[implicit parameters](07-implicits.html#implicit-parameters). - -#### Flexible Typing of Pattern Matching - -The new version of Scala implements more flexible typing rules when it -comes to [pattern matching over heterogeneous class hierarchies] -(08-pattern-matching.html#pattern-matching-expressions). -A *heterogeneous class hierarchy* is one where subclasses -inherit a common superclass with different parameter types. With the new -rules in Scala version 2.0 one can perform pattern matches over such -hierarchies with more precise typings that keep track of the information -gained by comparing the types of a selector and a matching pattern. -This gives Scala capabilities analogous to guarded algebraic data types. From 38e79aae2f11522e55948f4073e852ac90d821fb Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 14 Oct 2022 14:11:12 +0200 Subject: [PATCH 104/657] =?UTF-8?q?Replace=20=E2=80=A6=20by=20...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/_spec/01-lexical-syntax.md | 4 +- docs/_spec/03-types.md | 56 +++++++++---------- docs/_spec/08-pattern-matching.md | 6 +- docs/_spec/13-syntax-summary.md | 8 +-- .../changed-features/overload-resolution.md | 4 +- docs/_spec/TODOreference/syntax.md | 14 ++--- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md index 921384b40e20..46bd876b4077 100644 --- a/docs/_spec/01-lexical-syntax.md +++ b/docs/_spec/01-lexical-syntax.md @@ -19,7 +19,7 @@ classes (Unicode general category given in parentheses): 1. Letters, which include lower case letters (`Ll`), upper case letters (`Lu`), title case letters (`Lt`), other letters (`Lo`), modifier letters (`Lm`), letter numerals (`Nl`) and the two characters `\u0024 ‘$’` and `\u005F ‘_’`. -1. Digits `‘0’ | … | ‘9’`. +1. Digits `‘0’ | ... | ‘9’`. 1. Parentheses `‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ `. 1. Delimiter characters ``‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ ``. 1. Operator characters. These consist of all printable ASCII characters @@ -41,7 +41,7 @@ id ::= plainid idrest ::= {letter | digit} [‘_’ op] escapeSeq ::= UnicodeEscape | charEscapeSeq UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit -hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ +hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ ``` There are three ways to form an identifier. First, an identifier can diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index 3c78b33e571c..cd70a4e34373 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -207,13 +207,13 @@ substitution ´[ a_1 := T_1 , \ldots , a_n := T_n ]´. Given the partial type definitions: ```scala -class TreeMap[A <: Comparable[A], B] { … } -class List[A] { … } -class I extends Comparable[I] { … } +class TreeMap[A <: Comparable[A], B] { ... } +class List[A] { ... } +class I extends Comparable[I] { ... } -class F[M[_], X] { … } -class S[K <: String] { … } -class G[M[ Z <: I ], I] { … } +class F[M[_], X] { ... } +class S[K <: String] { ... } +class G[M[ Z <: I ], I] { ... } ``` the following parameterized types are well-formed: @@ -255,20 +255,20 @@ A _tuple type_ ´(T_1 , \ldots , T_n)´ is an alias for the class `scala.Tuple´n´[´T_1´, … , ´T_n´]`, where ´n \geq 2´. Tuple classes are case classes whose fields can be accessed using -selectors `_1` , … , `_n`. Their functionality is +selectors `_1`, ..., `_n`. Their functionality is abstracted in a corresponding `Product` trait. The _n_-ary tuple class and product trait are defined at least as follows in the standard Scala library (they might also add other methods and implement other traits). ```scala -case class Tuple´_n´[+´T_1´, … , +´T_n´](_1: ´T_1´, … , _n: ´T_n´) -extends Product´_n´[´T_1´, … , ´T_n´] +case class Tuple´_n´[+´T_1´, ..., +´T_n´](_1: ´T_1´, ..., _n: ´T_n´) +extends Product´_n´[´T_1´, ..., ´T_n´] -trait Product´_n´[+´T_1´, … , +´T_n´] { +trait Product´_n´[+´T_1´, ..., +´T_n´] { override def productArity = ´n´ def _1: ´T_1´ - … + ... def _n: ´T_n´ } ``` @@ -302,7 +302,7 @@ RefineStat ::= Dcl | ``` -A _compound type_ ´T_1´ `with` … `with` ´T_n \\{ R \\}´ +A _compound type_ ´T_1´ `with` ... `with` ´T_n \\{ R \\}´ represents objects with members as given in the component types ´T_1 , \ldots , T_n´ and the refinement ´\\{ R \\}´. A refinement ´\\{ R \\}´ contains declarations and type definitions. @@ -323,7 +323,7 @@ definition within the refinement. This restriction does not apply to the method's result type. If no refinement is given, the empty refinement is implicitly added, -i.e. ´T_1´ `with` … `with` ´T_n´ is a shorthand for ´T_1´ `with` … `with` ´T_n \\{\\}´. +i.e. ´T_1´ `with` ... `with` ´T_n´ is a shorthand for ´T_1´ `with` ... `with` ´T_n \\{\\}´. A compound type may also consist of just a refinement ´\\{ R \\}´ with no preceding component types. Such a type is @@ -336,12 +336,12 @@ a parameter type that contains a refinement with structural declarations. ```scala case class Bird (val name: String) extends Object { - def fly(height: Int) = … -… + def fly(height: Int) = ... +... } case class Plane (val callsign: String) extends Object { - def fly(height: Int) = … -… + def fly(height: Int) = ... +... } def takeoff( runway: Int, @@ -415,8 +415,8 @@ types are defined in the Scala library for ´n´ between 0 and 22 as follows. ```scala package scala -trait Function´_n´[-´T_1´ , … , -´T_n´, +´U´] { - def apply(´x_1´: ´T_1´ , … , ´x_n´: ´T_n´): ´U´ +trait Function´_n´[-´T_1´, ..., -´T_n´, +´U´] { + def apply(´x_1´: ´T_1´, ..., ´x_n´: ´T_n´): ´U´ override def toString = "" } ``` @@ -724,7 +724,7 @@ These notions are defined mutually recursively as follows. given as follows. - The base types of a class type ´C´ with parents ´T_1 , \ldots , T_n´ are ´C´ itself, as well as the base types of the compound type - `´T_1´ with … with ´T_n´ { ´R´ }`. + `´T_1´ with ... with ´T_n´ { ´R´ }`. - The base types of an aliased type are the base types of its alias. - The base types of an abstract type are the base types of its upper bound. - The base types of a parameterized type @@ -861,8 +861,8 @@ The conformance relation ´(<:)´ is the smallest transitive relation that satis - A singleton type `´p´.type` conforms to the type of the path ´p´. - A singleton type `´p´.type` conforms to the type `scala.Singleton`. - A type projection `´T´#´t´` conforms to `´U´#´t´` if ´T´ conforms to ´U´. -- A parameterized type `´T´[´T_1´ , … , ´T_n´]` conforms to - `´T´[´U_1´ , … , ´U_n´]` if +- A parameterized type `´T´[´T_1´, ..., ´T_n´]` conforms to + `´T´[´U_1´, ..., ´U_n´]` if the following three conditions hold for ´i \in \{ 1 , \ldots , n \}´: 1. If the ´i´'th type parameter of ´T´ is declared covariant, then ´T_i <: U_i´. @@ -922,13 +922,13 @@ type ´C'´, if one of the following holds. subsumes a method declaration that defines ´x´ with type ´T'´, provided ´T <: T'´. - A type alias - `type ´t´[´T_1´ , … , ´T_n´] = ´T´` subsumes a type alias - `type ´t´[´T_1´ , … , ´T_n´] = ´T'´` if ´T \equiv T'´. -- A type declaration `type ´t´[´T_1´ , … , ´T_n´] >: ´L´ <: ´U´` subsumes - a type declaration `type ´t´[´T_1´ , … , ´T_n´] >: ´L'´ <: ´U'´` if + `type ´t´[´T_1´, ..., ´T_n´] = ´T´` subsumes a type alias + `type ´t´[´T_1´, ..., ´T_n´] = ´T'´` if ´T \equiv T'´. +- A type declaration `type ´t´[´T_1´, ..., ´T_n´] >: ´L´ <: ´U´` subsumes + a type declaration `type ´t´[´T_1´, ..., ´T_n´] >: ´L'´ <: ´U'´` if ´L' <: L´ and ´U <: U'´. - A type or class definition that binds a type name ´t´ subsumes an abstract - type declaration `type t[´T_1´ , … , ´T_n´] >: L <: U` if + type declaration `type t[´T_1´, ..., ´T_n´] >: L <: U` if ´L <: t <: U´. @@ -1024,7 +1024,7 @@ A value member of a volatile type cannot appear in a [path](#paths). A type is _volatile_ if it falls into one of four categories: -A compound type `´T_1´ with … with ´T_n´ {´R\,´}` +A compound type `´T_1´ with ... with ´T_n´ {´R\,´}` is volatile if one of the following three conditions hold. 1. One of ´T_2 , \ldots , T_n´ is a type parameter or abstract type, or diff --git a/docs/_spec/08-pattern-matching.md b/docs/_spec/08-pattern-matching.md index 7607b0db85e0..53dc3ffe4547 100644 --- a/docs/_spec/08-pattern-matching.md +++ b/docs/_spec/08-pattern-matching.md @@ -114,12 +114,12 @@ The expansion of interpolated string literals in patterns is the same as in expressions. If it occurs in a pattern, a interpolated string literal of either of the forms ``` -id"text0{ pat1 }text1 … { patn }textn" -id"""text0{ pat1 }text1 … { patn }textn""" +id"text0{ pat1 }text1 ... { patn }textn" +id"""text0{ pat1 }text1 ... { patn }textn""" ``` is equivalent to: ``` -StringContext("""text0""", …, """textn""").id(pat1, …, patn) +StringContext("""text0""", ..., """textn""").id(pat1, ..., patn) ``` You could define your own `StringContext` to shadow the default one that's in the `scala` package. diff --git a/docs/_spec/13-syntax-summary.md b/docs/_spec/13-syntax-summary.md index 6ece538e2ff1..e799a6f579b3 100644 --- a/docs/_spec/13-syntax-summary.md +++ b/docs/_spec/13-syntax-summary.md @@ -14,14 +14,14 @@ The lexical syntax of Scala is given by the following grammar in EBNF form: ```ebnf whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ -upper ::= ‘A’ | … | ‘Z’ | ‘$’ and any character in Unicode categories Lu, Lt or Nl, +upper ::= ‘A’ | ... | ‘Z’ | ‘$’ and any character in Unicode categories Lu, Lt or Nl, and any character in Unicode categories Lo and Lm that doesn't have contributory property Other_Lowercase -lower ::= ‘a’ | … | ‘z’ | ‘_’ and any character in Unicode category Ll, +lower ::= ‘a’ | ... | ‘z’ | ‘_’ and any character in Unicode category Ll, and any character in Unicode categories Lo or Lm that has contributory property Other_Lowercase letter ::= upper | lower -digit ::= ‘0’ | … | ‘9’ +digit ::= ‘0’ | ... | ‘9’ paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | @@ -29,7 +29,7 @@ opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ and any character in Unicode categories Sm or So printableChar ::= all characters in [\u0020, \u007E] inclusive UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit -hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ +hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) escapeSeq ::= UnicodeEscape | charEscapeSeq op ::= opchar {opchar} diff --git a/docs/_spec/TODOreference/changed-features/overload-resolution.md b/docs/_spec/TODOreference/changed-features/overload-resolution.md index bd7782ded520..621515c2a7f8 100644 --- a/docs/_spec/TODOreference/changed-features/overload-resolution.md +++ b/docs/_spec/TODOreference/changed-features/overload-resolution.md @@ -66,11 +66,11 @@ as follows: Replace the sentence -> Otherwise, let `S1,…,Sm` be the vector of types obtained by typing each argument with an undefined expected type. +> Otherwise, let `S1,...,Sm` be the vector of types obtained by typing each argument with an undefined expected type. with the following paragraph: -> Otherwise, let `S1,…,Sm` be the vector of known types of all argument types, where the _known type_ of an argument `E` +> Otherwise, let `S1,...,Sm` be the vector of known types of all argument types, where the _known type_ of an argument `E` is determined as followed: - If `E` is a function value `(p_1, ..., p_n) => B` that misses some parameter types, the known type diff --git a/docs/_spec/TODOreference/syntax.md b/docs/_spec/TODOreference/syntax.md index e11629c8eaf9..d3526783a5eb 100644 --- a/docs/_spec/TODOreference/syntax.md +++ b/docs/_spec/TODOreference/syntax.md @@ -26,7 +26,7 @@ hexadecimal code: ``` UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit -hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ +hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ ``` Informal descriptions are typeset as `“some comment”`. @@ -38,15 +38,15 @@ form. ``` whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ -upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and Unicode category Lu” -lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll” -letter ::= upper | lower “… and Unicode categories Lo, Lt, Nl” -digit ::= ‘0’ | … | ‘9’ +upper ::= ‘A’ | ... | ‘Z’ | ‘\$’ | ‘_’ “... and Unicode category Lu” +lower ::= ‘a’ | ... | ‘z’ “... and Unicode category Ll” +letter ::= upper | lower “... and Unicode categories Lo, Lt, Nl” +digit ::= ‘0’ | ... | ‘9’ paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ - “… and Unicode categories Sm, So” + “... and Unicode categories Sm, So” printableChar ::= “all characters in [\u0020, \u007E] inclusive” charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) @@ -65,7 +65,7 @@ spliceId ::= ‘$’ alphaid ; integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] -nonZeroDigit ::= ‘1’ | … | ‘9’ +nonZeroDigit ::= ‘1’ | ... | ‘9’ floatingPointLiteral ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] From 14a80c25027bdb7b1ddee864351761193c77a8fa Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 14 Oct 2022 14:46:14 +0200 Subject: [PATCH 105/657] replace \ldots by ... --- docs/_spec/03-types.md | 122 +++++------ .../04-basic-declarations-and-definitions.md | 50 ++--- docs/_spec/05-classes-and-objects.md | 54 ++--- docs/_spec/06-expressions.md | 200 +++++++++--------- docs/_spec/07-implicits.md | 32 +-- docs/_spec/08-pattern-matching.md | 124 +++++------ docs/_spec/09-top-level-definitions.md | 6 +- docs/_spec/11-annotations.md | 2 +- docs/_spec/12-the-scala-standard-library.md | 52 ++--- 9 files changed, 321 insertions(+), 321 deletions(-) diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index cd70a4e34373..205af416b9c5 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -191,16 +191,16 @@ SimpleType ::= SimpleType TypeArgs TypeArgs ::= ‘[’ Types ‘]’ ``` -A _parameterized type_ ´T[ T_1 , \ldots , T_n ]´ consists of a type -designator ´T´ and type parameters ´T_1 , \ldots , T_n´ where +A _parameterized type_ ´T[ T_1, ..., T_n ]´ consists of a type +designator ´T´ and type parameters ´T_1, ..., T_n´ where ´n \geq 1´. ´T´ must refer to a type constructor which takes ´n´ type -parameters ´a_1 , \ldots , a_n´. +parameters ´a_1, ..., a_n´. -Say the type parameters have lower bounds ´L_1 , \ldots , L_n´ and -upper bounds ´U_1, \ldots, U_n´. The parameterized type is +Say the type parameters have lower bounds ´L_1, ..., L_n´ and +upper bounds ´U_1, ..., U_n´. The parameterized type is well-formed if each actual type parameter _conforms to its bounds_, i.e. ´\sigma L_i <: T_i <: \sigma U_i´ where ´\sigma´ is the -substitution ´[ a_1 := T_1 , \ldots , a_n := T_n ]´. +substitution ´[ a_1 := T_1, ..., a_n := T_n ]´. ###### Example Parameterized Types @@ -251,8 +251,8 @@ G[S, Int] // illegal: S constrains its parameter to SimpleType ::= ‘(’ Types ‘)’ ``` -A _tuple type_ ´(T_1 , \ldots , T_n)´ is an alias for the -class `scala.Tuple´n´[´T_1´, … , ´T_n´]`, where ´n \geq 2´. +A _tuple type_ ´(T_1 , ... , T_n)´ is an alias for the +class `scala.Tuple´n´[´T_1´, ... , ´T_n´]`, where ´n \geq 2´. Tuple classes are case classes whose fields can be accessed using selectors `_1`, ..., `_n`. Their functionality is @@ -279,9 +279,9 @@ trait Product´_n´[+´T_1´, ..., +´T_n´] { AnnotType ::= SimpleType {Annotation} ``` -An _annotated type_ ´T´ ´a_1, \ldots, a_n´ +An _annotated type_ ´T´ ´a_1, ..., a_n´ attaches [annotations](11-annotations.html#user-defined-annotations) -´a_1 , \ldots , a_n´ to the type ´T´. +´a_1, ..., a_n´ to the type ´T´. ###### Example @@ -304,10 +304,10 @@ RefineStat ::= Dcl A _compound type_ ´T_1´ `with` ... `with` ´T_n \\{ R \\}´ represents objects with members as given in the component types -´T_1 , \ldots , T_n´ and the refinement ´\\{ R \\}´. A refinement +´T_1, ..., T_n´ and the refinement ´\\{ R \\}´. A refinement ´\\{ R \\}´ contains declarations and type definitions. If a declaration or definition overrides a declaration or definition in -one of the component types ´T_1 , \ldots , T_n´, the usual rules for +one of the component types ´T_1, ..., T_n´, the usual rules for [overriding](05-classes-and-objects.html#overriding) apply; otherwise the declaration or definition is said to be “structural” [^2]. @@ -380,13 +380,13 @@ ending in a colon ‘:’ are right-associative; all other operators are left-associative. In a sequence of consecutive type infix operations -´t_0 \, \mathit{op} \, t_1 \, \mathit{op_2} \, \ldots \, \mathit{op_n} \, t_n´, -all operators ´\mathit{op}\_1 , \ldots , \mathit{op}\_n´ must have the same +´t_0 \, \mathit{op} \, t_1 \, \mathit{op_2} \, ... \, \mathit{op_n} \, t_n´, +all operators ´\mathit{op}\_1, ..., \mathit{op}\_n´ must have the same associativity. If they are all left-associative, the sequence is interpreted as -´(\ldots (t_0 \mathit{op_1} t_1) \mathit{op_2} \ldots) \mathit{op_n} t_n´, +´(... (t_0 \mathit{op_1} t_1) \mathit{op_2} ...) \mathit{op_n} t_n´, otherwise it is interpreted as -´t_0 \mathit{op_1} (t_1 \mathit{op_2} ( \ldots \mathit{op_n} t_n) \ldots)´. +´t_0 \mathit{op_1} (t_1 \mathit{op_2} ( ... \mathit{op_n} t_n) ...)´. ### Function Types @@ -396,8 +396,8 @@ FunctionArgs ::= InfixType | ‘(’ [ ParamType {‘,’ ParamType } ] ‘)’ ``` -The type ´(T_1 , \ldots , T_n) \Rightarrow U´ represents the set of function -values that take arguments of types ´T_1 , \ldots , Tn´ and yield +The type ´(T_1, ..., T_n) \Rightarrow U´ represents the set of function +values that take arguments of types ´T_1, ..., Tn´ and yield results of type ´U´. In the case of exactly one argument type ´T \Rightarrow U´ is a shorthand for ´(T) \Rightarrow U´. An argument type of the form ´\Rightarrow T´ @@ -589,10 +589,10 @@ report as the internal types of defined identifiers. ### Method Types A _method type_ is denoted internally as ´(\mathit{Ps})U´, where ´(\mathit{Ps})´ -is a sequence of parameter names and types ´(p_1:T_1 , \ldots , p_n:T_n)´ +is a sequence of parameter names and types ´(p_1:T_1, ..., p_n:T_n)´ for some ´n \geq 0´ and ´U´ is a (value or method) type. This type -represents named methods that take arguments named ´p_1 , \ldots , p_n´ -of types ´T_1 , \ldots , T_n´ +represents named methods that take arguments named ´p_1, ..., p_n´ +of types ´T_1, ..., T_n´ and that return a result of type ´U´. Method types associate to the right: ´(\mathit{Ps}\_1)(\mathit{Ps}\_2)U´ is @@ -629,13 +629,13 @@ c: (Int) (String, String) String A polymorphic method type is denoted internally as `[´\mathit{tps}\,´]´T´` where `[´\mathit{tps}\,´]` is a type parameter section -`[´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]` +`[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]` for some ´n \geq 0´ and ´T´ is a (value or method) type. This type represents named methods that -take type arguments `´S_1 , \ldots , S_n´` which +take type arguments `´S_1, ..., S_n´` which [conform](#parameterized-types) to the lower bounds -`´L_1 , \ldots , L_n´` and the upper bounds -`´U_1 , \ldots , U_n´` and that yield results of type ´T´. +`´L_1, ..., L_n´` and the upper bounds +`´U_1, ..., U_n´` and that yield results of type ´T´. ###### Example @@ -656,7 +656,7 @@ union : [A >: Nothing <: Comparable[A]] (x: Set[A], xs: Set[A]) Set[A] ### Type Constructors A _type constructor_ is represented internally much like a polymorphic method type. -`[´\pm´ ´a_1´ >: ´L_1´ <: ´U_1 , \ldots , \pm a_n´ >: ´L_n´ <: ´U_n´] ´T´` +`[´\pm´ ´a_1´ >: ´L_1´ <: ´U_1, ..., \pm a_n´ >: ´L_n´ <: ´U_n´] ´T´` represents a type that is expected by a [type constructor parameter](04-basic-declarations-and-definitions.html#type-parameters) or an [abstract type constructor binding](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases) with @@ -681,15 +681,15 @@ anonymous type `[+X] Iterable[X]`, which may be passed to the More than one values or methods are defined in the same scope with the same name, we model -An overloaded type consisting of type alternatives ´T_1 \commadots T_n (n \geq 2)´ is denoted internally ´T_1 \overload \ldots \overload T_n´. +An overloaded type consisting of type alternatives ´T_1 \commadots T_n (n \geq 2)´ is denoted internally ´T_1 \overload ... \overload T_n´. ###### Example ```scala def println(): Unit -def println(s: String): Unit = ´\ldots´ -def println(x: Float): Unit = ´\ldots´ -def println(x: Float, width: Int): Unit = ´\ldots´ -def println[A](x: A)(tostring: A => String): Unit = ´\ldots´ +def println(s: String): Unit = ... +def println(x: Float): Unit = ... +def println(x: Float, width: Int): Unit = ... +def println[A](x: A)(tostring: A => String): Unit = ... ``` define a single function `println` which has an overloaded type. @@ -703,7 +703,7 @@ println: () Unit ´\overload´ ###### Example ```scala -def f(x: T): T = ´\ldots´ +def f(x: T): T = ... val f = 0 ``` define a function `f} which has type `(x: T)T ´\overload´ Int`. @@ -722,25 +722,25 @@ These notions are defined mutually recursively as follows. 1. The set of _base types_ of a type is a set of class types, given as follows. - - The base types of a class type ´C´ with parents ´T_1 , \ldots , T_n´ are + - The base types of a class type ´C´ with parents ´T_1, ..., T_n´ are ´C´ itself, as well as the base types of the compound type `´T_1´ with ... with ´T_n´ { ´R´ }`. - The base types of an aliased type are the base types of its alias. - The base types of an abstract type are the base types of its upper bound. - The base types of a parameterized type - `´C´[´T_1 , \ldots , T_n´]` are the base types + `´C´[´T_1, ..., T_n´]` are the base types of type ´C´, where every occurrence of a type parameter ´a_i´ of ´C´ has been replaced by the corresponding parameter type ´T_i´. - The base types of a singleton type `´p´.type` are the base types of the type of ´p´. - The base types of a compound type - `´T_1´ with ´\ldots´ with ´T_n´ { ´R´ }` + `´T_1´ with ... with ´T_n´ { ´R´ }` are the _reduced union_ of the base classes of all ´T_i´'s. This means: Let the multi-set ´\mathscr{S}´ be the multi-set-union of the base types of all ´T_i´'s. If ´\mathscr{S}´ contains several type instances of the same class, say - `´S^i´#´C´[´T^i_1 , \ldots , T^i_n´]` ´(i \in I)´, then + `´S^i´#´C´[´T^i_1, ..., T^i_n´]` ´(i \in I)´, then all those instances are replaced by one of them which conforms to all others. It is an error if no such instance exists. It follows that the @@ -759,15 +759,15 @@ These notions are defined mutually recursively as follows. 1. The notion of a type ´T´ _in class ´C´ seen from some prefix type ´S´_ makes sense only if the prefix type ´S´ has a type instance of class ´C´ as a base type, say - `´S'´#´C´[´T_1 , \ldots , T_n´]`. Then we define as follows. + `´S'´#´C´[´T_1, ..., T_n´]`. Then we define as follows. - If `´S´ = ´\epsilon´.type`, then ´T´ in ´C´ seen from ´S´ is ´T´ itself. - Otherwise, if ´S´ is an existential type `´S'´ forSome { ´Q´ }`, and ´T´ in ´C´ seen from ´S'´ is ´T'´, then ´T´ in ´C´ seen from ´S´ is `´T'´ forSome {´\,Q\,´}`. - Otherwise, if ´T´ is the ´i´'th type parameter of some class ´D´, then - - If ´S´ has a base type `´D´[´U_1 , \ldots , U_n´]`, for some type - parameters `[´U_1 , \ldots , U_n´]`, then ´T´ in ´C´ seen from ´S´ + - If ´S´ has a base type `´D´[´U_1, ..., U_n´]`, for some type + parameters `[´U_1, ..., U_n´]`, then ´T´ in ´C´ seen from ´S´ is ´U_i´. - Otherwise, if ´C´ is defined in a class ´C'´, then ´T´ in ´C´ seen from ´S´ is the same as ´T´ in ´C'´ seen from ´S'´. @@ -863,19 +863,19 @@ The conformance relation ´(<:)´ is the smallest transitive relation that satis - A type projection `´T´#´t´` conforms to `´U´#´t´` if ´T´ conforms to ´U´. - A parameterized type `´T´[´T_1´, ..., ´T_n´]` conforms to `´T´[´U_1´, ..., ´U_n´]` if - the following three conditions hold for ´i \in \{ 1 , \ldots , n \}´: + the following three conditions hold for ´i \in \{ 1, ..., n \}´: 1. If the ´i´'th type parameter of ´T´ is declared covariant, then ´T_i <: U_i´. 1. If the ´i´'th type parameter of ´T´ is declared contravariant, then ´U_i <: T_i´. 1. If the ´i´'th type parameter of ´T´ is declared neither covariant nor contravariant, then ´U_i \equiv T_i´. -- A compound type `´T_1´ with ´\ldots´ with ´T_n´ {´R\,´}` conforms to +- A compound type `´T_1´ with ... with ´T_n´ {´R\,´}` conforms to each of its component types ´T_i´. -- If ´T <: U_i´ for ´i \in \{ 1 , \ldots , n \}´ and for every +- If ´T <: U_i´ for ´i \in \{ 1, ..., n \}´ and for every binding ´d´ of a type or value ´x´ in ´R´ there exists a member binding of ´x´ in ´T´ which subsumes ´d´, then ´T´ conforms to the - compound type `´U_1´ with ´\ldots´ with ´U_n´ {´R\,´}`. + compound type `´U_1´ with ... with ´U_n´ {´R\,´}`. - The existential type `´T´ forSome {´\,Q\,´}` conforms to ´U´ if its [skolemization](#existential-types) conforms to ´U´. @@ -883,25 +883,25 @@ The conformance relation ´(<:)´ is the smallest transitive relation that satis if ´T´ conforms to one of the [type instances](#existential-types) of `´U´ forSome {´\,Q\,´}`. - If - ´T_i \equiv T_i'´ for ´i \in \{ 1 , \ldots , n\}´ and ´U´ conforms to ´U'´ - then the method type ´(p_1:T_1 , \ldots , p_n:T_n) U´ conforms to - ´(p_1':T_1' , \ldots , p_n':T_n') U'´. + ´T_i \equiv T_i'´ for ´i \in \{ 1, ..., n\}´ and ´U´ conforms to ´U'´ + then the method type ´(p_1:T_1, ..., p_n:T_n) U´ conforms to + ´(p_1':T_1', ..., p_n':T_n') U'´. - The polymorphic type - ´[a_1 >: L_1 <: U_1 , \ldots , a_n >: L_n <: U_n] T´ conforms to the + ´[a_1 >: L_1 <: U_1, ..., a_n >: L_n <: U_n] T´ conforms to the polymorphic type - ´[a_1 >: L_1' <: U_1' , \ldots , a_n >: L_n' <: U_n'] T'´ if, assuming - ´L_1' <: a_1 <: U_1' , \ldots , L_n' <: a_n <: U_n'´ + ´[a_1 >: L_1' <: U_1', ..., a_n >: L_n' <: U_n'] T'´ if, assuming + ´L_1' <: a_1 <: U_1', ..., L_n' <: a_n <: U_n'´ one has ´T <: T'´ and ´L_i <: L_i'´ and ´U_i' <: U_i´ - for ´i \in \{ 1 , \ldots , n \}´. + for ´i \in \{ 1, ..., n \}´. - Type constructors ´T´ and ´T'´ follow a similar discipline. We characterize ´T´ and ´T'´ by their type parameter clauses - ´[a_1 , \ldots , a_n]´ and - ´[a_1' , \ldots , a_n']´, where an ´a_i´ or ´a_i'´ may include a variance + ´[a_1, ..., a_n]´ and + ´[a_1', ..., a_n']´, where an ´a_i´ or ´a_i'´ may include a variance annotation, a higher-order type parameter clause, and bounds. Then, ´T´ - conforms to ´T'´ if any list ´[t_1 , \ldots , t_n]´ -- with declared + conforms to ´T'´ if any list ´[t_1, ..., t_n]´ -- with declared variances, bounds and higher-order type parameter clauses -- of valid type arguments for ´T'´ is also a valid list of type arguments for ´T´ and - ´T[t_1 , \ldots , t_n] <: T'[t_1 , \ldots , t_n]´. Note that this entails + ´T[t_1, ..., t_n] <: T'[t_1, ..., t_n]´. Note that this entails that: - The bounds on ´a_i´ must be weaker than the corresponding bounds declared for ´a'_i´. @@ -1027,11 +1027,11 @@ A type is _volatile_ if it falls into one of four categories: A compound type `´T_1´ with ... with ´T_n´ {´R\,´}` is volatile if one of the following three conditions hold. -1. One of ´T_2 , \ldots , T_n´ is a type parameter or abstract type, or +1. One of ´T_2, ..., T_n´ is a type parameter or abstract type, or 1. ´T_1´ is an abstract type and either the refinement ´R´ or a type ´T_j´ for ´j > 1´ contributes an abstract member to the compound type, or -1. one of ´T_1 , \ldots , T_n´ is a singleton type. +1. one of ´T_1, ..., T_n´ is a singleton type. Here, a type ´S´ _contributes an abstract member_ to a type ´T´ if ´S´ contains an abstract member that is also a member of ´T´. @@ -1059,18 +1059,18 @@ The erasure mapping is defined as follows. - The erasure of an abstract type is the erasure of its upper bound. - The erasure of the parameterized type `scala.Array´[T_1]´` is `scala.Array´[|T_1|]´`. -- The erasure of every other parameterized type ´T[T_1 , \ldots , T_n]´ is ´|T|´. +- The erasure of every other parameterized type ´T[T_1, ..., T_n]´ is ´|T|´. - The erasure of a singleton type `´p´.type` is the erasure of the type of ´p´. - The erasure of a type projection `´T´#´x´` is `|´T´|#´x´`. - The erasure of a compound type - `´T_1´ with ´\ldots´ with ´T_n´ {´R\,´}` is the erasure of the intersection - dominator of ´T_1 , \ldots , T_n´. + `´T_1´ with ... with ´T_n´ {´R\,´}` is the erasure of the intersection + dominator of ´T_1, ..., T_n´. - The erasure of an existential type `´T´ forSome {´\,Q\,´}` is ´|T|´. -The _intersection dominator_ of a list of types ´T_1 , \ldots , T_n´ is computed +The _intersection dominator_ of a list of types ´T_1, ..., T_n´ is computed as follows. -Let ´T_{i_1} , \ldots , T_{i_m}´ be the subsequence of types ´T_i´ +Let ´T_{i_1}, ..., T_{i_m}´ be the subsequence of types ´T_i´ which are not supertypes of some other type ´T_j´. If this subsequence contains a type designator ´T_c´ that refers to a class which is not a trait, diff --git a/docs/_spec/04-basic-declarations-and-definitions.md b/docs/_spec/04-basic-declarations-and-definitions.md index acaf2491d99b..0dd311e558b8 100644 --- a/docs/_spec/04-basic-declarations-and-definitions.md +++ b/docs/_spec/04-basic-declarations-and-definitions.md @@ -32,7 +32,7 @@ associate term names with types. The scope of a name introduced by a declaration or definition is the whole statement sequence containing the binding. However, there is a restriction on forward references in blocks: In a statement sequence -´s_1 \ldots s_n´ making up a block, if a simple name in ´s_i´ refers +´s_1 ... s_n´ making up a block, if a simple name in ´s_i´ refers to an entity defined by ´s_j´ where ´j \geq i´, then for all ´s_k´ between and including ´s_i´ and ´s_j´, @@ -138,12 +138,12 @@ as left-hand side. If ´p´ is some pattern other than a simple name or a name followed by a colon and a type, then the value definition `val ´p´ = ´e´` is expanded as follows: -1. If the pattern ´p´ has bound variables ´x_1 , \ldots , x_n´, where ´n > 1´: +1. If the pattern ´p´ has bound variables ´x_1, ..., x_n´, where ´n > 1´: ```scala -val ´\$x´ = ´e´ match {case ´p´ => (´x_1 , \ldots , x_n´)} +val ´\$x´ = ´e´ match {case ´p´ => (´x_1, ..., x_n´)} val ´x_1´ = ´\$x´._1 -´\ldots´ +... val ´x_n´ = ´\$x´._n ``` @@ -184,11 +184,11 @@ val xs = x´\$´._2 The name of any declared or defined value may not end in `_=`. -A value declaration `val ´x_1 , \ldots , x_n´: ´T´` is a shorthand for the +A value declaration `val ´x_1, ..., x_n´: ´T´` is a shorthand for the sequence of value declarations `val ´x_1´: ´T´; ...; val ´x_n´: ´T´`. -A value definition `val ´p_1 , \ldots , p_n´ = ´e´` is a shorthand for the +A value definition `val ´p_1, ..., p_n´ = ´e´` is a shorthand for the sequence of value definitions `val ´p_1´ = ´e´; ...; val ´p_n´ = ´e´`. -A value definition `val ´p_1 , \ldots , p_n: T´ = ´e´` is a shorthand for the +A value definition `val ´p_1, ..., p_n: T´ = ´e´` is a shorthand for the sequence of value definitions `val ´p_1: T´ = ´e´; ...; val ´p_n: T´ = ´e´`. ## Variable Declarations and Definitions @@ -283,11 +283,11 @@ d.hours = 8; d.minutes = 30; d.seconds = 0 d.hours = 25 // throws a DateError exception ``` -A variable declaration `var ´x_1 , \ldots , x_n´: ´T´` is a shorthand for the +A variable declaration `var ´x_1, ..., x_n´: ´T´` is a shorthand for the sequence of variable declarations `var ´x_1´: ´T´; ...; var ´x_n´: ´T´`. -A variable definition `var ´x_1 , \ldots , x_n´ = ´e´` is a shorthand for the +A variable definition `var ´x_1, ..., x_n´ = ´e´` is a shorthand for the sequence of variable definitions `var ´x_1´ = ´e´; ...; var ´x_n´ = ´e´`. -A variable definition `var ´x_1 , \ldots , x_n: T´ = ´e´` is a shorthand for +A variable definition `var ´x_1, ..., x_n: T´ = ´e´` is a shorthand for the sequence of variable definitions `var ´x_1: T´ = ´e´; ...; var ´x_n: T´ = ´e´`. @@ -407,7 +407,7 @@ definitions with lower bounds `>: ´L´` and upper bounds is deferred to [here](07-implicits.html#context-bounds-and-view-bounds). The most general form of a proper type parameter is -`´@a_1 \ldots @a_n´ ´\pm´ ´t´ >: ´L´ <: ´U´`. +`´@a_1 ... @a_n´ ´\pm´ ´t´ >: ´L´ <: ´U´`. Here, ´L´, and ´U´ are lower and upper bounds that constrain possible type arguments for the parameter. It is a compile-time error if ´L´ does not conform to ´U´. ´\pm´ is a _variance_, i.e. an optional prefix of either `+`, or @@ -426,7 +426,7 @@ TODO: this is a pretty awkward description of scoping and distinctness of binder The names of all type parameters must be pairwise different in their enclosing type parameter clause. The scope of a type parameter includes in each case the whole type parameter clause. Therefore it is possible that a type parameter appears as part of its own bounds or the bounds of other type parameters in the same clause. However, a type parameter may not be bounded directly or indirectly by itself. -A type constructor parameter adds a nested type parameter clause to the type parameter. The most general form of a type constructor parameter is `´@a_1 \ldots @a_n \pm t[\mathit{tps}\,]´ >: ´L´ <: ´U´`. +A type constructor parameter adds a nested type parameter clause to the type parameter. The most general form of a type constructor parameter is `´@a_1 ... @a_n \pm t[\mathit{tps}\,]´ >: ´L´ <: ´U´`. The above scoping restrictions are generalized to the case of nested type parameter clauses, which declare higher-order type parameters. Higher-order type parameters (the type parameters of a type parameter ´t´) are only visible in their immediately surrounding parameter clause (possibly including clauses at a deeper nesting level) and in the bounds of ´t´. Therefore, their names must only be pairwise different from the names of other visible parameters. Since the names of higher-order type parameters are thus often irrelevant, they may be denoted with a `‘_’`, which is nowhere visible. @@ -491,11 +491,11 @@ changes at the following constructs. - The type of a mutable variable is always in invariant position. - The right-hand side of a type alias is always in invariant position. - The prefix ´S´ of a type selection `´S´#´T´` is always in invariant position. -- For a type argument ´T´ of a type `´S´[´\ldots T \ldots´ ]`: If the +- For a type argument ´T´ of a type `´S´[´... T ...´ ]`: If the corresponding type parameter is invariant, then ´T´ is in invariant position. If the corresponding type parameter is contravariant, the variance position of ´T´ is the opposite of - the variance position of the enclosing type `´S´[´\ldots T \ldots´ ]`. + the variance position of the enclosing type `´S´[´... T ...´ ]`. @@ -602,7 +602,7 @@ signature and ´T´ is its result type. A _function definition_ i.e. an expression which defines the function's result. A parameter signature consists of an optional type parameter clause `[´\mathit{tps}\,´]`, followed by zero or more value parameter clauses -`(´\mathit{ps}_1´)´\ldots´(´\mathit{ps}_n´)`. Such a declaration or definition +`(´\mathit{ps}_1´)...(´\mathit{ps}_n´)`. Such a declaration or definition introduces a value with a (possibly polymorphic) method type whose parameter types and result type are as given. @@ -635,7 +635,7 @@ For every parameter ´p_{i,j}´ with a default argument a method named expression. Here, ´n´ denotes the parameter's position in the method declaration. These methods are parametrized by the type parameter clause `[´\mathit{tps}\,´]` and all value parameter clauses -`(´\mathit{ps}_1´)´\ldots´(´\mathit{ps}_{i-1}´)` preceding ´p_{i,j}´. +`(´\mathit{ps}_1´)...(´\mathit{ps}_{i-1}´)` preceding ´p_{i,j}´. The `´f\$´default´\$´n` methods are inaccessible for user programs. ###### Example @@ -717,17 +717,17 @@ _repeated_ parameter inside the method is then the sequence type `scala.Seq[´T´]`. Methods with repeated parameters `´T´*` take a variable number of arguments of type ´T´. That is, if a method ´m´ with type -`(´p_1:T_1 , \ldots , p_n:T_n, p_s:S´*)´U´` is applied to arguments -´(e_1 , \ldots , e_k)´ where ´k \geq n´, then ´m´ is taken in that application -to have type ´(p_1:T_1 , \ldots , p_n:T_n, p_s:S , \ldots , p_{s'}:S)U´, with +`(´p_1:T_1, ..., p_n:T_n, p_s:S´*)´U´` is applied to arguments +´(e_1, ..., e_k)´ where ´k \geq n´, then ´m´ is taken in that application +to have type ´(p_1:T_1, ..., p_n:T_n, p_s:S, ..., p_{s'}:S)U´, with ´k - n´ occurrences of type ´S´ where any parameter names beyond ´p_s´ are fresh. The only exception to this rule is if the last argument is marked to be a _sequence argument_ via a `_*` type annotation. If ´m´ above is applied to arguments -`(´e_1 , \ldots , e_n, e'´: _*)`, then the type of ´m´ in +`(´e_1, ..., e_n, e'´: _*)`, then the type of ´m´ in that application is taken to be -`(´p_1:T_1, \ldots , p_n:T_n,p_{s}:´scala.Seq[´S´])`. +`(´p_1:T_1, ... , p_n:T_n,p_{s}:´scala.Seq[´S´])`. It is not allowed to define any default arguments in a parameter section with a repeated parameter. @@ -875,7 +875,7 @@ _importable_ if it is [accessible](05-classes-and-objects.html#modifiers). The most general form of an import expression is a list of _import selectors_ ```scala -{ ´x_1´ => ´y_1 , \ldots , x_n´ => ´y_n´, _ } +{ ´x_1´ => ´y_1, ..., x_n´ => ´y_n´, _ } ``` for ´n \geq 0´, where the final wildcard `‘_’` may be absent. It @@ -883,7 +883,7 @@ makes available each importable member `´p´.´x_i´` under the unqualified nam ´y_i´. I.e. every import selector `´x_i´ => ´y_i´` renames `´p´.´x_i´` to ´y_i´. If a final wildcard is present, all importable members ´z´ of -´p´ other than `´x_1 , \ldots , x_n,y_1 , \ldots , y_n´` are also made available +´p´ other than `´x_1, ..., x_n,y_1, ..., y_n´` are also made available under their own unqualified names. Import selectors work in the same way for type and term members. For @@ -918,9 +918,9 @@ i.e. it makes available without qualification all members of ´p´ (this is analogous to `import ´p´.*` in Java). An import clause with multiple import expressions -`import ´p_1´.´I_1 , \ldots , p_n´.´I_n´` is interpreted as a +`import ´p_1´.´I_1, ..., p_n´.´I_n´` is interpreted as a sequence of import clauses -`import ´p_1´.´I_1´; ´\ldots´; import ´p_n´.´I_n´`. +`import ´p_1´.´I_1´; ...; import ´p_n´.´I_n´`. ###### Example Consider the object definition: diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index 5c3a74e608a2..88ca4de54692 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -31,10 +31,10 @@ A _template_ defines the type signature, behavior and initial state of a trait or class of objects or of a single object. Templates form part of instance creation expressions, class definitions, and object definitions. A template -`´sc´ with ´mt_1´ with ´\ldots´ with ´mt_n´ { ´\mathit{stats}´ }` +`´sc´ with ´mt_1´ with ... with ´mt_n´ { ´\mathit{stats}´ }` consists of a constructor invocation ´sc´ which defines the template's _superclass_, trait references -`´mt_1 , \ldots , mt_n´` ´(n \geq 0)´, which define the +`´mt_1, ..., mt_n´` ´(n \geq 0)´, which define the template's _traits_, and a statement sequence ´\mathit{stats}´ which contains initialization code and additional member definitions for the template. @@ -43,7 +43,7 @@ Each trait reference ´mt_i´ must denote a [trait](#traits). By contrast, the superclass constructor ´sc´ normally refers to a class which is not a trait. It is possible to write a list of parents that starts with a trait reference, e.g. -`´mt_1´ with ´\ldots´ with ´mt_n´`. In that case the list +`´mt_1´ with ... with ´mt_n´`. In that case the list of parents is implicitly extended to include the supertype of ´mt_1´ as the first parent type. The new supertype must have at least one constructor that does not take parameters. In the following, we will @@ -53,7 +53,7 @@ constructor, not a trait reference. The list of parents of a template must be well-formed. This means that the class denoted by the superclass constructor ´sc´ must be a -subclass of the superclasses of all the traits ´mt_1 , \ldots , mt_n´. +subclass of the superclasses of all the traits ´mt_1, ..., mt_n´. In other words, the non-trait classes inherited by a template form a chain in the inheritance hierarchy which starts with the template's superclass. @@ -155,12 +155,12 @@ Constr ::= AnnotType {‘(’ [Exprs] ‘)’} Constructor invocations define the type, members, and initial state of objects created by an instance creation expression, or of parts of an object's definition which are inherited by a class or object -definition. A constructor invocation is a function application -`´x´.´c´[´\mathit{targs}´](´\mathit{args}_1´)´\ldots´(´\mathit{args}_n´)`, where ´x´ is a +definition. A constructor invocation is a method application +`´x´.´c´[´\mathit{targs}´](´\mathit{args}_1´)...(´\mathit{args}_n´)`, where ´x´ is a [stable identifier](03-types.html#paths), ´c´ is a type name which either designates a class or defines an alias type for one, ´\mathit{targs}´ is a type argument -list, ´\mathit{args}_1 , \ldots , \mathit{args}_n´ are argument lists, and there is a -constructor of that class which is [applicable](06-expressions.html#function-applications) +list, ´\mathit{args}_1, ..., \mathit{args}_n´ are argument lists, and there is a +constructor of that class which is [applicable](06-expressions.html#method-applications) to the given arguments. If the constructor invocation uses named or default arguments, it is transformed into a block expression using the same transformation as described [here](sec:named-default). @@ -172,11 +172,11 @@ using [local type inference](06-expressions.html#local-type-inference). If no ex arguments are given, an empty list `()` is implicitly supplied. An evaluation of a constructor invocation -`´x´.´c´[´\mathit{targs}´](´\mathit{args}_1´)´\ldots´(´\mathit{args}_n´)` +`´x´.´c´[´\mathit{targs}´](´\mathit{args}_1´)...(´\mathit{args}_n´)` consists of the following steps: - First, the prefix ´x´ is evaluated. -- Then, the arguments ´\mathit{args}_1 , \ldots , \mathit{args}_n´ are evaluated from +- Then, the arguments ´\mathit{args}_1, ..., \mathit{args}_n´ are evaluated from left to right. - Finally, the class being constructed is initialized by evaluating the template of the class referred to by ´c´. @@ -193,7 +193,7 @@ Let ´C´ be a class with template ´C_1´ with ... with ´C_n´ { ´\mathit{stats}´ }`. The _linearization_ of ´C´, ´\mathcal{L}(C)´ is defined as follows: $$ -\mathcal{L}(C) = C, \mathcal{L}(C_n) \; \vec{+} \; \ldots \; \vec{+} \; \mathcal{L}(C_1) +\mathcal{L}(C) = C, \mathcal{L}(C_n) \; \vec{+} \; ... \; \vec{+} \; \mathcal{L}(C_1) $$ Here ´\vec{+}´ denotes concatenation where elements of the right operand @@ -246,7 +246,7 @@ which is not a suffix of the linearization of `Iter`. ### Class Members -A class ´C´ defined by a template `´C_1´ with ´\ldots´ with ´C_n´ { ´\mathit{stats}´ }` +A class ´C´ defined by a template `´C_1´ with ... with ´C_n´ { ´\mathit{stats}´ }` can define members in its statement sequence ´\mathit{stats}´ and can inherit members from all parent classes. Scala adopts Java and C\#'s conventions for static overloading of @@ -694,7 +694,7 @@ ClassTemplateOpt ::= ‘extends’ ClassTemplate | [[‘extends’] TemplateBo The most general form of class definition is ```scala -class ´c´[´\mathit{tps}\,´] ´as´ ´m´(´\mathit{ps}_1´)´\ldots´(´\mathit{ps}_n´) extends ´t´ ´\quad(n \geq 0)´. +class ´c´[´\mathit{tps}\,´] ´as´ ´m´(´\mathit{ps}_1´)...(´\mathit{ps}_n´) extends ´t´ ´\quad(n \geq 0)´. ``` Here, @@ -714,7 +714,7 @@ Here, - ´m´ is an [access modifier](#modifiers) such as `private` or `protected`, possibly with a qualification. If such an access modifier is given it applies to the primary constructor of the class. - - ´(\mathit{ps}\_1)\ldots(\mathit{ps}\_n)´ are formal value parameter clauses for + - ´(\mathit{ps}\_1)...(\mathit{ps}\_n)´ are formal value parameter clauses for the _primary constructor_ of the class. The scope of a formal value parameter includes all subsequent parameter sections and the template ´t´. However, a formal value parameter may not form part of the types of any of the parent classes or members of the class template ´t´. @@ -737,12 +737,12 @@ Here, - ´t´ is a [template](#templates) of the form ```scala - ´sc´ with ´mt_1´ with ´\ldots´ with ´mt_m´ { ´\mathit{stats}´ } // ´m \geq 0´ + ´sc´ with ´mt_1´ with ... with ´mt_m´ { ´\mathit{stats}´ } // ´m \geq 0´ ``` which defines the base classes, behavior and initial state of objects of the class. The extends clause - `extends ´sc´ with ´mt_1´ with ´\ldots´ with ´mt_m´` + `extends ´sc´ with ´mt_1´ with ... with ´mt_m´` can be omitted, in which case `extends scala.AnyRef` is assumed. The class body `{ ´\mathit{stats}´ }` may also be omitted, in which case the empty body @@ -789,14 +789,14 @@ SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} A class may have additional constructors besides the primary constructor. These are defined by constructor definitions of the form -`def this(´\mathit{ps}_1´)´\ldots´(´\mathit{ps}_n´) = ´e´`. Such a +`def this(´\mathit{ps}_1´)...(´\mathit{ps}_n´) = ´e´`. Such a definition introduces an additional constructor for the enclosing class, with parameters as given in the formal parameter lists ´\mathit{ps}_1 -, \ldots , \mathit{ps}_n´, and whose evaluation is defined by the constructor +, ..., \mathit{ps}_n´, and whose evaluation is defined by the constructor expression ´e´. The scope of each formal parameter is the subsequent parameter sections and the constructor expression ´e´. A constructor expression is either a self constructor -invocation `this(´\mathit{args}_1´)´\ldots´(´\mathit{args}_n´)` or a block +invocation `this(´\mathit{args}_1´)...(´\mathit{args}_n´)` or a block which begins with a self constructor invocation. The self constructor invocation must construct a generic instance of the class. I.e. if the class in question has name ´C´ and type parameters @@ -861,16 +861,16 @@ implicitly added to such a parameter, unless the parameter already carries a `val` or `var` modifier. Hence, an accessor definition for the parameter is [generated](#class-definitions). -A case class definition of `´c´[´\mathit{tps}\,´](´\mathit{ps}_1\,´)´\ldots´(´\mathit{ps}_n´)` with type +A case class definition of `´c´[´\mathit{tps}\,´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` with type parameters ´\mathit{tps}´ and value parameters ´\mathit{ps}´ implies the definition of a companion object, which serves as an [extractor object](08-pattern-matching.html#extractor-patterns). It has the following shape: ```scala object ´c´ { - def apply[´\mathit{tps}\,´](´\mathit{ps}_1\,´)´\ldots´(´\mathit{ps}_n´): ´c´[´\mathit{tps}\,´] = new ´c´[´\mathit{Ts}\,´](´\mathit{xs}_1\,´)´\ldots´(´\mathit{xs}_n´) + def apply[´\mathit{tps}\,´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´): ´c´[´\mathit{tps}\,´] = new ´c´[´\mathit{Ts}\,´](´\mathit{xs}_1\,´)...(´\mathit{xs}_n´) def unapply[´\mathit{tps}\,´](´x´: ´c´[´\mathit{tps}\,´]) = if (x eq null) scala.None - else scala.Some(´x.\mathit{xs}_{11}, \ldots , x.\mathit{xs}_{1k}´) + else scala.Some(´x.\mathit{xs}_{11}, ... , x.\mathit{xs}_{1k}´) } ``` @@ -878,7 +878,7 @@ Here, ´\mathit{Ts}´ stands for the vector of types defined in the type parameter section ´\mathit{tps}´, each ´\mathit{xs}\_i´ denotes the parameter names of the parameter section ´\mathit{ps}\_i´, and -´\mathit{xs}\_{11}, \ldots , \mathit{xs}\_{1k}´ denote the names of all parameters +´\mathit{xs}\_{11}, ... , \mathit{xs}\_{1k}´ denote the names of all parameters in the first parameter section ´\mathit{xs}\_1´. If a type parameter section is missing in the class, it is also missing in the `apply` and `unapply` methods. @@ -905,7 +905,7 @@ class already has a member (directly defined or inherited) with that name, or th class has a repeated parameter. The method is defined as follows: ```scala -def copy[´\mathit{tps}\,´](´\mathit{ps}'_1\,´)´\ldots´(´\mathit{ps}'_n´): ´c´[´\mathit{tps}\,´] = new ´c´[´\mathit{Ts}\,´](´\mathit{xs}_1\,´)´\ldots´(´\mathit{xs}_n´) +def copy[´\mathit{tps}\,´](´\mathit{ps}'_1\,´)...(´\mathit{ps}'_n´): ´c´[´\mathit{tps}\,´] = new ´c´[´\mathit{Ts}\,´](´\mathit{xs}_1\,´)...(´\mathit{xs}_n´) ``` Again, `´\mathit{Ts}´` stands for the vector of types defined in the type parameter section `´\mathit{tps}´` @@ -1090,11 +1090,11 @@ most general form is ´t´ is a [template](#templates) of the form ```scala -´sc´ with ´mt_1´ with ´\ldots´ with ´mt_n´ { ´\mathit{stats}´ } +´sc´ with ´mt_1´ with ... with ´mt_n´ { ´\mathit{stats}´ } ``` which defines the base classes, behavior and initial state of ´m´. -The extends clause `extends ´sc´ with ´mt_1´ with ´\ldots´ with ´mt_n´` +The extends clause `extends ´sc´ with ´mt_1´ with ... with ´mt_n´` can be omitted, in which case `extends scala.AnyRef` is assumed. The class body `{ ´\mathit{stats}´ }` may also be omitted, in which case the empty body @@ -1105,7 +1105,7 @@ conforming to the template ´t´. It is roughly equivalent to the following definition of a lazy value: ```scala -lazy val ´m´ = new ´sc´ with ´mt_1´ with ´\ldots´ with ´mt_n´ { this: ´m.type´ => ´\mathit{stats}´ } +lazy val ´m´ = new ´sc´ with ´mt_1´ with ... with ´mt_n´ { this: ´m.type´ => ´\mathit{stats}´ } ``` Note that the value defined by an object definition is instantiated diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index f574d7ad2469..36c31af8969c 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -62,12 +62,12 @@ expression: If the type of an expression would be an existential type [skolemization](03-types.html#existential-types) of ´T´. Skolemization is reversed by type packing. Assume an expression ´e´ of -type ´T´ and let ´t_1[\mathit{tps}\_1] >: L_1 <: U_1 , \ldots , t_n[\mathit{tps}\_n] >: L_n <: U_n´ be +type ´T´ and let ´t_1[\mathit{tps}\_1] >: L_1 <: U_1, ..., t_n[\mathit{tps}\_n] >: L_n <: U_n´ be all the type variables created by skolemization of some part of ´e´ which are free in ´T´. Then the _packed type_ of ´e´ is ```scala -´T´ forSome { type ´t_1[\mathit{tps}\_1] >: L_1 <: U_1´; ´\ldots´; type ´t_n[\mathit{tps}\_n] >: L_n <: U_n´ }. +´T´ forSome { type ´t_1[\mathit{tps}\_1] >: L_1 <: U_1´; ...; type ´t_n[\mathit{tps}\_n] >: L_n <: U_n´ }. ``` ## Literals @@ -246,9 +246,9 @@ ArgumentExprs ::= ‘(’ [Exprs] ‘)’ Exprs ::= Expr {‘,’ Expr} ``` -An application `´f(e_1 , \ldots , e_m)´` applies the function `´f´` to the argument expressions `´e_1, \ldots , e_m´`. For this expression to be well-typed, the function must be *applicable* to its arguments, which is defined next by case analysis on ´f´'s type. +An application `´f(e_1, ..., e_m)´` applies the function `´f´` to the argument expressions `´e_1, ..., e_m´`. For this expression to be well-typed, the function must be *applicable* to its arguments, which is defined next by case analysis on ´f´'s type. -If ´f´ has a method type `(´p_1´:´T_1 , \ldots , p_n´:´T_n´)´U´`, each argument expression ´e_i´ is typed with the corresponding parameter type ´T_i´ as expected type. Let ´S_i´ be the type of argument ´e_i´ ´(i = 1 , \ldots , m)´. The method ´f´ must be _applicable_ to its arguments ´e_1, \ldots , e_n´ of types ´S_1 , \ldots , S_n´. We say that an argument expression ´e_i´ is a _named_ argument if it has the form `´x_i=e'_i´` and `´x_i´` is one of the parameter names `´p_1, \ldots, p_n´`. +If ´f´ has a method type `(´p_1´:´T_1, ..., p_n´:´T_n´)´U´`, each argument expression ´e_i´ is typed with the corresponding parameter type ´T_i´ as expected type. Let ´S_i´ be the type of argument ´e_i´ ´(i = 1, ..., m)´. The method ´f´ must be _applicable_ to its arguments ´e_1, ..., e_n´ of types ´S_1, ..., S_n´. We say that an argument expression ´e_i´ is a _named_ argument if it has the form `´x_i=e'_i´` and `´x_i´` is one of the parameter names `´p_1, ..., p_n´`. Once the types ´S_i´ have been determined, the method ´f´ of the above method type is said to be applicable if all of the following conditions hold: - for every named argument ´p_j=e_i'´ the type ´S_i´ is [compatible](03-types.html#compatibility) with the parameter type ´T_j´; @@ -258,12 +258,12 @@ Once the types ´S_i´ have been determined, the method ´f´ of the above metho If ´f´ is a polymorphic method, [local type inference](#local-type-inference) is used to instantiate ´f´'s type parameters. The polymorphic method is applicable if type inference can determine type arguments so that the instantiated method is applicable. -If ´f´ has some value type, the application is taken to be equivalent to `´f´.apply(´e_1 , \ldots , e_m´)`, +If ´f´ has some value type, the application is taken to be equivalent to `´f´.apply(´e_1, ..., e_m´)`, i.e. the application of an `apply` method defined by ´f´. The value `´f´` is applicable to the given arguments if `´f´.apply` is applicable. -The application `´f´(´e_1 , \ldots , e_n´)` evaluates ´f´ and then each argument -´e_1 , \ldots , e_n´ from left to right, except for arguments that correspond to +The application `´f´(´e_1, ..., e_n´)` evaluates ´f´ and then each argument +´e_1, ..., e_n´ from left to right, except for arguments that correspond to a by-name parameter (see below). Each argument expression is converted to the type of its corresponding formal parameter. After that, the application is rewritten to the function's right hand side, with actual arguments substituted @@ -338,7 +338,7 @@ If an application is to use named arguments ´p = e´ or default arguments, the following conditions must hold. - For every named argument ´p_i = e_i´ which appears left of a positional argument - in the argument list ´e_1 \ldots e_m´, the argument position ´i´ coincides with + in the argument list ´e_1 ... e_m´, the argument position ´i´ coincides with the position of parameter ´p_i´ in the parameter list of the applied method. - The names ´x_i´ of all named arguments are pairwise distinct and no named argument defines a parameter which is already specified by a @@ -367,16 +367,16 @@ the form ```scala { val q = ´p´ val ´x_1´ = expr´_1´ - ´\ldots´ + ... val ´x_k´ = expr´_k´ - q.´m´[´\mathit{targs}´](´\mathit{args}_1´)´, \ldots ,´(´\mathit{args}_l´) + q.´m´[´\mathit{targs}´](´\mathit{args}_1´), ...,(´\mathit{args}_l´) } ``` -where every argument in ´(\mathit{args}\_1) , \ldots , (\mathit{args}\_l)´ is a reference to -one of the values ´x_1 , \ldots , x_k´. To integrate the current application +where every argument in ´(\mathit{args}\_1), ..., (\mathit{args}\_l)´ is a reference to +one of the values ´x_1, ..., x_k´. To integrate the current application into the block, first a value definition using a fresh name ´y_i´ is created -for every argument in ´e_1 , \ldots , e_m´, which is initialised to ´e_i´ for +for every argument in ´e_1, ..., e_m´, which is initialised to ´e_i´ for positional arguments and to ´e'_i´ for named arguments of the form `´x_i=e'_i´`. Then, for every parameter which is not specified by the argument list, a value definition using a fresh name ´z_i´ is created, @@ -386,33 +386,33 @@ this parameter. Let ´\mathit{args}´ be a permutation of the generated names ´y_i´ and ´z_i´ such such that the position of each name matches the position of its corresponding -parameter in the method type `(´p_1:T_1 , \ldots , p_n:T_n´)´U´`. +parameter in the method type `(´p_1:T_1, ..., p_n:T_n´)´U´`. The final result of the transformation is a block of the form ```scala { val q = ´p´ val ´x_1´ = expr´_1´ - ´\ldots´ + ... val ´x_l´ = expr´_k´ val ´y_1´ = ´e_1´ - ´\ldots´ + ... val ´y_m´ = ´e_m´ - val ´z_1´ = ´q.m\$default\$i[\mathit{targs}](\mathit{args}_1), \ldots ,(\mathit{args}_l)´ - ´\ldots´ - val ´z_d´ = ´q.m\$default\$j[\mathit{targs}](\mathit{args}_1), \ldots ,(\mathit{args}_l)´ - q.´m´[´\mathit{targs}´](´\mathit{args}_1´)´, \ldots ,´(´\mathit{args}_l´)(´\mathit{args}´) + val ´z_1´ = ´q.m\$default\$i[\mathit{targs}](\mathit{args}_1), ..., (\mathit{args}_l)´ + ... + val ´z_d´ = ´q.m\$default\$j[\mathit{targs}](\mathit{args}_1), ..., (\mathit{args}_l)´ + q.´m´[´\mathit{targs}´](´\mathit{args}_1´), ..., (´\mathit{args}_l´)(´\mathit{args}´) } ``` ### Signature Polymorphic Methods -For invocations of signature polymorphic methods of the target platform `´f´(´e_1 , \ldots , e_m´)`, -the invoked method has a different method type `(´p_1´:´T_1 , \ldots , p_n´:´T_n´)´U´` at each call -site. The parameter types `´T_ , \ldots , T_n´` are the types of the argument expressions -`´e_1 , \ldots , e_m´`. If the declared return type `´R´` of the signature polymorphic method is +For invocations of signature polymorphic methods of the target platform `´f´(´e_1, ..., e_m´)`, +the invoked method has a different method type `(´p_1´:´T_1, ..., p_n´:´T_n´)´U´` at each call +site. The parameter types `´T_, ..., T_n´` are the types of the argument expressions +`´e_1, ..., e_m´`. If the declared return type `´R´` of the signature polymorphic method is any type other than `scala.AnyRef`, then the return type `´U´` is `´R´`. Otherwise, `´U´` is the expected type at the call site. If the expected type is undefined then -`´U´` is `scala.AnyRef`. The parameter names `´p_1 , \ldots , p_n´` are fresh. +`´U´` is `scala.AnyRef`. The parameter names `´p_1, ..., p_n´` are fresh. ###### Note @@ -456,19 +456,19 @@ because otherwise the underscore would be considered part of the name. SimpleExpr ::= SimpleExpr TypeArgs ``` -A _type application_ `´e´[´T_1 , \ldots , T_n´]` instantiates +A _type application_ `´e´[´T_1, ..., T_n´]` instantiates a polymorphic value ´e´ of type -`[´a_1´ >: ´L_1´ <: ´U_1, \ldots , a_n´ >: ´L_n´ <: ´U_n´]´S´` +`[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´S´` with argument types -`´T_1 , \ldots , T_n´`. Every argument type ´T_i´ must obey +`´T_1, ..., T_n´`. Every argument type ´T_i´ must obey the corresponding bounds ´L_i´ and ´U_i´. That is, for each ´i = 1 -, \ldots , n´, we must have ´\sigma L_i <: T_i <: \sigma -U_i´, where ´\sigma´ is the substitution ´[a_1 := T_1 , \ldots , a_n +, ..., n´, we must have ´\sigma L_i <: T_i <: \sigma +U_i´, where ´\sigma´ is the substitution ´[a_1 := T_1, ..., a_n := T_n]´. The type of the application is ´\sigma S´. If the function part ´e´ is of some value type, the type application is taken to be equivalent to -`´e´.apply[´T_1 , \ldots ,´ T´_n´]`, i.e. the application of an `apply` method defined by +`´e´.apply[´T_1 , ...,´ T´_n´]`, i.e. the application of an `apply` method defined by ´e´. Type applications can be omitted if @@ -482,9 +482,9 @@ and the expected result type. SimpleExpr ::= ‘(’ [Exprs] ‘)’ ``` -A _tuple expression_ `(´e_1 , \ldots , e_n´)` is an alias +A _tuple expression_ `(´e_1´, ..., ´e_n´)` is an alias for the class instance creation -`scala.Tuple´n´(´e_1 , \ldots , e_n´)`, where ´n \geq 2´. +`scala.Tuple´n´(´e_1´, ..., ´e_n´)`, where ´n \geq 2´. The empty tuple `()` is the unique value of type `scala.Unit`. @@ -562,8 +562,8 @@ BlockExpr ::= ‘{’ CaseClauses ‘}’ Block ::= BlockStat {semi BlockStat} [ResultExpr] ``` -A _block expression_ `{´s_1´; ´\ldots´; ´s_n´; ´e\,´}` is -constructed from a sequence of block statements ´s_1 , \ldots , s_n´ +A _block expression_ `{´s_1´; ...; ´s_n´; ´e\,´}` is +constructed from a sequence of block statements ´s_1, ..., s_n´ and a final expression ´e´. The statement sequence may not contain two definitions or declarations that bind the same name in the same namespace. The final expression can be omitted, in which @@ -573,11 +573,11 @@ The expected type of the final expression ´e´ is the expected type of the block. The expected type of all preceding statements is undefined. -The type of a block `´s_1´; ´\ldots´; ´s_n´; ´e´` is +The type of a block `´s_1´; ...; ´s_n´; ´e´` is `´T´ forSome {´\,Q\,´}`, where ´T´ is the type of ´e´ and ´Q´ contains [existential clauses](03-types.html#existential-types) for every value or type name which is free in ´T´ -and which is defined locally in one of the statements ´s_1 , \ldots , s_n´. +and which is defined locally in one of the statements ´s_1, ..., s_n´. We say the existential clause _binds_ the occurrence of the value or type name. Specifically, @@ -599,7 +599,7 @@ Evaluation of the block entails evaluation of its statement sequence, followed by an evaluation of the final expression ´e´, which defines the result of the block. -A block expression `{´c_1´; ´\ldots´; ´c_n´}` where ´s_1 , \ldots , s_n´ are +A block expression `{´c_1´; ...; ´c_n´}` where ´s_1, ..., s_n´ are case clauses forms a [pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions). ###### Example @@ -696,22 +696,22 @@ parts of an expression as follows. expression, then operators with higher precedence bind more closely than operators with lower precedence. - If there are consecutive infix - operations ´e_0; \mathit{op}\_1; e_1; \mathit{op}\_2 \ldots \mathit{op}\_n; e_n´ - with operators ´\mathit{op}\_1 , \ldots , \mathit{op}\_n´ of the same precedence, + operations ´e_0; \mathit{op}\_1; e_1; \mathit{op}\_2 ... \mathit{op}\_n; e_n´ + with operators ´\mathit{op}\_1, ..., \mathit{op}\_n´ of the same precedence, then all these operators must have the same associativity. If all operators are left-associative, the sequence is interpreted as - ´(\ldots(e_0;\mathit{op}\_1;e_1);\mathit{op}\_2\ldots);\mathit{op}\_n;e_n´. + ´(...(e_0;\mathit{op}\_1;e_1);\mathit{op}\_2...);\mathit{op}\_n;e_n´. Otherwise, if all operators are right-associative, the sequence is interpreted as - ´e_0;\mathit{op}\_1;(e_1;\mathit{op}\_2;(\ldots \mathit{op}\_n;e_n)\ldots)´. + ´e_0;\mathit{op}\_1;(e_1;\mathit{op}\_2;(... \mathit{op}\_n;e_n)...)´. - Postfix operators always have lower precedence than infix operators. E.g. ´e_1;\mathit{op}\_1;e_2;\mathit{op}\_2´ is always equivalent to ´(e_1;\mathit{op}\_1;e_2);\mathit{op}\_2´. The right-hand operand of a left-associative operator may consist of -several arguments enclosed in parentheses, e.g. ´e;\mathit{op};(e_1,\ldots,e_n)´. -This expression is then interpreted as ´e.\mathit{op}(e_1,\ldots,e_n)´. +several arguments enclosed in parentheses, e.g. ´e;\mathit{op};(e_1,...,e_n)´. +This expression is then interpreted as ´e.\mathit{op}(e_1,...,e_n)´. A left-associative binary operation ´e_1;\mathit{op};e_2´ is interpreted as ´e_1.\mathit{op}(e_2)´. If ´\mathit{op}´ is @@ -778,8 +778,8 @@ Here are examples of well-typed and ill-typed expressions. Expr1 ::= PostfixExpr ‘:’ Annotation {Annotation} ``` -An _annotated expression_ `´e´: @´a_1´ ´\ldots´ @´a_n´` -attaches [annotations](11-annotations.html#user-defined-annotations) ´a_1 , \ldots , a_n´ to the +An _annotated expression_ `´e´: @´a_1´ ... @´a_n´` +attaches [annotations](11-annotations.html#user-defined-annotations) ´a_1, ..., a_n´ to the expression ´e´. ## Assignments @@ -977,35 +977,35 @@ comprehensions have been eliminated. - A for comprehension ```scala - for (´p´ <- ´e´; ´p'´ <- ´e'; \ldots´) yield ´e''´ + for (´p´ <- ´e´; ´p'´ <- ´e'; ...´) yield ´e''´ ``` - where `´\ldots´` is a (possibly empty) + where `...` is a (possibly empty) sequence of generators, definitions, or guards, is translated to ```scala - ´e´.flatMap { case ´p´ => for (´p'´ <- ´e'; \ldots´) yield ´e''´ } + ´e´.flatMap { case ´p´ => for (´p'´ <- ´e'; ...´) yield ´e''´ } ``` - A for loop ```scala - for (´p´ <- ´e´; ´p'´ <- ´e'; \ldots´) ´e''´ + for (´p´ <- ´e´; ´p'´ <- ´e'; ...´) ´e''´ ``` - where `´\ldots´` is a (possibly empty) + where `...` is a (possibly empty) sequence of generators, definitions, or guards, is translated to ```scala - ´e´.foreach { case ´p´ => for (´p'´ <- ´e'; \ldots´) ´e''´ } + ´e´.foreach { case ´p´ => for (´p'´ <- ´e'; ...´) ´e''´ } ``` - A generator `´p´ <- ´e´` followed by a guard `if ´g´` is translated to a single generator - `´p´ <- ´e´.withFilter((´x_1 , \ldots , x_n´) => ´g\,´)` where - ´x_1 , \ldots , x_n´ are the free variables of ´p´. + `´p´ <- ´e´.withFilter((´x_1, ..., x_n´) => ´g\,´)` where + ´x_1, ..., x_n´ are the free variables of ´p´. - A generator `´p´ <- ´e´` followed by a value definition `´p'´ = ´e'´` is translated to the following generator of pairs of values, where @@ -1137,7 +1137,7 @@ where the handler ´h´ is usually a [pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions) ```scala -{ case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ } +{ case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ } ``` This expression is evaluated by evaluating the block @@ -1187,7 +1187,7 @@ Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’ Binding ::= (id | ‘_’) [‘:’ Type] ``` -The anonymous function of arity ´n´, `(´x_1´: ´T_1 , \ldots , x_n´: ´T_n´) => e` maps parameters ´x_i´ of types ´T_i´ to a result given by expression ´e´. The scope of each formal parameter ´x_i´ is ´e´. Formal parameters must have pairwise distinct names. +The anonymous function of arity ´n´, `(´x_1´: ´T_1, ..., x_n´: ´T_n´) => e` maps parameters ´x_i´ of types ´T_i´ to a result given by expression ´e´. The scope of each formal parameter ´x_i´ is ´e´. Formal parameters must have pairwise distinct names. In the case of a single untyped formal parameter, `(´x\,´) => ´e´` can be abbreviated to `´x´ => ´e´`. If an anonymous function `(´x´: ´T\,´) => ´e´` with a single typed parameter appears as the result expression of a block, it can be abbreviated to `´x´: ´T´ => e`. @@ -1196,12 +1196,12 @@ A formal parameter may also be a wildcard represented by an underscore `_`. In t A named parameter of an anonymous function may be optionally preceded by an `implicit` modifier. In that case the parameter is labeled [`implicit`](07-implicits.html#implicit-parameters-and-views); however the parameter section itself does not count as an [implicit parameter section](07-implicits.html#implicit-parameters). Hence, arguments to anonymous functions always have to be given explicitly. ### Translation -If the expected type of the anonymous function is of the shape `scala.Function´n´[´S_1 , \ldots , S_n´, ´R\,´]`, or can be [SAM-converted](#sam-conversion) to such a function type, the type `´T_i´` of a parameter `´x_i´` can be omitted, as far as `´S_i´` is defined in the expected type, and `´T_i´ = ´S_i´` is assumed. Furthermore, the expected type when type checking ´e´ is ´R´. +If the expected type of the anonymous function is of the shape `scala.Function´n´[´S_1´, ..., ´S_n´, ´R\,´]`, or can be [SAM-converted](#sam-conversion) to such a function type, the type `´T_i´` of a parameter `´x_i´` can be omitted, as far as `´S_i´` is defined in the expected type, and `´T_i´ = ´S_i´` is assumed. Furthermore, the expected type when type checking ´e´ is ´R´. -If there is no expected type for the function literal, all formal parameter types `´T_i´` must be specified explicitly, and the expected type of ´e´ is undefined. The type of the anonymous function is `scala.Function´n´[´T_1 , \ldots , T_n´, ´R\,´]`, where ´R´ is the [packed type](#expression-typing) of ´e´. ´R´ must be equivalent to a type which does not refer to any of the formal parameters ´x_i´. +If there is no expected type for the function literal, all formal parameter types `´T_i´` must be specified explicitly, and the expected type of ´e´ is undefined. The type of the anonymous function is `scala.Function´n´[´T_1´, ..., ´T_n´, ´R\,´]`, where ´R´ is the [packed type](#expression-typing) of ´e´. ´R´ must be equivalent to a type which does not refer to any of the formal parameters ´x_i´. The eventual run-time value of an anonymous function is determined by the expected type: - - a subclass of one of the builtin function types, `scala.Function´n´[´S_1 , \ldots , S_n´, ´R\,´]` (with ´S_i´ and ´R´ fully defined), + - a subclass of one of the builtin function types, `scala.Function´n´[´S_1, ..., S_n´, ´R\,´]` (with ´S_i´ and ´R´ fully defined), - a [single-abstract-method (SAM) type](#sam-conversion); - `PartialFunction[´T´, ´U´]` - some other type. @@ -1209,8 +1209,8 @@ The eventual run-time value of an anonymous function is determined by the expect The standard anonymous function evaluates in the same way as the following instance creation expression: ```scala -new scala.Function´n´[´T_1 , \ldots , T_n´, ´T´] { - def apply(´x_1´: ´T_1 , \ldots , x_n´: ´T_n´): ´T´ = ´e´ +new scala.Function´n´[´T_1, ..., T_n´, ´T´] { + def apply(´x_1´: ´T_1, ..., x_n´: ´T_n´): ´T´ = ´e´ } ``` @@ -1220,7 +1220,7 @@ The underlying platform may provide more efficient ways of constructing these in When a `PartialFunction` is required, an additional member `isDefinedAt` is synthesized, which simply returns `true`. -However, if the function literal has the shape `x => x match { $\ldots$ }`, +However, if the function literal has the shape `x => x match { $...$ }`, then `isDefinedAt` is derived from the pattern match in the following way: each case from the match expression evaluates to `true`, and if there is no default case, @@ -1268,7 +1268,7 @@ An expression ´e´ of syntactic category `Expr` _binds_ an underscore section (2) there is no other expression of syntactic category `Expr` which is properly contained in ´e´ and which itself properly contains ´u´. -If an expression ´e´ binds underscore sections ´u_1 , \ldots , u_n´, in this order, it is equivalent to +If an expression ´e´ binds underscore sections ´u_1, ..., u_n´, in this order, it is equivalent to the anonymous function `(´u'_1´, ... ´u'_n´) => ´e'´` where each ´u_i'´ results from ´u_i´ by replacing the underscore with a fresh identifier and ´e'´ results from ´e´ by replacing each underscore section ´u_i´ by ´u_i'´. @@ -1296,7 +1296,7 @@ include at least the expressions of the following forms: - A string literal - A class constructed with [`Predef.classOf`](12-the-scala-standard-library.html#the-predef-object) - An element of an enumeration from the underlying platform -- A literal array, of the form `Array´(c_1 , \ldots , c_n)´`, +- A literal array, of the form `Array´(c_1, ..., c_n)´`, where all of the ´c_i´'s are themselves constant expressions - An identifier defined by a [constant value definition](04-basic-declarations-and-definitions.html#value-declarations-and-definitions). @@ -1354,16 +1354,16 @@ is applied to pick a unique member. An expression ´e´ of polymorphic type ```scala -[´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]´T´ +[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´ ``` which does not appear as the function part of a type application is converted to a type instance of ´T´ by determining with [local type inference](#local-type-inference) -instance types `´T_1 , \ldots , T_n´` -for the type variables `´a_1 , \ldots , a_n´` and +instance types `´T_1, ..., T_n´` +for the type variables `´a_1, ..., a_n´` and implicitly embedding ´e´ in the [type application](#type-applications) -`´e´[´T_1 , \ldots , T_n´]`. +`´e´[´T_1, ..., T_n´]`. ###### Numeric Widening If ´e´ has a primitive number type which [weakly conforms](03-types.html#weak-conformance) @@ -1456,24 +1456,24 @@ member. The way this is done depends on whether or not ´e´ is used as a function. Let ´\mathscr{A}´ be the set of members referenced by ´e´. Assume first that ´e´ appears as a function in an application, as in -`´e´(´e_1 , \ldots , e_m´)`. +`´e´(´e_1´, ..., ´e_m´)`. One first determines the set of functions that is potentially [applicable](#function-applications) based on the _shape_ of the arguments. The *shape* of an argument expression ´e´, written ´\mathit{shape}(e)´, is a type that is defined as follows: - - For a function expression `(´p_1´: ´T_1 , \ldots , p_n´: ´T_n´) => ´b´: (Any ´, \ldots ,´ Any) => ´\mathit{shape}(b)´`, + - For a function expression `(´p_1´: ´T_1, ..., p_n´: ´T_n´) => ´b´: (Any, ..., Any) => ´\mathit{shape}(b)´`, where `Any` occurs ´n´ times in the argument type. - For a pattern-matching anonymous function definition `{ case ... }`: `PartialFunction[Any, Nothing]`. - For a named argument `´n´ = ´e´`: ´\mathit{shape}(e)´. - For all other expressions: `Nothing`. Let ´\mathscr{B}´ be the set of alternatives in ´\mathscr{A}´ that are [_applicable_](#function-applications) -to expressions ´(e_1 , \ldots , e_n)´ of types ´(\mathit{shape}(e_1) , \ldots , \mathit{shape}(e_n))´. +to expressions ´(e_1, ..., e_n)´ of types ´(\mathit{shape}(e_1), ..., \mathit{shape}(e_n))´. If there is precisely one alternative in ´\mathscr{B}´, that alternative is chosen. -Otherwise, let ´S_1 , \ldots , S_m´ be the list of types obtained by typing each argument as follows. +Otherwise, let ´S_1, ..., S_m´ be the list of types obtained by typing each argument as follows. Normally, an argument is typed without an expected type, except when all alternatives explicitly specify the same parameter type for this argument (a missing parameter type, @@ -1501,12 +1501,12 @@ given `´k´`. Then, the expected type for `´e_i´` is derived as follows: (and a potentially varying result type), the expected type encodes these argument types and the SAM class. For every member ´m´ in ´\mathscr{B}´ one determines whether it is applicable -to expressions (´e_1 , \ldots , e_m´) of types ´S_1, \ldots , S_m´. +to expressions (´e_1, ..., e_m´) of types ´S_1, ..., S_m´. It is an error if none of the members in ´\mathscr{B}´ is applicable. If there is one single applicable alternative, that alternative is chosen. Otherwise, let ´\mathscr{CC}´ be the set of applicable alternatives which don't employ any default argument -in the application to ´e_1 , \ldots , e_m´. +in the application to ´e_1, ..., e_m´. It is again an error if ´\mathscr{CC}´ is empty. Otherwise, one chooses the _most specific_ alternative among the alternatives @@ -1524,20 +1524,20 @@ question: given so the method is not more specific than the value. --> -- A parameterized method ´m´ of type `(´p_1:T_1, \ldots , p_n:T_n´)´U´` is +- A parameterized method ´m´ of type `(´p_1:T_1, ..., p_n:T_n´)´U´` is _as specific as_ some other member ´m'´ of type ´S´ if ´m'´ is [applicable](#function-applications) - to arguments `(´p_1 , \ldots , p_n´)` of types ´T_1 , \ldots , T_n´. -- A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]´T´` is + to arguments `(´p_1, ..., p_n´)` of types ´T_1, ..., T_n´. +- A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is as specific as some other member of type ´S´ if ´T´ is as specific as ´S´ - under the assumption that for ´i = 1 , \ldots , n´ each ´a_i´ is an abstract type name + under the assumption that for ´i = 1, ..., n´ each ´a_i´ is an abstract type name bounded from below by ´L_i´ and from above by ´U_i´. - A member of any other type is always as specific as a parameterized method or a polymorphic method. - Given two members of types ´T´ and ´U´ which are neither parameterized nor polymorphic method types, the member of type ´T´ is as specific as the member of type ´U´ if the existential dual of ´T´ conforms to the existential dual of ´U´. Here, the existential dual of a polymorphic type - `[´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]´T´` is - `´T´ forSome { type ´a_1´ >: ´L_1´ <: ´U_1´ ´, \ldots ,´ type ´a_n´ >: ´L_n´ <: ´U_n´}`. + `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is + `´T´ forSome { type ´a_1´ >: ´L_1´ <: ´U_1´, ..., type ´a_n´ >: ´L_n´ <: ´U_n´}`. The existential dual of every other type is the type itself. The _relative weight_ of an alternative ´A´ over an alternative ´B´ is a @@ -1580,8 +1580,8 @@ Consider the following definitions: ```scala class A extends B {} -def f(x: B, y: B) = ´\ldots´ -def f(x: A, y: B) = ´\ldots´ +def f(x: B, y: B) = ... +def f(x: A, y: B) = ... val a: A val b: B ``` @@ -1591,7 +1591,7 @@ definition of ´f´ whereas the application `f(a, a)` refers to the second. Assume now we add a third overloaded definition ```scala -def f(x: B, y: A) = ´\ldots´ +def f(x: B, y: A) = ... ``` Then the application `f(a, a)` is rejected for being ambiguous, since @@ -1600,12 +1600,12 @@ no most specific applicable signature exists. ### Local Type Inference Local type inference infers type arguments to be passed to expressions -of polymorphic type. Say ´e´ is of type [´a_1´ >: ´L_1´ <: ´U_1, \ldots , a_n´ >: ´L_n´ <: ´U_n´]´T´ +of polymorphic type. Say ´e´ is of type [´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´ and no explicit type parameters are given. Local type inference converts this expression to a type -application `´e´[´T_1 , \ldots , T_n´]`. The choice of the -type arguments ´T_1 , \ldots , T_n´ depends on the context in which +application `´e´[´T_1, ..., T_n´]`. The choice of the +type arguments ´T_1, ..., T_n´ depends on the context in which the expression appears and on the expected type ´\mathit{pt}´. There are three cases. @@ -1613,9 +1613,9 @@ There are three cases. If the expression appears as the prefix of a selection with a name ´x´, then type inference is _deferred_ to the whole expression ´e.x´. That is, if ´e.x´ has type ´S´, it is now treated as having -type [´a_1´ >: ´L_1´ <: ´U_1 , \ldots , a_n´ >: ´L_n´ <: ´U_n´]´S´, +type [´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´S´, and local type inference is applied in turn to infer type arguments -for ´a_1 , \ldots , a_n´, using the context in which ´e.x´ appears. +for ´a_1, ..., a_n´, using the context in which ´e.x´ appears. ###### Case 2: Values If the expression ´e´ appears as a value without being applied to @@ -1631,7 +1631,7 @@ parameters ´a_i´ such that unless it is a singleton type corresponding to an object or a constant value definition or the corresponding bound ´U_i´ is a subtype of `scala.Singleton`. - All type parameter bounds are respected, i.e. - ´\sigma L_i <: \sigma a_i´ and ´\sigma a_i <: \sigma U_i´ for ´i = 1 , \ldots , n´. + ´\sigma L_i <: \sigma a_i´ and ´\sigma a_i <: \sigma U_i´ for ´i = 1, ..., n´. - The expression's type conforms to the expected type, i.e. ´\sigma T <: \sigma \mathit{pt}´. @@ -1647,21 +1647,21 @@ an _optimal solution_ of the given constraint system for the type ´T´. ###### Case 3: Methods The last case applies if the expression -´e´ appears in an application ´e(d_1 , \ldots , d_m)´. In that case -´T´ is a method type ´(p_1:R_1 , \ldots , p_m:R_m)T'´. Without loss of +´e´ appears in an application ´e(d_1, ..., d_m)´. In that case +´T´ is a method type ´(p_1:R_1, ..., p_m:R_m)T'´. Without loss of generality we can assume that the result type ´T'´ is a value type; if it is a method type we apply [eta-expansion](#eta-expansion-section) to convert it to a function type. One computes first the types ´S_j´ of the argument expressions ´d_j´, using two alternative schemes. Each argument expression ´d_j´ is typed first with the expected type ´R_j´, -in which the type parameters ´a_1 , \ldots , a_n´ are taken as type +in which the type parameters ´a_1, ..., a_n´ are taken as type constants. If this fails, the argument ´d_j´ is typed instead with an expected type ´R_j'´ which results from ´R_j´ by replacing every type -parameter in ´a_1 , \ldots , a_n´ with _undefined_. +parameter in ´a_1, ..., a_n´ with _undefined_. In a second step, type arguments are inferred by solving a constraint system which relates the method's type with the expected type -´\mathit{pt}´ and the argument types ´S_1 , \ldots , S_m´. Solving the +´\mathit{pt}´ and the argument types ´S_1, ..., S_m´. Solving the constraint system means finding a substitution ´\sigma´ of types ´T_i´ for the type parameters ´a_i´ such that @@ -1670,11 +1670,11 @@ finding a substitution ´\sigma´ of types ´T_i´ for the type parameters unless it is a singleton type corresponding to an object or a constant value definition or the corresponding bound ´U_i´ is a subtype of `scala.Singleton`. - All type parameter bounds are respected, i.e. ´\sigma L_i <: \sigma a_i´ and - ´\sigma a_i <: \sigma U_i´ for ´i = 1 , \ldots , n´. + ´\sigma a_i <: \sigma U_i´ for ´i = 1, ..., n´. - The method's result type ´T'´ conforms to the expected type, i.e. ´\sigma T' <: \sigma \mathit{pt}´. - Each argument type [weakly conforms](03-types.html#weak-conformance) to the corresponding formal parameter - type, i.e. ´\sigma S_j <:_w \sigma R_j´ for ´j = 1 , \ldots , m´. + type, i.e. ´\sigma S_j <:_w \sigma R_j´ for ´j = 1, ..., m´. It is a compile time error if no such substitution exists. If several solutions exist, an optimal one for the type ´T'´ is chosen. @@ -1788,18 +1788,18 @@ _Eta-expansion_ converts an expression of method type to an equivalent expression of function type. It proceeds in two steps. First, one identifies the maximal sub-expressions of ´e´; let's -say these are ´e_1 , \ldots , e_m´. For each of these, one creates a +say these are ´e_1, ..., e_m´. For each of these, one creates a fresh name ´x_i´. Let ´e'´ be the expression resulting from replacing every maximal subexpression ´e_i´ in ´e´ by the corresponding fresh name ´x_i´. Second, one creates a fresh name ´y_i´ -for every argument type ´T_i´ of the method (´i = 1 , \ldots , +for every argument type ´T_i´ of the method (´i = 1 , ..., n´). The result of eta-conversion is then: ```scala { val ´x_1´ = ´e_1´; - ´\ldots´ + ... val ´x_m´ = ´e_m´; - (´y_1: T_1 , \ldots , y_n: T_n´) => ´e'´(´y_1 , \ldots , y_n´) + (´y_1: T_1, ..., y_n: T_n´) => ´e'´(´y_1, ..., y_n´) } ``` diff --git a/docs/_spec/07-implicits.md b/docs/_spec/07-implicits.md index 6f38fdd48074..47dc4ca0b0d4 100644 --- a/docs/_spec/07-implicits.md +++ b/docs/_spec/07-implicits.md @@ -45,7 +45,7 @@ object Monoids { ## Implicit Parameters An _implicit parameter list_ -`(implicit ´p_1´,´\ldots´,´p_n´)` of a method marks the parameters ´p_1 , \ldots , p_n´ as +`(implicit ´p_1´,...,´p_n´)` of a method marks the parameters ´p_1, ..., p_n´ as implicit. A method or constructor can have only one implicit parameter list, and it must be the last parameter list given. @@ -71,18 +71,18 @@ Here, we say a class ´C´ is _associated_ with a type ´T´ if it is a [base cl The _parts_ of a type ´T´ are: -- if ´T´ is a compound type `´T_1´ with ´\ldots´ with ´T_n´`, - the union of the parts of ´T_1 , \ldots , T_n´, as well as ´T´ itself; -- if ´T´ is a parameterized type `´S´[´T_1 , \ldots , T_n´]`, - the union of the parts of ´S´ and ´T_1 , \ldots , T_n´; +- if ´T´ is a compound type `´T_1´ with ... with ´T_n´`, + the union of the parts of ´T_1, ..., T_n´, as well as ´T´ itself; +- if ´T´ is a parameterized type `´S´[´T_1, ..., T_n´]`, + the union of the parts of ´S´ and ´T_1, ..., T_n´; - if ´T´ is a singleton type `´p´.type`, the parts of the type of ´p´; - if ´T´ is a type projection `´S´#´U´`, the parts of ´S´ as well as ´T´ itself; - if ´T´ is a type alias, the parts of its expansion; - if ´T´ is an abstract type, the parts of its upper bound; -- if ´T´ denotes an implicit conversion to a type with a method with argument types ´T_1 , \ldots , T_n´ and result type ´U´, - the union of the parts of ´T_1 , \ldots , T_n´ and ´U´; +- if ´T´ denotes an implicit conversion to a type with a method with argument types ´T_1, ..., T_n´ and result type ´U´, + the union of the parts of ´T_1, ..., T_n´ and ´U´; - the parts of quantified (existential or universal) and annotated types are defined as the parts of the underlying types (e.g., the parts of `T forSome { ... }` are the parts of `T`); - in all other cases, just ´T´ itself. @@ -268,7 +268,7 @@ the type: - For a type designator, ´\mathit{ttcs}(p.c) ~=~ \{c\}´; - For a parameterized type, ´\mathit{ttcs}(p.c[\mathit{targs}]) ~=~ \{c\}´; - For a singleton type, ´\mathit{ttcs}(p.type) ~=~ \mathit{ttcs}(T)´, provided ´p´ has type ´T´; -- For a compound type, `´\mathit{ttcs}(T_1´ with ´\ldots´ with ´T_n)´` ´~=~ \mathit{ttcs}(T_1) \cup \ldots \cup \mathit{ttcs}(T_n)´. +- For a compound type, `´\mathit{ttcs}(T_1´ with ... with ´T_n)´` ´~=~ \mathit{ttcs}(T_1) \cup ... \cup \mathit{ttcs}(T_n)´. The _complexity_ ´\operatorname{complexity}(T)´ of a core type is an integer which also depends on the form of the type: @@ -277,7 +277,7 @@ the type: - For a parameterized type, ´\operatorname{complexity}(p.c[\mathit{targs}]) ~=~ 1 + \Sigma \operatorname{complexity}(\mathit{targs})´ - For a singleton type denoting a package ´p´, ´\operatorname{complexity}(p.type) ~=~ 0´ - For any other singleton type, ´\operatorname{complexity}(p.type) ~=~ 1 + \operatorname{complexity}(T)´, provided ´p´ has type ´T´; -- For a compound type, `´\operatorname{complexity}(T_1´ with ´\ldots´ with ´T_n)´` ´= \Sigma\operatorname{complexity}(T_i)´ +- For a compound type, `´\operatorname{complexity}(T_1´ with ... with ´T_n)´` ´= \Sigma\operatorname{complexity}(T_i)´ The _covering set_ ´\mathit{cs}(T)´ of a type ´T´ is the set of type designators mentioned in a type. For example, given the following, @@ -495,31 +495,31 @@ Then the following rules apply. 1. If ´T´ is an instance of `Array[´S´]`, a manifest is generated with the invocation `´\mathit{Mobj}´.arrayType[S](m)`, where ´m´ is the manifest determined for ´M[S]´. -1. If ´T´ is some other class type ´S´#´C[U_1, \ldots, U_n]´ where the prefix +1. If ´T´ is some other class type ´S´#´C[U_1, ..., U_n]´ where the prefix type ´S´ cannot be statically determined from the class ´C´, a manifest is generated with the invocation `´\mathit{Mobj}´.classType[T](´m_0´, classOf[T], ´ms´)` where ´m_0´ is the manifest determined for ´M'[S]´ and ´ms´ are the - manifests determined for ´M'[U_1], \ldots, M'[U_n]´. -1. If ´T´ is some other class type with type arguments ´U_1 , \ldots , U_n´, + manifests determined for ´M'[U_1], ..., M'[U_n]´. +1. If ´T´ is some other class type with type arguments ´U_1, ..., U_n´, a manifest is generated with the invocation `´\mathit{Mobj}´.classType[T](classOf[T], ´ms´)` where ´ms´ are the - manifests determined for ´M'[U_1] , \ldots , M'[U_n]´. + manifests determined for ´M'[U_1], ..., M'[U_n]´. 1. If ´T´ is a singleton type `´p´.type`, a manifest is generated with the invocation `´\mathit{Mobj}´.singleType[T](´p´)` 1. If ´T´ is a refined type ´T' \{ R \}´, a manifest is generated for ´T'´. (That is, refinements are never reflected in manifests). 1. If ´T´ is an intersection type - `´T_1´ with ´, \ldots ,´ with ´T_n´` + `´T_1´ with ... with ´T_n´` where ´n > 1´, the result depends on whether a full manifest is to be determined or not. If ´M´ is trait `Manifest`, then a manifest is generated with the invocation `Manifest.intersectionType[T](´ms´)` where ´ms´ are the manifests - determined for ´M[T_1] , \ldots , M[T_n]´. + determined for ´M[T_1], ..., M[T_n]´. Otherwise, if ´M´ is trait `ClassManifest`, then a manifest is generated for the [intersection dominator](03-types.html#type-erasure) - of the types ´T_1 , \ldots , T_n´. + of the types ´T_1, ..., T_n´. 1. If ´T´ is some other type, then if ´M´ is trait `OptManifest`, a manifest is generated from the designator `scala.reflect.NoManifest`. If ´M´ is a type different from `OptManifest`, a static error results. diff --git a/docs/_spec/08-pattern-matching.md b/docs/_spec/08-pattern-matching.md index 53dc3ffe4547..f7203e831438 100644 --- a/docs/_spec/08-pattern-matching.md +++ b/docs/_spec/08-pattern-matching.md @@ -194,20 +194,20 @@ one is a variable pattern. SimplePattern ::= StableId ‘(’ [Patterns] ‘)’ ``` -A _constructor pattern_ is of the form ´c(p_1 , \ldots , p_n)´ where ´n +A _constructor pattern_ is of the form ´c(p_1, ..., p_n)´ where ´n \geq 0´. It consists of a stable identifier ´c´, followed by element -patterns ´p_1 , \ldots , p_n´. The constructor ´c´ is a simple or +patterns ´p_1, ..., p_n´. The constructor ´c´ is a simple or qualified name which denotes a [case class](05-classes-and-objects.html#case-classes). If the case class is monomorphic, then it must conform to the expected type of the pattern, and the formal parameter types of ´x´'s [primary constructor](05-classes-and-objects.html#class-definitions) -are taken as the expected types of the element patterns ´p_1, \ldots , +are taken as the expected types of the element patterns ´p_1, ..., p_n´. If the case class is polymorphic, then its type parameters are instantiated so that the instantiation of ´c´ conforms to the expected type of the pattern. The instantiated formal parameter types of ´c´'s primary constructor are then taken as the expected types of the -component patterns ´p_1, \ldots , p_n´. The pattern matches all -objects created from constructor invocations ´c(v_1 , \ldots , v_n)´ +component patterns ´p_1, ..., p_n´. The pattern matches all +objects created from constructor invocations ´c(v_1, ..., v_n)´ where each element pattern ´p_i´ matches the corresponding value ´v_i´. @@ -220,8 +220,8 @@ repeated parameter. This is further discussed [here](#pattern-sequences). SimplePattern ::= ‘(’ [Patterns] ‘)’ ``` -A _tuple pattern_ `(´p_1 , \ldots , p_n´)` is an alias -for the constructor pattern `scala.Tuple´n´(´p_1 , \ldots , p_n´)`, +A _tuple pattern_ `(´p_1´, ..., ´p_n´)` is an alias +for the constructor pattern `scala.Tuple´n´(´p_1´, ..., ´p_n´)`, where ´n \geq 2´. The empty tuple `()` is the unique value of type `scala.Unit`. @@ -231,7 +231,7 @@ where ´n \geq 2´. The empty tuple SimplePattern ::= StableId ‘(’ [Patterns] ‘)’ ``` -An _extractor pattern_ ´x(p_1 , \ldots , p_n)´ where ´n \geq 0´ is of +An _extractor pattern_ ´x(p_1, ..., p_n)´ where ´n \geq 0´ is of the same syntactic form as a constructor pattern. However, instead of a case class, the stable identifier ´x´ denotes an object which has a member method named `unapply` or `unapplySeq` that matches @@ -245,7 +245,7 @@ method `get` with return type `T`, and a method `isEmpty` with a return type that conforms to `Boolean`. `Option[T]` is an extractor type for type `T`. An `unapply` method in an object ´x´ _matches_ the pattern -´x(p_1 , \ldots , p_n)´ if it has a single parameter (and, optionally, an +´x(p_1, ..., p_n)´ if it has a single parameter (and, optionally, an implicit parameter list) and one of the following applies: * ´n=0´ and `unapply`'s result type conforms to `Boolean`. In this case @@ -257,18 +257,18 @@ implicit parameter list) and one of the following applies: values ´v´ for which `´x´.unapply(´v´)` yields a value ´u´ for which `´u´.isEmpty` yields `false`, `´u´.get` yields a value ´v_1´, and ´p_1´ matches ´v_1´. * ´n>1´ and `unapply`'s result type is - an extractor type for some type ´T´ with members ´\_1 , \ldots , \_n´ returning - types ´T_1 , \ldots , T_n´. In this case, the argument patterns ´p_1 - , \ldots , p_n´ are typed in turn with expected types ´T_1 , \ldots , + an extractor type for some type ´T´ with members ´\_1, ..., \_n´ returning + types ´T_1, ..., T_n´. In this case, the argument patterns ´p_1 + , ..., p_n´ are typed in turn with expected types ´T_1 , ..., T_n´. The extractor pattern matches then all values ´v´ for which `´x´.unapply(´v´)` yields a value ´u´ for which `´u´.isEmpty` yields `false`, `´u´.get` yields some value ´t´, and each pattern ´p_i´ matches the corresponding value ´t._1´ from - ´t._1 , \ldots , t._n´. + ´t._1, ..., t._n´. An `unapplySeq` method in an object ´x´ matches the pattern -´x(q_1 , \ldots , q_m, p_1 , \ldots , p_n)´ if it takes exactly one argument -and its result type is of the form `Option[(´T_1 , \ldots , T_m´, Seq[S])]` (if `m = 0`, the type `Option[Seq[S]]` is also accepted). +´x(q_1, ..., q_m, p_1, ..., p_n)´ if it takes exactly one argument +and its result type is of the form `Option[(´T_1, ..., T_m´, Seq[S])]` (if `m = 0`, the type `Option[Seq[S]]` is also accepted). This case is further discussed [below](#pattern-sequences). ###### Example 1 @@ -330,9 +330,9 @@ object Extractor { SimplePattern ::= StableId ‘(’ [Patterns ‘,’] [varid ‘@’] ‘_’ ‘*’ ‘)’ ``` -A _pattern sequence_ ´p_1 , \ldots , p_n´ appears in two contexts. -First, in a constructor pattern ´c(q_1 , \ldots , q_m, p_1 , \ldots , p_n)´, where ´c´ is a case class which has ´m+1´ primary constructor parameters, ending in a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters) of type `S*`. -Second, in an extractor pattern ´x(q_1 , \ldots , q_m, p_1 , \ldots , p_n)´ if the extractor object ´x´ does not have an `unapply` method, +A _pattern sequence_ ´p_1, ..., p_n´ appears in two contexts. +First, in a constructor pattern ´c(q_1, ..., q_m, p_1, ..., p_n)´, where ´c´ is a case class which has ´m+1´ primary constructor parameters, ending in a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters) of type `S*`. +Second, in an extractor pattern ´x(q_1, ..., q_m, p_1, ..., p_n)´ if the extractor object ´x´ does not have an `unapply` method, but it does define an `unapplySeq` method with a result type that is an extractor type for type `(T_1, ... , T_m, Seq[S])` (if `m = 0`, an extractor type for the type `Seq[S]` is also accepted). The expected type for the patterns ´p_i´ is ´S´. The last pattern in a pattern sequence may be a _sequence wildcard_ `_*`. @@ -340,9 +340,9 @@ Each element pattern ´p_i´ is type-checked with ´S´ as expected type, unless it is a sequence wildcard. If a final sequence wildcard is present, the pattern matches all values ´v´ that are sequences which start with elements matching patterns -´p_1 , \ldots , p_{n-1}´. If no final sequence wildcard is given, the +´p_1, ..., p_{n-1}´. If no final sequence wildcard is given, the pattern matches all values ´v´ that are sequences of -length ´n´ which consist of elements matching patterns ´p_1 , \ldots , +length ´n´ which consist of elements matching patterns ´p_1 , ..., p_n´. ### Infix Operation Patterns @@ -356,9 +356,9 @@ constructor or extractor pattern ´\mathit{op}(p, q)´. The precedence and associativity of operators in patterns is the same as in [expressions](06-expressions.html#prefix,-infix,-and-postfix-operations). -An infix operation pattern ´p;\mathit{op};(q_1 , \ldots , q_n)´ is a +An infix operation pattern ´p;\mathit{op};(q_1, ..., q_n)´ is a shorthand for the constructor or extractor pattern ´\mathit{op}(p, q_1 -, \ldots , q_n)´. +, ..., q_n)´. ### Pattern Alternatives @@ -366,7 +366,7 @@ shorthand for the constructor or extractor pattern ´\mathit{op}(p, q_1 Pattern ::= Pattern1 { ‘|’ Pattern1 } ``` -A _pattern alternative_ `´p_1´ | ´\ldots´ | ´p_n´` +A _pattern alternative_ `´p_1´ | ... | ´p_n´` consists of a number of alternative patterns ´p_i´. All alternative patterns are type checked with the expected type of the pattern. They may not bind variables other than wildcards. The alternative pattern @@ -398,9 +398,9 @@ A pattern ´p´ is _irrefutable_ for a type ´T´, if one of the following appli 1. ´p´ is a variable pattern, 1. ´p´ is a typed pattern ´x: T'´, and ´T <: T'´, -1. ´p´ is a constructor pattern ´c(p_1 , \ldots , p_n)´, the type ´T´ +1. ´p´ is a constructor pattern ´c(p_1, ..., p_n)´, the type ´T´ is an instance of class ´c´, the [primary constructor](05-classes-and-objects.html#class-definitions) - of type ´T´ has argument types ´T_1 , \ldots , T_n´, and each ´p_i´ is + of type ´T´ has argument types ´T_1, ..., T_n´, and each ´p_i´ is irrefutable for ´T_i´. 1. ´p´ is an extractor pattern for which the extractor type is `Some[´T´]` for some type ´T´ @@ -438,11 +438,11 @@ A type pattern ´T´ is of one of the following forms: denoted by the literal ´lit´ (the `==` method is used to compare the matched value to ´lit´). -* A compound type pattern `´T_1´ with ´\ldots´ with ´T_n´` where each ´T_i´ is a +* A compound type pattern `´T_1´ with ... with ´T_n´` where each ´T_i´ is a type pattern. This type pattern matches all values that are matched by each of the type patterns ´T_i´. -* A parameterized type pattern ´T[a_1 , \ldots , a_n]´, where the ´a_i´ +* A parameterized type pattern ´T[a_1, ..., a_n]´, where the ´a_i´ are type variable patterns or wildcards `_`. This type pattern matches all values which match ´T´ for some arbitrary instantiation of the type variables and wildcards. The @@ -472,46 +472,46 @@ pattern. ### Type parameter inference for typed patterns Assume a typed pattern ´p: T'´. Let ´T´ result from ´T'´ where all wildcards in -´T'´ are renamed to fresh variable names. Let ´a_1 , \ldots , a_n´ be +´T'´ are renamed to fresh variable names. Let ´a_1, ..., a_n´ be the type variables in ´T´. These type variables are considered bound in the pattern. Let the expected type of the pattern be ´\mathit{pt}´. Type parameter inference constructs first a set of subtype constraints over the type variables ´a_i´. The initial constraints set ´\mathcal{C}\_0´ reflects just the bounds of these type variables. That is, assuming ´T´ has -bound type variables ´a_1 , \ldots , a_n´ which correspond to class -type parameters ´a_1' , \ldots , a_n'´ with lower bounds ´L_1, \ldots , L_n´ -and upper bounds ´U_1 , \ldots , U_n´, ´\mathcal{C}_0´ contains the constraints +bound type variables ´a_1, ..., a_n´ which correspond to class +type parameters ´a_1', ..., a_n'´ with lower bounds ´L_1, ..., L_n´ +and upper bounds ´U_1, ..., U_n´, ´\mathcal{C}_0´ contains the constraints $$ \begin{cases} -a_i &<: \sigma U_i & \quad (i = 1, \ldots , n) \\\\ -\sigma L_i &<: a_i & \quad (i = 1, \ldots , n) +a_i &<: \sigma U_i & \quad (i = 1, ..., n) \\\\ +\sigma L_i &<: a_i & \quad (i = 1, ..., n) \end{cases} $$ -where ´\sigma´ is the substitution ´[a_1' := a_1 , \ldots , a_n' :=a_n]´. +where ´\sigma´ is the substitution ´[a_1' := a_1, ..., a_n' :=a_n]´. The set ´\mathcal{C}_0´ is then augmented by further subtype constraints. There are two cases. ###### Case 1 -If there exists a substitution ´\sigma´ over the type variables ´a_i , \ldots , a_n´ such that ´\sigma T´ conforms to ´\mathit{pt}´, one determines the weakest subtype constraints -´\mathcal{C}\_1´ over the type variables ´a_1, \ldots , a_n´ such that ´\mathcal{C}\_0 \wedge \mathcal{C}_1´ implies that ´T´ conforms to ´\mathit{pt}´. +If there exists a substitution ´\sigma´ over the type variables ´a_i, ..., a_n´ such that ´\sigma T´ conforms to ´\mathit{pt}´, one determines the weakest subtype constraints +´\mathcal{C}\_1´ over the type variables ´a_1, ..., a_n´ such that ´\mathcal{C}\_0 \wedge \mathcal{C}_1´ implies that ´T´ conforms to ´\mathit{pt}´. ###### Case 2 Otherwise, if ´T´ can not be made to conform to ´\mathit{pt}´ by instantiating its type variables, one determines all type variables in ´\mathit{pt}´ which are defined as type parameters of a method enclosing -the pattern. Let the set of such type parameters be ´b_1 , \ldots , +the pattern. Let the set of such type parameters be ´b_1 , ..., b_m´. Let ´\mathcal{C}\_0'´ be the subtype constraints reflecting the bounds of the type variables ´b_i´. If ´T´ denotes an instance type of a final class, let ´\mathcal{C}\_2´ be the weakest set of subtype constraints over the type -variables ´a_1 , \ldots , a_n´ and ´b_1 , \ldots , b_m´ such that +variables ´a_1, ..., a_n´ and ´b_1, ..., b_m´ such that ´\mathcal{C}\_0 \wedge \mathcal{C}\_0' \wedge \mathcal{C}\_2´ implies that ´T´ conforms to ´\mathit{pt}´. If ´T´ does not denote an instance type of a final class, let ´\mathcal{C}\_2´ be the weakest set of subtype constraints over the type variables -´a_1 , \ldots , a_n´ and ´b_1 , \ldots , b_m´ such that ´\mathcal{C}\_0 \wedge +´a_1, ..., a_n´ and ´b_1, ..., b_m´ such that ´\mathcal{C}\_0 \wedge \mathcal{C}\_0' \wedge \mathcal{C}\_2´ implies that it is possible to construct a type ´T'´ which conforms to both ´T´ and ´\mathit{pt}´. It is a static error if there is no satisfiable set of constraints ´\mathcal{C}\_2´ with this property. @@ -521,13 +521,13 @@ variables which imply the established constraint system. The process is different for the two cases above. ###### Case 1 -We take ´a_i >: L_i <: U_i´ where each ´L_i´ is minimal and each ´U_i´ is maximal wrt ´<:´ such that ´a_i >: L_i <: U_i´ for ´i = 1, \ldots, n´ implies ´\mathcal{C}\_0 \wedge \mathcal{C}\_1´. +We take ´a_i >: L_i <: U_i´ where each ´L_i´ is minimal and each ´U_i´ is maximal wrt ´<:´ such that ´a_i >: L_i <: U_i´ for ´i = 1, ..., n´ implies ´\mathcal{C}\_0 \wedge \mathcal{C}\_1´. ###### Case 2 We take ´a_i >: L_i <: U_i´ and ´b\_i >: L_i' <: U_i' ´ where each ´L_i´ and ´L_j'´ is minimal and each ´U_i´ and ´U_j'´ is maximal such that -´a_i >: L_i <: U_i´ for ´i = 1 , \ldots , n´ and -´b_j >: L_j' <: U_j'´ for ´j = 1 , \ldots , m´ +´a_i >: L_i <: U_i´ for ´i = 1, ..., n´ and +´b_j >: L_j' <: U_j'´ for ´j = 1, ..., m´ implies ´\mathcal{C}\_0 \wedge \mathcal{C}\_0' \wedge \mathcal{C}_2´. In both cases, local type inference is permitted to limit the @@ -536,10 +536,10 @@ to be understood relative to the set of types of acceptable complexity. ### Type parameter inference for constructor patterns -Assume a constructor pattern ´C(p_1 , \ldots , p_n)´ where class ´C´ -has type parameters ´a_1 , \ldots , a_n´. These type parameters +Assume a constructor pattern ´C(p_1, ..., p_n)´ where class ´C´ +has type parameters ´a_1, ..., a_n´. These type parameters are inferred in the same way as for the typed pattern -`(_: ´C[a_1 , \ldots , a_n]´)`. +`(_: ´C[a_1, ..., a_n]´)`. ###### Example Consider the program fragment: @@ -625,7 +625,7 @@ function's declared result type, `Number`. A _pattern matching expression_ ```scala -e match { case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ } +e match { case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ } ``` consists of a selector expression ´e´ and a number ´n > 0´ of @@ -636,9 +636,9 @@ The scope of the pattern variables in ´p_i´ comprises the pattern's guard and the corresponding block ´b_i´. Let ´T´ be the type of the selector expression ´e´ and let ´a_1 -, \ldots , a_m´ be the type parameters of all methods enclosing +, ..., a_m´ be the type parameters of all methods enclosing the pattern matching expression. For every ´a_i´, let ´L_i´ be its -lower bound and ´U_i´ be its higher bound. Every pattern ´p \in \{p_1, , \ldots , p_n\}´ +lower bound and ´U_i´ be its higher bound. Every pattern ´p \in \{p_1,, ..., p_n\}´ can be typed in two ways. First, it is attempted to type ´p´ with ´T´ as its expected type. If this fails, ´p´ is instead typed with a modified expected type ´T'´ which results from @@ -646,12 +646,12 @@ instead typed with a modified expected type ´T'´ which results from *undefined*. If this second step fails also, a compile-time error results. If the second step succeeds, let ´T_p´ be the type of pattern ´p´ seen as an expression. One then determines minimal bounds -´L_11 , \ldots , L_m'´ and maximal bounds ´U_1' , \ldots , U_m'´ such +´L_11, ..., L_m'´ and maximal bounds ´U_1', ..., U_m'´ such that for all ´i´, ´L_i <: L_i'´ and ´U_i' <: U_i´ and the following constraint system is satisfied: $$ -L_1 <: a_1 <: U_1\;\wedge\;\ldots\;\wedge\;L_m <: a_m <: U_m \ \Rightarrow\ T_p <: T +L_1 <: a_1 <: U_1\;\wedge\;...\;\wedge\;L_m <: a_m <: U_m \ \Rightarrow\ T_p <: T $$ If no such bounds can be found, a compile time error results. If such @@ -742,23 +742,23 @@ conforms to its expected type, `T`. An anonymous function can be defined by a sequence of cases ```scala -{ case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ } +{ case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ } ``` which appear as an expression without a prior `match`. The expected type of such an expression must in part be defined. It must -be either `scala.Function´k´[´S_1 , \ldots , S_k´, ´R´]` for some ´k > 0´, +be either `scala.Function´k´[´S_1, ..., S_k´, ´R´]` for some ´k > 0´, or `scala.PartialFunction[´S_1´, ´R´]`, where the -argument type(s) ´S_1 , \ldots , S_k´ must be fully determined, but the result type +argument type(s) ´S_1, ..., S_k´ must be fully determined, but the result type ´R´ may be undetermined. If the expected type is [SAM-convertible](06-expressions.html#sam-conversion) -to `scala.Function´k´[´S_1 , \ldots , S_k´, ´R´]`, +to `scala.Function´k´[´S_1, ..., S_k´, ´R´]`, the expression is taken to be equivalent to the anonymous function: ```scala -(´x_1: S_1 , \ldots , x_k: S_k´) => (´x_1 , \ldots , x_k´) match { - case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ +(´x_1: S_1, ..., x_k: S_k´) => (´x_1, ..., x_k´) match { + case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ } ``` @@ -768,9 +768,9 @@ equivalent to the following instance creation expression, where ´T´ is the weak least upper bound of the types of all ´b_i´. ```scala -new scala.Function´k´[´S_1 , \ldots , S_k´, ´T´] { - def apply(´x_1: S_1 , \ldots , x_k: S_k´): ´T´ = (´x_1 , \ldots , x_k´) match { - case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ +new scala.Function´k´[´S_1, ..., S_k´, ´T´] { + def apply(´x_1: S_1, ..., x_k: S_k´): ´T´ = (´x_1, ..., x_k´) match { + case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ } } ``` @@ -781,10 +781,10 @@ the expression is taken to be equivalent to the following instance creation expr ```scala new scala.PartialFunction[´S´, ´T´] { def apply(´x´: ´S´): ´T´ = x match { - case ´p_1´ => ´b_1´ ´\ldots´ case ´p_n´ => ´b_n´ + case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ } def isDefinedAt(´x´: ´S´): Boolean = { - case ´p_1´ => true ´\ldots´ case ´p_n´ => true + case ´p_1´ => true ... case ´p_n´ => true case _ => false } } @@ -792,7 +792,7 @@ new scala.PartialFunction[´S´, ´T´] { Here, ´x´ is a fresh name and ´T´ is the weak least upper bound of the types of all ´b_i´. The final default case in the `isDefinedAt` -method is omitted if one of the patterns ´p_1 , \ldots , p_n´ is +method is omitted if one of the patterns ´p_1, ..., p_n´ is already a variable or wildcard pattern. ###### Example diff --git a/docs/_spec/09-top-level-definitions.md b/docs/_spec/09-top-level-definitions.md index 33c436e8d77b..3588d26f6034 100644 --- a/docs/_spec/09-top-level-definitions.md +++ b/docs/_spec/09-top-level-definitions.md @@ -27,7 +27,7 @@ A _compilation unit_ ```scala package ´p_1´; -´\ldots´ +... package ´p_n´; ´\mathit{stats}´ ``` @@ -37,10 +37,10 @@ clauses is equivalent to a compilation unit consisting of the packaging ```scala -package ´p_1´ { ´\ldots´ +package ´p_1´ { ... package ´p_n´ { ´\mathit{stats}´ - } ´\ldots´ + } ... } ``` diff --git a/docs/_spec/11-annotations.md b/docs/_spec/11-annotations.md index 388d99b60e56..9f806ff5a471 100644 --- a/docs/_spec/11-annotations.md +++ b/docs/_spec/11-annotations.md @@ -14,7 +14,7 @@ chapter: 11 ## Definition Annotations associate meta-information with definitions. -A simple annotation has the form `@´c´` or `@´c(a_1 , \ldots , a_n)´`. +A simple annotation has the form `@´c´` or `@´c(a_1, ..., a_n)´`. Here, ´c´ is a constructor of a class ´C´, which must conform to the class `scala.Annotation`. diff --git a/docs/_spec/12-the-scala-standard-library.md b/docs/_spec/12-the-scala-standard-library.md index 80d66c0c4166..9bbc12e463dd 100644 --- a/docs/_spec/12-the-scala-standard-library.md +++ b/docs/_spec/12-the-scala-standard-library.md @@ -56,10 +56,10 @@ abstract class Any { final def != (that: Any): Boolean = !(this == that) /** Hash code; abstract here */ - def hashCode: Int = ´\ldots´ + def hashCode: Int = ... /** Textual representation; abstract here */ - def toString: String = ´\ldots´ + def toString: String = ... /** Type test; needs to be inlined to work as given */ def isInstanceOf[a]: Boolean @@ -78,11 +78,11 @@ final class AnyVal extends Any /** The root class of all reference types */ class AnyRef extends Any { def equals(that: Any): Boolean = this eq that - final def eq(that: AnyRef): Boolean = ´\ldots´ // reference equality + final def eq(that: AnyRef): Boolean = ... // reference equality final def ne(that: AnyRef): Boolean = !(this eq that) - def hashCode: Int = ´\ldots´ // hashCode computed from allocation address - def toString: String = ´\ldots´ // toString computed from hashCode and class name + def hashCode: Int = ... // hashCode computed from allocation address + def toString: String = ... // toString computed from hashCode and class name def synchronized[T](body: => T): T // execute `body` in while locking `this`. } @@ -353,13 +353,13 @@ right operand. ### The `Tuple` classes -Scala defines tuple classes `Tuple´n´` for ´n = 2 , \ldots , 22´. +Scala defines tuple classes `Tuple´n´` for ´n = 2, ..., 22´. These are defined as follows. ```scala package scala case class Tuple´n´[+T_1, ..., +T_n](_1: T_1, ..., _´n´: T_´n´) { - def toString = "(" ++ _1 ++ "," ++ ´\ldots´ ++ "," ++ _´n´ ++ ")" + def toString = "(" ++ _1 ++ "," ++ ... ++ "," ++ _´n´ ++ ")" } ``` @@ -397,10 +397,10 @@ informational purposes only: ```scala final class Array[T](_length: Int) extends java.io.Serializable with java.lang.Cloneable { - def length: Int = ´\ldots´ - def apply(i: Int): T = ´\ldots´ - def update(i: Int, x: T): Unit = ´\ldots´ - override def clone(): Array[T] = ´\ldots´ + def length: Int = ... + def apply(i: Int): T = ... + def update(i: Int, x: T): Unit = ... + override def clone(): Array[T] = ... } ``` @@ -502,46 +502,46 @@ package scala object Array { /** copies array elements from `src` to `dest`. */ def copy(src: AnyRef, srcPos: Int, - dest: AnyRef, destPos: Int, length: Int): Unit = ´\ldots´ + dest: AnyRef, destPos: Int, length: Int): Unit = ... /** Returns an array of length 0 */ def empty[T: ClassTag]: Array[T] = /** Create an array with given elements. */ - def apply[T: ClassTag](xs: T*): Array[T] = ´\ldots´ + def apply[T: ClassTag](xs: T*): Array[T] = ... /** Creates array with given dimensions */ - def ofDim[T: ClassTag](n1: Int): Array[T] = ´\ldots´ + def ofDim[T: ClassTag](n1: Int): Array[T] = ... /** Creates a 2-dimensional array */ - def ofDim[T: ClassTag](n1: Int, n2: Int): Array[Array[T]] = ´\ldots´ - ´\ldots´ + def ofDim[T: ClassTag](n1: Int, n2: Int): Array[Array[T]] = ... + ... /** Concatenate all argument arrays into a single array. */ - def concat[T: ClassTag](xss: Array[T]*): Array[T] = ´\ldots´ + def concat[T: ClassTag](xss: Array[T]*): Array[T] = ... /** Returns an array that contains the results of some element computation a number * of times. */ - def fill[T: ClassTag](n: Int)(elem: => T): Array[T] = ´\ldots´ + def fill[T: ClassTag](n: Int)(elem: => T): Array[T] = ... /** Returns a two-dimensional array that contains the results of some element * computation a number of times. */ - def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): Array[Array[T]] = ´\ldots´ - ´\ldots´ + def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): Array[Array[T]] = ... + ... /** Returns an array containing values of a given function over a range of integer * values starting from 0. */ - def tabulate[T: ClassTag](n: Int)(f: Int => T): Array[T] = ´\ldots´ + def tabulate[T: ClassTag](n: Int)(f: Int => T): Array[T] = ... /** Returns a two-dimensional array containing values of a given function * over ranges of integer values starting from `0`. */ - def tabulate[T: ClassTag](n1: Int, n2: Int)(f: (Int, Int) => T): Array[Array[T]] = ´\ldots´ - ´\ldots´ + def tabulate[T: ClassTag](n1: Int, n2: Int)(f: (Int, Int) => T): Array[Array[T]] = ... + ... /** Returns an array containing a sequence of increasing integers in a range. */ - def range(start: Int, end: Int): Array[Int] = ´\ldots´ + def range(start: Int, end: Int): Array[Int] = ... /** Returns an array containing equally spaced values in some integer interval. */ - def range(start: Int, end: Int, step: Int): Array[Int] = ´\ldots´ + def range(start: Int, end: Int, step: Int): Array[Int] = ... /** Returns an array containing repeated applications of a function to a start value. */ - def iterate[T: ClassTag](start: T, len: Int)(f: T => T): Array[T] = ´\ldots´ + def iterate[T: ClassTag](start: T, len: Int)(f: T => T): Array[T] = ... /** Enables pattern matching over arrays */ def unapplySeq[A](x: Array[A]): Option[IndexedSeq[A]] = Some(x) From 95b703f237d06dbc05842495356c232b69e6f6ea Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 18 Oct 2022 16:19:08 +0200 Subject: [PATCH 106/657] Reformat to 1sentence=1line convention --- docs/_spec/01-lexical-syntax.md | 308 ++-- docs/_spec/02-identifiers-names-and-scopes.md | 97 +- docs/_spec/03-types.md | 713 +++------- .../04-basic-declarations-and-definitions.md | 538 +++---- docs/_spec/05-classes-and-objects.md | 890 ++++-------- docs/_spec/06-expressions.md | 1266 ++++++----------- docs/_spec/07-implicits.md | 348 ++--- docs/_spec/08-pattern-matching.md | 589 +++----- docs/_spec/09-top-level-definitions.md | 104 +- docs/_spec/10-xml-expressions-and-patterns.md | 61 +- docs/_spec/11-annotations.md | 192 +-- docs/_spec/12-the-scala-standard-library.md | 437 +++--- docs/_spec/13-syntax-summary.md | 3 +- docs/_spec/README.md | 4 + 14 files changed, 1899 insertions(+), 3651 deletions(-) diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md index 46bd876b4077..032cf3703bb9 100644 --- a/docs/_spec/01-lexical-syntax.md +++ b/docs/_spec/01-lexical-syntax.md @@ -9,22 +9,16 @@ chapter: 1 Scala source code consists of Unicode text. The program text is tokenized as described in this chapter. -See the last section for special support for XML literals, -which are parsed in _XML mode_. +See the last section for special support for XML literals, which are parsed in _XML mode_. -To construct tokens, characters are distinguished according to the following -classes (Unicode general category given in parentheses): +To construct tokens, characters are distinguished according to the following classes (Unicode general category given in parentheses): 1. Whitespace characters. `\u0020 | \u0009 | \u000D | \u000A`. -1. Letters, which include lower case letters (`Ll`), upper case letters (`Lu`), - title case letters (`Lt`), other letters (`Lo`), modifier letters (`Lm`), - letter numerals (`Nl`) and the two characters `\u0024 ‘$’` and `\u005F ‘_’`. +1. Letters, which include lower case letters (`Ll`), upper case letters (`Lu`), title case letters (`Lt`), other letters (`Lo`), modifier letters (`Lm`), letter numerals (`Nl`) and the two characters `\u0024 ‘$’` and `\u005F ‘_’`. 1. Digits `‘0’ | ... | ‘9’`. 1. Parentheses `‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ `. 1. Delimiter characters ``‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ ``. -1. Operator characters. These consist of all printable ASCII characters - (`\u0020` - `\u007E`) that are in none of the sets above, mathematical - symbols (`Sm`) and other symbols (`So`). +1. Operator characters. These consist of all printable ASCII characters (`\u0020` - `\u007E`) that are in none of the sets above, mathematical symbols (`Sm`) and other symbols (`So`). ## Identifiers @@ -44,19 +38,16 @@ UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ ``` -There are three ways to form an identifier. First, an identifier can -start with a letter, followed by an arbitrary sequence of -letters and digits. This may be followed by underscore `‘_‘` -characters and another string composed of either letters and digits or -of operator characters. Second, an identifier can start with an operator -character followed by an arbitrary sequence of operator characters. -The preceding two forms are called _plain_ identifiers. Finally, -an identifier may also be formed by an arbitrary string between -backquotes (host systems may impose some restrictions on which -strings are legal for identifiers). The identifier then is composed -of all characters excluding the backquotes themselves. +There are three ways to form an identifier. +First, an identifier can start with a letter, followed by an arbitrary sequence of letters and digits. +This may be followed by underscore `‘_‘` characters and another string composed of either letters and digits or of operator characters. +Second, an identifier can start with an operator character followed by an arbitrary sequence of operator characters. +The preceding two forms are called _plain_ identifiers. +Finally, an identifier may also be formed by an arbitrary string between backquotes (host systems may impose some restrictions on which strings are legal for identifiers). +The identifier then is composed of all characters excluding the backquotes themselves. -As usual, the longest match rule applies. For instance, the string +As usual, the longest match rule applies. +For instance, the string ```scala big_bob++=`def` @@ -65,15 +56,9 @@ big_bob++=`def` decomposes into the three identifiers `big_bob`, `++=`, and `def`. -The rules for pattern matching further distinguish between -_variable identifiers_, which start with a lower case letter -or `_`, and _constant identifiers_, which do not. +The rules for pattern matching further distinguish between _variable identifiers_, which start with a lower case letter or `_`, and _constant identifiers_, which do not. -For this purpose, lower case letters include not only a-z, -but also all characters in Unicode category Ll (lowercase letter), -as well as all letters that have contributory property -Other_Lowercase, except characters in category Nl (letter numerals), -which are never taken as lower case. +For this purpose, lower case letters include not only a-z, but also all characters in Unicode category Ll (lowercase letter), as well as all letters that have contributory property Other_Lowercase, except characters in category Nl (letter numerals), which are never taken as lower case. The following are examples of variable identifiers: @@ -94,8 +79,7 @@ Some examples of constant identifiers are The ‘$’ character is reserved for compiler-synthesized identifiers. User programs should not define identifiers that contain ‘$’ characters. -The following names are reserved words instead of being members of the -syntactic class `id` of lexical identifiers. +The following names are reserved words instead of being members of the syntactic class `id` of lexical identifiers. ```scala abstract case catch class def @@ -109,8 +93,7 @@ val var while with yield _ : = => <- <: <% >: # @ ``` -The Unicode operators `\u21D2` ‘´\Rightarrow´’ and `\u2190` ‘´\leftarrow´’, which have the ASCII -equivalents `=>` and `<-`, are also reserved. +The Unicode operators `\u21D2` ‘´\Rightarrow´’ and `\u2190` ‘´\leftarrow´’, which have the ASCII equivalents `=>` and `<-`, are also reserved. > Here are examples of identifiers: > ```scala @@ -131,24 +114,21 @@ equivalents `=>` and `<-`, are also reserved. semi ::= ‘;’ | nl {nl} ``` -Scala is a line-oriented language where statements may be terminated by -semi-colons or newlines. A newline in a Scala source text is treated -as the special token “nl” if the three following criteria are satisfied: +Scala is a line-oriented language where statements may be terminated by semi-colons or newlines. +A newline in a Scala source text is treated as the special token “nl” if the three following criteria are satisfied: 1. The token immediately preceding the newline can terminate a statement. 1. The token immediately following the newline can begin a statement. 1. The token appears in a region where newlines are enabled. -The tokens that can terminate a statement are: literals, identifiers -and the following delimiters and reserved words: +The tokens that can terminate a statement are: literals, identifiers and the following delimiters and reserved words: ```scala this null true false return type _ ) ] } ``` -The tokens that can begin a statement are all Scala tokens _except_ -the following delimiters and reserved words: +The tokens that can begin a statement are all Scala tokens _except_ the following delimiters and reserved words: ```scala catch else extends finally forSome match @@ -161,64 +141,39 @@ A `case` token can begin a statement only if followed by a Newlines are enabled in: -1. all of a Scala source file, except for nested regions where newlines - are disabled, and -1. the interval between matching `{` and `}` brace tokens, - except for nested regions where newlines are disabled. +1. all of a Scala source file, except for nested regions where newlines are disabled, and +1. the interval between matching `{` and `}` brace tokens, except for nested regions where newlines are disabled. Newlines are disabled in: -1. the interval between matching `(` and `)` parenthesis tokens, except for - nested regions where newlines are enabled, and -1. the interval between matching `[` and `]` bracket tokens, except for nested - regions where newlines are enabled. -1. The interval between a `case` token and its matching - `=>` token, except for nested regions where newlines are - enabled. +1. the interval between matching `(` and `)` parenthesis tokens, except for nested regions where newlines are enabled, and +1. the interval between matching `[` and `]` bracket tokens, except for nested regions where newlines are enabled. +1. The interval between a `case` token and its matching `=>` token, except for nested regions where newlines are enabled. 1. Any regions analyzed in [XML mode](#xml-mode). -Note that the brace characters of `{...}` escapes in XML and -string literals are not tokens, -and therefore do not enclose a region where newlines -are enabled. - -Normally, only a single `nl` token is inserted between two -consecutive non-newline tokens which are on different lines, even if there are multiple lines -between the two tokens. However, if two tokens are separated by at -least one completely blank line (i.e a line which contains no -printable characters), then two `nl` tokens are inserted. - -The Scala grammar (given in full [here](13-syntax-summary.html)) -contains productions where optional `nl` tokens, but not -semicolons, are accepted. This has the effect that a new line in one of these -positions does not terminate an expression or statement. These positions can -be summarized as follows: - -Multiple newline tokens are accepted in the following places (note -that a semicolon in place of the newline would be illegal in every one -of these cases): - -- between the condition of a - [conditional expression](06-expressions.html#conditional-expressions) - or [while loop](06-expressions.html#while-loop-expressions) and the next - following expression, -- between the enumerators of a - [for-comprehension](06-expressions.html#for-comprehensions-and-for-loops) - and the next following expression, and -- after the initial `type` keyword in a - [type definition or declaration](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases). +Note that the brace characters of `{...}` escapes in XML and string literals are not tokens, and therefore do not enclose a region where newlines are enabled. + +Normally, only a single `nl` token is inserted between two consecutive non-newline tokens which are on different lines, even if there are multiple lines between the two tokens. +However, if two tokens are separated by at least one completely blank line (i.e a line which contains no printable characters), then two `nl` tokens are inserted. + +The Scala grammar (given in full [here](13-syntax-summary.html)) contains productions where optional `nl` tokens, but not semicolons, are accepted. +This has the effect that a new line in one of these positions does not terminate an expression or statement. +These positions can be summarized as follows: + +Multiple newline tokens are accepted in the following places (note that a semicolon in place of the newline would be illegal in every one of these cases): + +- between the condition of a [conditional expression](06-expressions.html#conditional-expressions) or [while loop](06-expressions.html#while-loop-expressions) and the next following expression, +- between the enumerators of a [for-comprehension](06-expressions.html#for-comprehensions-and-for-loops) and the next following expression, and +- after the initial `type` keyword in a [type definition or declaration](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases). A single new line token is accepted -- in front of an opening brace ‘{’, if that brace is a legal - continuation of the current statement or expression, -- after an [infix operator](06-expressions.html#prefix,-infix,-and-postfix-operations), - if the first token on the next line can start an expression, +- in front of an opening brace ‘{’, if that brace is a legal continuation of the current statement or expression, +- after an [infix operator](06-expressions.html#prefix,-infix,-and-postfix-operations), if the first token on the next line can start an expression, - in front of a [parameter clause](04-basic-declarations-and-definitions.html#function-declarations-and-definitions), and - after an [annotation](11-annotations.html#user-defined-annotations). -> The newline tokens between the two lines are not -> treated as statement separators. +> The newline tokens between the two lines are not treated as statement separators. > > ```scala > if (x > 0) @@ -245,8 +200,7 @@ A single new line token is accepted > } > ``` > -> With an additional newline character, the same code is interpreted as -> an object creation followed by a local block: +> With an additional newline character, the same code is interpreted as an object creation followed by a local block: > > ```scala > new Iterator[Int] @@ -265,8 +219,7 @@ A single new line token is accepted > x > 10 > ``` > -> With an additional newline character, the same code is interpreted as -> two expressions: +> With an additional newline character, the same code is interpreted as two expressions: > > ```scala > x < 0 || @@ -281,8 +234,7 @@ A single new line token is accepted > (y: Int) = x + y > ``` > -> With an additional newline character, the same code is interpreted as -> an abstract function definition and a syntactically illegal statement: +> With an additional newline character, the same code is interpreted as an abstract function definition and a syntactically illegal statement: > > ```scala > def func(x: Int) @@ -297,8 +249,7 @@ A single new line token is accepted > protected class Data { ... } > ``` > -> With an additional newline character, the same code is interpreted as -> an attribute and a separate statement (which is syntactically illegal). +> With an additional newline character, the same code is interpreted as an attribute and a separate statement (which is syntactically illegal). > > ```scala > @serializable @@ -308,9 +259,8 @@ A single new line token is accepted ## Literals -There are literals for integer numbers, floating point numbers, -characters, booleans, symbols, strings. The syntax of these literals is in -each case as in Java. +There are literals for integer numbers, floating point numbers, characters, booleans, symbols, strings. +The syntax of these literals is in each case as in Java. -> The phrase `1.toString` parses as three different tokens: -> the integer literal `1`, a `.`, and the identifier `toString`. +> The phrase `1.toString` parses as three different tokens: the integer literal `1`, a `.`, and the identifier `toString`. @@ -407,8 +344,7 @@ whitespace character between the two tokens. booleanLiteral ::= ‘true’ | ‘false’ ``` -The boolean literals `true` and `false` are -members of type `Boolean`. +The boolean literals `true` and `false` are members of type `Boolean`. ### Character Literals @@ -417,9 +353,7 @@ characterLiteral ::= ‘'’ (charNoQuoteOrNewline | escapeSeq) ‘'’ ``` A character literal is a single character enclosed in quotes. -The character can be any Unicode character except the single quote -delimiter or `\u000A` (LF) or `\u000D` (CR); -or any Unicode character represented by an +The character can be any Unicode character except the single quote delimiter or `\u000A` (LF) or `\u000D` (CR); or any Unicode character represented by an [escape sequence](#escape-sequences). > ```scala @@ -434,9 +368,7 @@ stringElement ::= charNoDoubleQuoteOrNewline | escapeSeq ``` A string literal is a sequence of characters in double quotes. -The characters can be any Unicode character except the double quote -delimiter or `\u000A` (LF) or `\u000D` (CR); -or any Unicode character represented by an [escape sequence](#escape-sequences). +The characters can be any Unicode character except the double quote delimiter or `\u000A` (LF) or `\u000D` (CR); or any Unicode character represented by an [escape sequence](#escape-sequences). If the string literal contains a double quote character, it must be escaped using `"\""`. @@ -455,13 +387,10 @@ stringLiteral ::= ‘"""’ multiLineChars ‘"""’ multiLineChars ::= {[‘"’] [‘"’] charNoDoubleQuote} {‘"’} ``` -A multi-line string literal is a sequence of characters enclosed in -triple quotes `""" ... """`. The sequence of characters is -arbitrary, except that it may contain three or more consecutive quote characters -only at the very end. Characters -must not necessarily be printable; newlines or other -control characters are also permitted. [Escape sequences](#escape-sequences) are -not processed, except for Unicode escapes (this is deprecated since 2.13.2). +A multi-line string literal is a sequence of characters enclosed in triple quotes `""" ... """`. +The sequence of characters is arbitrary, except that it may contain three or more consecutive quote characters only at the very end. +Characters must not necessarily be printable; newlines or other control characters are also permitted. +[Escape sequences](#escape-sequences) are not processed, except for Unicode escapes (this is deprecated since 2.13.2). > ```scala > """the present string @@ -477,8 +406,7 @@ not processed, except for Unicode escapes (this is deprecated since 2.13.2). > lines. > ``` > -> The Scala library contains a utility method `stripMargin` -> which can be used to strip leading whitespace from multi-line strings. +> The Scala library contains a utility method `stripMargin` which can be used to strip leading whitespace from multi-line strings. > The expression > > ```scala @@ -513,35 +441,28 @@ alphaid ::= upper idrest ``` -An interpolated string consists of an identifier starting with a letter immediately -followed by a string literal. There may be no whitespace characters or comments -between the leading identifier and the opening quote `"` of the string. -The string literal in an interpolated string can be standard (single quote) -or multi-line (triple quote). +An interpolated string consists of an identifier starting with a letter immediately followed by a string literal. +There may be no whitespace characters or comments between the leading identifier and the opening quote `"` of the string. +The string literal in an interpolated string can be standard (single quote) or multi-line (triple quote). -Inside an interpolated string none of the usual escape characters are interpreted -no matter whether the string literal is normal (enclosed in single quotes) or -multi-line (enclosed in triple quotes). Note that the sequence `\"` does not -close a normal string literal (enclosed in single quotes). +Inside an interpolated string none of the usual escape characters are interpreted no matter whether the string literal is normal (enclosed in single quotes) or multi-line (enclosed in triple quotes). +Note that the sequence `\"` does not close a normal string literal (enclosed in single quotes). There are three forms of dollar sign escape. The most general form encloses an expression in `${` and `}`, i.e. `${expr}`. -The expression enclosed in the braces that follow the leading `$` character is of -syntactical category BlockExpr. Hence, it can contain multiple statements, -and newlines are significant. Single ‘$’-signs are not permitted in isolation -in an interpolated string. A single ‘$’-sign can still be obtained by doubling the ‘$’ -character: ‘$$’. A single ‘"’-sign can be obtained by the sequence ‘\$"’. - -The simpler form consists of a ‘$’-sign followed by an identifier starting with -a letter and followed only by letters, digits, and underscore characters, e.g., `$id`. -The simpler form is expanded by putting braces around the identifier, -e.g., `$id` is equivalent to `${id}`. In the following, unless we explicitly state otherwise, -we assume that this expansion has already been performed. - -The expanded expression is type checked normally. Usually, `StringContext` will resolve to -the default implementation in the scala package, -but it could also be user-defined. Note that new interpolators can also be added through -implicit conversion of the built-in `scala.StringContext`. +The expression enclosed in the braces that follow the leading `$` character is of syntactical category BlockExpr. +Hence, it can contain multiple statements, and newlines are significant. +Single ‘$’-signs are not permitted in isolation in an interpolated string. +A single ‘$’-sign can still be obtained by doubling the ‘$’ character: ‘$$’. +A single ‘"’-sign can be obtained by the sequence ‘\$"’. + +The simpler form consists of a ‘$’-sign followed by an identifier starting with a letter and followed only by letters, digits, and underscore characters, e.g., `$id`. +The simpler form is expanded by putting braces around the identifier, e.g., `$id` is equivalent to `${id}`. +In the following, unless we explicitly state otherwise, we assume that this expansion has already been performed. + +The expanded expression is type checked normally. +Usually, `StringContext` will resolve to the default implementation in the scala package, but it could also be user-defined. +Note that new interpolators can also be added through implicit conversion of the built-in `scala.StringContext`. One could write an extension ```scala @@ -565,11 +486,9 @@ The following character escape sequences are recognized in character and string | `‘\‘ ‘'‘` | `\u0027` | single quote | `'` | | `‘\‘ ‘\‘` | `\u005c` | backslash | `\` | -In addition, Unicode escape sequences of the form `\uxxxx`, where each `x` is a hex digit are -recognized in character and string literals. +In addition, Unicode escape sequences of the form `\uxxxx`, where each `x` is a hex digit are recognized in character and string literals. -It is a compile time error if a backslash character in a character or -string literal does not start a valid escape sequence. +It is a compile time error if a backslash character in a character or string literal does not start a valid escape sequence. ### Symbol literals @@ -579,30 +498,23 @@ symbolLiteral ::= ‘'’ plainid A symbol literal `'x` is deprecated shorthand for the expression `scala.Symbol("x")`. -The `apply` method of `Symbol`'s companion object -caches weak references to `Symbol`s, thus ensuring that -identical symbol literals are equivalent with respect to reference -equality. +The `apply` method of `Symbol`'s companion object caches weak references to `Symbol`s, thus ensuring that identical symbol literals are equivalent with respect to reference equality. ## Whitespace and Comments -Tokens may be separated by whitespace characters -and/or comments. Comments come in two forms: +Tokens may be separated by whitespace characters and/or comments. +Comments come in two forms: -A single-line comment is a sequence of characters which starts with -`//` and extends to the end of the line. +A single-line comment is a sequence of characters which starts with `//` and extends to the end of the line. -A multi-line comment is a sequence of characters between -`/*` and `*/`. Multi-line comments may be nested, -but are required to be properly nested. Therefore, a comment like -`/* /* */` will be rejected as having an unterminated -comment. +A multi-line comment is a sequence of characters between `/*` and `*/`. +Multi-line comments may be nested, but are required to be properly nested. +Therefore, a comment like `/* /* */` will be rejected as having an unterminated comment. ## Trailing Commas in Multi-line Expressions -If a comma (`,`) is followed immediately, ignoring whitespace, by a newline and -a closing parenthesis (`)`), bracket (`]`), or brace (`}`), then the comma is -treated as a "trailing comma" and is ignored. For example: +If a comma (`,`) is followed immediately, ignoring whitespace, by a newline and a closing parenthesis (`)`), bracket (`]`), or brace (`}`), then the comma is treated as a "trailing comma" and is ignored. +For example: ```scala foo( @@ -614,11 +526,8 @@ foo( ## XML mode -In order to allow literal inclusion of XML fragments, lexical analysis -switches from Scala mode to XML mode when encountering an opening -angle bracket ‘<’ in the following circumstance: The ‘<’ must be -preceded either by whitespace, an opening parenthesis or an opening -brace and immediately followed by a character starting an XML name. +In order to allow literal inclusion of XML fragments, lexical analysis switches from Scala mode to XML mode when encountering an opening angle bracket ‘<’ in the following circumstance: +The ‘<’ must be preceded either by whitespace, an opening parenthesis or an opening brace and immediately followed by a character starting an XML name. ```ebnf ( whitespace | ‘(’ | ‘{’ ) ‘<’ (XNameStart | ‘!’ | ‘?’) @@ -628,20 +537,13 @@ brace and immediately followed by a character starting an XML name. The scanner switches from XML mode to Scala mode if either -- the XML expression or the XML pattern started by the initial ‘<’ has been - successfully parsed, or if -- the parser encounters an embedded Scala expression or pattern and - forces the Scanner - back to normal mode, until the Scala expression or pattern is - successfully parsed. In this case, since code and XML fragments can be - nested, the parser has to maintain a stack that reflects the nesting - of XML and Scala expressions adequately. +- the XML expression or the XML pattern started by the initial ‘<’ has been successfully parsed, or if +- the parser encounters an embedded Scala expression or pattern and forces the Scanner back to normal mode, until the Scala expression or pattern is successfully parsed. +In this case, since code and XML fragments can be nested, the parser has to maintain a stack that reflects the nesting of XML and Scala expressions adequately. -Note that no Scala tokens are constructed in XML mode, and that comments are interpreted -as text. +Note that no Scala tokens are constructed in XML mode, and that comments are interpreted as text. -> The following value definition uses an XML literal with two embedded -> Scala expressions: +> The following value definition uses an XML literal with two embedded Scala expressions: > > ```scala > val b = diff --git a/docs/_spec/02-identifiers-names-and-scopes.md b/docs/_spec/02-identifiers-names-and-scopes.md index b8bde8cfd1a9..2b34ae8844cf 100644 --- a/docs/_spec/02-identifiers-names-and-scopes.md +++ b/docs/_spec/02-identifiers-names-and-scopes.md @@ -6,8 +6,8 @@ chapter: 2 # Identifiers, Names and Scopes -Names in Scala identify types, values, methods, and classes which are -collectively called _entities_. Names are introduced by local +Names in Scala identify types, values, methods, and classes which are collectively called _entities_. +Names are introduced by local [definitions and declarations](04-basic-declarations-and-definitions.html#basic-declarations-and-definitions), [inheritance](05-classes-and-objects.html#class-members), [import clauses](04-basic-declarations-and-definitions.html#import-clauses), or @@ -16,29 +16,22 @@ which are collectively called _bindings_. Bindings of different kinds have precedence defined on them: -1. Definitions and declarations that are local, inherited, or made - available by a package clause and also defined in the same compilation unit - as the reference to them, have the highest precedence. +1. Definitions and declarations that are local, inherited, or made available by a package clause and also defined in the same compilation unit as the reference to them, have the highest precedence. 1. Explicit imports have the next highest precedence. 1. Wildcard imports have the next highest precedence. -1. Definitions made available by a package clause, but not also defined in the - same compilation unit as the reference to them, as well as imports which - are supplied by the compiler but not explicitly written in source code, - have the lowest precedence. +1. Definitions made available by a package clause, but not also defined in the same compilation unit as the reference to them, as well as imports which are supplied by the compiler but not explicitly written in source code, have the lowest precedence. -There are two different name spaces, one for [types](03-types.html#types) -and one for [terms](06-expressions.html#expressions). The same name may designate a -type and a term, depending on the context where the name is used. +There are two different name spaces, one for [types](03-types.html#types) and one for [terms](06-expressions.html#expressions). +The same name may designate a type and a term, depending on the context where the name is used. A binding has a _scope_ in which the entity defined by a single -name can be accessed using a simple name. Scopes are nested. A binding -in some inner scope _shadows_ bindings of lower precedence in the -same scope as well as bindings of the same or lower precedence in outer -scopes. +name can be accessed using a simple name. +Scopes are nested. +A binding in some inner scope _shadows_ bindings of lower precedence in the same scope as well as bindings of the same or lower precedence in outer scopes. -Note that shadowing is only a partial order. In the following example, -neither binding of `x` shadows the other. Consequently, the -reference to `x` in the last line of the block is ambiguous. +Note that shadowing is only a partial order. +In the following example, neither binding of `x` shadows the other. +Consequently, the reference to `x` in the last line of the block is ambiguous. ```scala val x = 1 @@ -48,32 +41,24 @@ locally { } ``` -A reference to an unqualified (type- or term-) identifier ´x´ is bound -by the unique binding, which +A reference to an unqualified (type- or term-) identifier ´x´ is bound by the unique binding, which - defines an entity with name ´x´ in the same namespace as the identifier, and -- shadows all other bindings that define entities with name ´x´ in that - namespace. - -It is an error if no such binding exists. If ´x´ is bound by an -import clause, then the simple name ´x´ is taken to be equivalent to -the qualified name to which ´x´ is mapped by the import clause. If ´x´ -is bound by a definition or declaration, then ´x´ refers to the entity -introduced by that binding. In that case, the type of ´x´ is the type -of the referenced entity. - -A reference to a qualified (type- or term-) identifier ´e.x´ refers to -the member of the type ´T´ of ´e´ which has the name ´x´ in the same -namespace as the identifier. It is an error if ´T´ is not a [value type](03-types.html#value-types). +- shadows all other bindings that define entities with name ´x´ in that namespace. + +It is an error if no such binding exists. +If ´x´ is bound by an import clause, then the simple name ´x´ is taken to be equivalent to the qualified name to which ´x´ is mapped by the import clause. +If ´x´ is bound by a definition or declaration, then ´x´ refers to the entity introduced by that binding. +In that case, the type of ´x´ is the type of the referenced entity. + +A reference to a qualified (type- or term-) identifier ´e.x´ refers to the member of the type ´T´ of ´e´ which has the name ´x´ in the same namespace as the identifier. +It is an error if ´T´ is not a [value type](03-types.html#value-types). The type of ´e.x´ is the member type of the referenced entity in ´T´. Binding precedence implies that the way source is bundled in files affects name resolution. -In particular, imported names have higher precedence than names, defined in other files, -that might otherwise be visible because they are defined in -either the current package or an enclosing package. +In particular, imported names have higher precedence than names, defined in other files, that might otherwise be visible because they are defined in either the current package or an enclosing package. -Note that a package definition is taken as lowest precedence, since packages -are open and can be defined across arbitrary compilation units. +Note that a package definition is taken as lowest precedence, since packages are open and can be defined across arbitrary compilation units. ```scala package util { @@ -85,8 +70,8 @@ package util { } ``` -The compiler supplies imports in a preamble to every source file. This preamble -conceptually has the following form, where braces indicate nested scopes: +The compiler supplies imports in a preamble to every source file. +This preamble conceptually has the following form, where braces indicate nested scopes: ```scala import java.lang._ @@ -99,16 +84,12 @@ import java.lang._ } ``` -These imports are taken as lowest precedence, so that they are always shadowed -by user code, which may contain competing imports and definitions. -They also increase the nesting depth as shown, so that later imports -shadow earlier ones. +These imports are taken as lowest precedence, so that they are always shadowed by user code, which may contain competing imports and definitions. +They also increase the nesting depth as shown, so that later imports shadow earlier ones. -As a convenience, multiple bindings of a type identifier to the same -underlying type is permitted. This is possible when import clauses introduce -a binding of a member type alias with the same binding precedence, typically -through wildcard imports. This allows redundant type aliases to be imported -without introducing an ambiguity. +As a convenience, multiple bindings of a type identifier to the same underlying type is permitted. +This is possible when import clauses introduce a binding of a member type alias with the same binding precedence, typically through wildcard imports. +This allows redundant type aliases to be imported without introducing an ambiguity. ```scala object X { type T = annotation.tailrec } @@ -119,8 +100,7 @@ object Z { } ``` -Similarly, imported aliases of names introduced by package statements are -allowed, even though the names are strictly ambiguous: +Similarly, imported aliases of names introduced by package statements are allowed, even though the names are strictly ambiguous: ```scala // c.scala @@ -132,16 +112,12 @@ package p { class X extends C } package q { class Y extends C } ``` -The reference to `C` in the definition of `X` is strictly ambiguous -because `C` is available by virtue of the package clause in -a different file, and can't shadow the imported name. But because -the references are the same, the definition is taken as though it -did shadow the import. +The reference to `C` in the definition of `X` is strictly ambiguous because `C` is available by virtue of the package clause in a different file, and can't shadow the imported name. +But because the references are the same, the definition is taken as though it did shadow the import. ###### Example -Assume the following two definitions of objects named `X` in packages `p` and `q` -in separate compilation units. +Assume the following two definitions of objects named `X` in packages `p` and `q` in separate compilation units. ```scala package p { @@ -153,8 +129,7 @@ package q { } ``` -The following program illustrates different kinds of bindings and -precedences between them. +The following program illustrates different kinds of bindings and precedences between them. ```scala package p { // `X' bound by package clause diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index 205af416b9c5..664ddea49713 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -29,40 +29,29 @@ chapter: 3 Types ::= Type {‘,’ Type} ``` -We distinguish between proper types and type constructors, which -take type parameters and yield types. A subset of proper types -called _value types_ represents sets of (first-class) values. +We distinguish between proper types and type constructors, which take type parameters and yield types. +A subset of proper types called _value types_ represents sets of (first-class) values. Value types are either _concrete_ or _abstract_. -Every concrete value type can be represented as a _class type_, i.e. a -[type designator](#type-designators) that refers to a -[class or a trait](05-classes-and-objects.html#class-definitions) [^1], or as a -[compound type](#compound-types) representing an -intersection of types, possibly with a [refinement](#compound-types) -that further constrains the types of its members. +Every concrete value type can be represented as a _class type_, i.e. a [type designator](#type-designators) that refers to a [class or a trait](05-classes-and-objects.html#class-definitions) [^1], or as a [compound type](#compound-types) representing an intersection of types, possibly with a [refinement](#compound-types) that further constrains the types of its members. + -Abstract value types are introduced by [type parameters](04-basic-declarations-and-definitions.html#type-parameters) -and [abstract type bindings](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases). +Abstract value types are introduced by [type parameters](04-basic-declarations-and-definitions.html#type-parameters) and [abstract type bindings](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases). Parentheses in types can be used for grouping. [^1]: We assume that objects and packages also implicitly define a class (of the same name as the object or package, but inaccessible to user programs). -Non-value types capture properties of identifiers that -[are not values](#non-value-types). For example, a -[type constructor](#type-constructors) does not directly specify a type of -values. However, when a type constructor is applied to the correct type -arguments, it yields a proper type, which may be a value type. +Non-value types capture properties of identifiers that [are not values](#non-value-types). +For example, a [type constructor](#type-constructors) does not directly specify a type of values. +However, when a type constructor is applied to the correct type arguments, it yields a proper type, which may be a value type. -Non-value types are expressed indirectly in Scala. E.g., a method type is -described by writing down a method signature, which in itself is not a real -type, although it gives rise to a corresponding [method type](#method-types). -Type constructors are another example, as one can write -`type Swap[m[_, _], a,b] = m[b, a]`, but there is no syntax to write -the corresponding anonymous type function directly. +Non-value types are expressed indirectly in Scala. +E.g., a method type is described by writing down a method signature, which in itself is not a real type, although it gives rise to a corresponding [method type](#method-types). +Type constructors are another example, as one can write `type Swap[m[_, _], a,b] = m[b, a]`, but there is no syntax to write the corresponding anonymous type function directly. ## Paths @@ -75,30 +64,24 @@ StableId ::= id ClassQualifier ::= ‘[’ id ‘]’ ``` -Paths are not types themselves, but they can be a part of named types -and in that function form a central role in Scala's type system. +Paths are not types themselves, but they can be a part of named types and in that function form a central role in Scala's type system. A path is one of the following. - The empty path ε (which cannot be written explicitly in user programs). - ´C.´`this`, where ´C´ references a class. - The path `this` is taken as a shorthand for ´C.´`this` where - ´C´ is the name of the class directly enclosing the reference. + The path `this` is taken as a shorthand for ´C.´`this` where ´C´ is the name of the class directly enclosing the reference. - ´p.x´ where ´p´ is a path and ´x´ is a stable member of ´p´. - _Stable members_ are packages or members introduced by object definitions or - by value definitions of [non-volatile types](#volatile-types). + _Stable members_ are packages or members introduced by object definitions or by value definitions of [non-volatile types](#volatile-types). - ´C.´`super`´.x´ or ´C.´`super`´[M].x´ - where ´C´ references a class and ´x´ references a - stable member of the super class or designated parent class ´M´ of ´C´. - The prefix `super` is taken as a shorthand for ´C.´`super` where - ´C´ is the name of the class directly enclosing the reference. + where ´C´ references a class and ´x´ references a stable member of the super class or designated parent class ´M´ of ´C´. + The prefix `super` is taken as a shorthand for ´C.´`super` where ´C´ is the name of the class directly enclosing the reference. A _stable identifier_ is a path which ends in an identifier. ## Value Types -Every value in Scala has a type which is of one of the following -forms. +Every value in Scala has a type which is of one of the following forms. ### Singleton Types @@ -106,12 +89,9 @@ forms. SimpleType ::= Path ‘.’ ‘type’ ``` -A _singleton type_ is of the form ´p.´`type`. Where ´p´ is a path pointing to a -value which [conforms](06-expressions.html#expression-typing) to -`scala.AnyRef`, the type denotes the set of values consisting of `null` and the -value denoted by ´p´ (i.e., the value ´v´ for which `v eq p`). Where the path -does not conform to `scala.AnyRef` the type denotes the set consisting of only -the value denoted by ´p´. +A _singleton type_ is of the form ´p.´`type`. +Where ´p´ is a path pointing to a value which [conforms](06-expressions.html#expression-typing) to `scala.AnyRef`, the type denotes the set of values consisting of `null` and the value denoted by ´p´ (i.e., the value ´v´ for which `v eq p`). +Where the path does not conform to `scala.AnyRef` the type denotes the set consisting of only the value denoted by ´p´. @@ -121,21 +101,17 @@ the value denoted by ´p´. SimpleType ::= Literal ``` -A literal type `lit` is a special kind of singleton type which denotes the -single literal value `lit`. Thus, the type ascription `1: 1` gives the most -precise type to the literal value `1`: the literal type `1`. +A literal type `lit` is a special kind of singleton type which denotes the single literal value `lit`. +Thus, the type ascription `1: 1` gives the most precise type to the literal value `1`: the literal type `1`. At run time, an expression `e` is considered to have literal type `lit` if `e == lit`. -Concretely, the result of `e.isInstanceOf[lit]` and `e match { case _ : lit => }` is -determined by evaluating `e == lit`. +Concretely, the result of `e.isInstanceOf[lit]` and `e match { case _ : lit => }` is determined by evaluating `e == lit`. -Literal types are available for all types for which there is dedicated syntax -except `Unit`. This includes the numeric types (other than `Byte` and `Short` -which don't currently have syntax), `Boolean`, `Char` and `String`. +Literal types are available for all types for which there is dedicated syntax except `Unit`. +This includes the numeric types (other than `Byte` and `Short` which don't currently have syntax), `Boolean`, `Char` and `String`. ### Stable Types -A _stable type_ is a singleton type, a literal type, -or a type that is declared to be a subtype of trait `scala.Singleton`. +A _stable type_ is a singleton type, a literal type, or a type that is declared to be a subtype of trait `scala.Singleton`. ### Type Projection @@ -143,8 +119,7 @@ or a type that is declared to be a subtype of trait `scala.Singleton`. SimpleType ::= SimpleType ‘#’ id ``` -A _type projection_ ´T´#´x´ references the type member named -´x´ of type ´T´. +A _type projection_ ´T´#´x´ references the type member named ´x´ of type ´T´. -The names of all type parameters must be pairwise different in their enclosing type parameter clause. The scope of a type parameter includes in each case the whole type parameter clause. Therefore it is possible that a type parameter appears as part of its own bounds or the bounds of other type parameters in the same clause. However, a type parameter may not be bounded directly or indirectly by itself. +The names of all type parameters must be pairwise different in their enclosing type parameter clause. +The scope of a type parameter includes in each case the whole type parameter clause. +Therefore it is possible that a type parameter appears as part of its own bounds or the bounds of other type parameters in the same clause. +However, a type parameter may not be bounded directly or indirectly by itself. -A type constructor parameter adds a nested type parameter clause to the type parameter. The most general form of a type constructor parameter is `´@a_1 ... @a_n \pm t[\mathit{tps}\,]´ >: ´L´ <: ´U´`. +A type constructor parameter adds a nested type parameter clause to the type parameter. +The most general form of a type constructor parameter is `´@a_1 ... @a_n \pm t[\mathit{tps}\,]´ >: ´L´ <: ´U´`. -The above scoping restrictions are generalized to the case of nested type parameter clauses, which declare higher-order type parameters. Higher-order type parameters (the type parameters of a type parameter ´t´) are only visible in their immediately surrounding parameter clause (possibly including clauses at a deeper nesting level) and in the bounds of ´t´. Therefore, their names must only be pairwise different from the names of other visible parameters. Since the names of higher-order type parameters are thus often irrelevant, they may be denoted with a `‘_’`, which is nowhere visible. +The above scoping restrictions are generalized to the case of nested type parameter clauses, which declare higher-order type parameters. +Higher-order type parameters (the type parameters of a type parameter ´t´) are only visible in their immediately surrounding parameter clause (possibly including clauses at a deeper nesting level) and in the bounds of ´t´. +Therefore, their names must only be pairwise different from the names of other visible parameters. +Since the names of higher-order type parameters are thus often irrelevant, they may be denoted with a `‘_’`, which is nowhere visible. ###### Example Here are some well-formed type parameter clauses: @@ -456,53 +405,32 @@ The following type parameter clauses are illegal: ## Variance Annotations -Variance annotations indicate how instances of parameterized types -vary with respect to [subtyping](03-types.html#conformance). A -‘+’ variance indicates a covariant dependency, a -‘-’ variance indicates a contravariant dependency, and a -missing variance indication indicates an invariant dependency. - -A variance annotation constrains the way the annotated type variable -may appear in the type or class which binds the type parameter. In a -type definition `type ´T´[´\mathit{tps}\,´] = ´S´`, or a type -declaration `type ´T´[´\mathit{tps}\,´] >: ´L´ <: ´U´` type parameters labeled -‘+’ must only appear in covariant position whereas -type parameters labeled ‘-’ must only appear in contravariant -position. Analogously, for a class definition -`class ´C´[´\mathit{tps}\,´](´\mathit{ps}\,´) extends ´T´ { ´x´: ´S´ => ...}`, -type parameters labeled -‘+’ must only appear in covariant position in the -self type ´S´ and the template ´T´, whereas type -parameters labeled ‘-’ must only appear in contravariant -position. - -The variance position of a type parameter in a type or template is -defined as follows. Let the opposite of covariance be contravariance, -and the opposite of invariance be itself. The top-level of the type -or template is always in covariant position. The variance position -changes at the following constructs. - -- The variance position of a method parameter is the opposite of the - variance position of the enclosing parameter clause. -- The variance position of a type parameter is the opposite of the - variance position of the enclosing type parameter clause. -- The variance position of the lower bound of a type declaration or type parameter - is the opposite of the variance position of the type declaration or parameter. +Variance annotations indicate how instances of parameterized types vary with respect to [subtyping](03-types.html#conformance). +A ‘+’ variance indicates a covariant dependency, a ‘-’ variance indicates a contravariant dependency, and a missing variance indication indicates an invariant dependency. + +A variance annotation constrains the way the annotated type variable may appear in the type or class which binds the type parameter. +In a type definition `type ´T´[´\mathit{tps}\,´] = ´S´`, or a type declaration `type ´T´[´\mathit{tps}\,´] >: ´L´ <: ´U´` type parameters labeled ‘+’ must only appear in covariant position whereas type parameters labeled ‘-’ must only appear in contravariant position. +Analogously, for a class definition `class ´C´[´\mathit{tps}\,´](´\mathit{ps}\,´) extends ´T´ { ´x´: ´S´ => ...}`, type parameters labeled ‘+’ must only appear in covariant position in the self type ´S´ and the template ´T´, whereas type parameters labeled ‘-’ must only appear in contravariant position. + +The variance position of a type parameter in a type or template is defined as follows. +Let the opposite of covariance be contravariance, and the opposite of invariance be itself. +The top-level of the type or template is always in covariant position. +The variance position changes at the following constructs. + +- The variance position of a method parameter is the opposite of the variance position of the enclosing parameter clause. +- The variance position of a type parameter is the opposite of the variance position of the enclosing type parameter clause. +- The variance position of the lower bound of a type declaration or type parameter is the opposite of the variance position of the type declaration or parameter. - The type of a mutable variable is always in invariant position. - The right-hand side of a type alias is always in invariant position. - The prefix ´S´ of a type selection `´S´#´T´` is always in invariant position. -- For a type argument ´T´ of a type `´S´[´... T ...´ ]`: If the - corresponding type parameter is invariant, then ´T´ is in - invariant position. If the corresponding type parameter is - contravariant, the variance position of ´T´ is the opposite of - the variance position of the enclosing type `´S´[´... T ...´ ]`. +- For a type argument ´T´ of a type `´S´[´... T ...´ ]`: +If the corresponding type parameter is invariant, then ´T´ is in invariant position. +If the corresponding type parameter is contravariant, the variance position of ´T´ is the opposite of the variance position of the enclosing type `´S´[´... T ...´ ]`. -References to the type parameters in -[object-private or object-protected values, types, variables, or methods](05-classes-and-objects.html#modifiers) of the class are not -checked for their variance position. In these members the type parameter may -appear anywhere without restricting its legal variance annotations. +References to the type parameters in [object-private or object-protected values, types, variables, or methods](05-classes-and-objects.html#modifiers) of the class are not checked for their variance position. +In these members the type parameter may appear anywhere without restricting its legal variance annotations. ###### Example The following variance annotation is legal. @@ -513,16 +441,14 @@ abstract class P[+A, +B] { } ``` -With this variance annotation, type instances -of ´P´ subtype covariantly with respect to their arguments. +With this variance annotation, type instances of ´P´ subtype covariantly with respect to their arguments. For instance, ```scala P[IOException, String] <: P[Throwable, AnyRef] ``` -If the members of ´P´ are mutable variables, -the same variance annotation becomes illegal. +If the members of ´P´ are mutable variables, the same variance annotation becomes illegal. ```scala abstract class Q[+A, +B](x: A, y: B) { @@ -531,8 +457,7 @@ abstract class Q[+A, +B](x: A, y: B) { } ``` -If the mutable variables are object-private, the class definition -becomes legal again: +If the mutable variables are object-private, the class definition becomes legal again: ```scala abstract class R[+A, +B](x: A, y: B) { @@ -543,8 +468,7 @@ abstract class R[+A, +B](x: A, y: B) { ###### Example -The following variance annotation is illegal, since ´a´ appears -in contravariant position in the parameter of `append`: +The following variance annotation is illegal, since ´a´ appears in contravariant position in the parameter of `append`: ```scala abstract class Sequence[+A] { @@ -554,8 +478,7 @@ abstract class Sequence[+A] { } ``` -The problem can be avoided by generalizing the type of `append` -by means of a lower bound: +The problem can be avoided by generalizing the type of `append` by means of a lower bound: ```scala abstract class Sequence[+A] { @@ -571,11 +494,8 @@ abstract class OutputChannel[-A] { } ``` -With that annotation, we have that -`OutputChannel[AnyRef]` conforms to `OutputChannel[String]`. -That is, a -channel on which one can write any object can substitute for a channel -on which one can write only strings. +With that annotation, we have that `OutputChannel[AnyRef]` conforms to `OutputChannel[String]`. +That is, a channel on which one can write any object can substitute for a channel on which one can write only strings. ## Function Declarations and Definitions @@ -595,47 +515,27 @@ ParamType ::= Type | Type ‘*’ ``` -A _function declaration_ has the form `def ´f\,\mathit{psig}´: ´T´`, where -´f´ is the function's name, ´\mathit{psig}´ is its parameter -signature and ´T´ is its result type. A _function definition_ -`def ´f\,\mathit{psig}´: ´T´ = ´e´` also includes a _function body_ ´e´, -i.e. an expression which defines the function's result. A parameter -signature consists of an optional type parameter clause `[´\mathit{tps}\,´]`, -followed by zero or more value parameter clauses -`(´\mathit{ps}_1´)...(´\mathit{ps}_n´)`. Such a declaration or definition -introduces a value with a (possibly polymorphic) method type whose -parameter types and result type are as given. - -The type of the function body is expected to [conform](06-expressions.html#expression-typing) -to the function's declared -result type, if one is given. If the function definition is not -recursive, the result type may be omitted, in which case it is -determined from the packed type of the function body. - -A _type parameter clause_ ´\mathit{tps}´ consists of one or more -[type declarations](#type-declarations-and-type-aliases), which introduce type -parameters, possibly with bounds. The scope of a type parameter includes -the whole signature, including any of the type parameter bounds as -well as the function body, if it is present. - -A _value parameter clause_ ´\mathit{ps}´ consists of zero or more formal -parameter bindings such as `´x´: ´T´` or `´x: T = e´`, which bind value -parameters and associate them with their types. +A _function declaration_ has the form `def ´f\,\mathit{psig}´: ´T´`, where ´f´ is the function's name, ´\mathit{psig}´ is its parameter signature and ´T´ is its result type. +A _function definition_ `def ´f\,\mathit{psig}´: ´T´ = ´e´` also includes a _function body_ ´e´, i.e. an expression which defines the function's result. +A parameter signature consists of an optional type parameter clause `[´\mathit{tps}\,´]`, followed by zero or more value parameter clauses `(´\mathit{ps}_1´)...(´\mathit{ps}_n´)`. +Such a declaration or definition introduces a value with a (possibly polymorphic) method type whose parameter types and result type are as given. + +The type of the function body is expected to [conform](06-expressions.html#expression-typing) to the function's declared result type, if one is given. +If the function definition is not recursive, the result type may be omitted, in which case it is determined from the packed type of the function body. + +A _type parameter clause_ ´\mathit{tps}´ consists of one or more [type declarations](#type-declarations-and-type-aliases), which introduce type parameters, possibly with bounds. +The scope of a type parameter includes the whole signature, including any of the type parameter bounds as well as the function body, if it is present. + +A _value parameter clause_ ´\mathit{ps}´ consists of zero or more formal parameter bindings such as `´x´: ´T´` or `´x: T = e´`, which bind value parameters and associate them with their types. ### Default Arguments -Each value parameter -declaration may optionally define a default argument. The default argument -expression ´e´ is type-checked with an expected type ´T'´ obtained -by replacing all occurrences of the function's type parameters in ´T´ by -the undefined type. - -For every parameter ´p_{i,j}´ with a default argument a method named -`´f\$´default´\$´n` is generated which computes the default argument -expression. Here, ´n´ denotes the parameter's position in the method -declaration. These methods are parametrized by the type parameter clause -`[´\mathit{tps}\,´]` and all value parameter clauses -`(´\mathit{ps}_1´)...(´\mathit{ps}_{i-1}´)` preceding ´p_{i,j}´. +Each value parameter declaration may optionally define a default argument. +The default argument expression ´e´ is type-checked with an expected type ´T'´ obtained by replacing all occurrences of the function's type parameters in ´T´ by the undefined type. + +For every parameter ´p_{i,j}´ with a default argument a method named `´f\$´default´\$´n` is generated which computes the default argument expression. +Here, ´n´ denotes the parameter's position in the method declaration. +These methods are parametrized by the type parameter clause `[´\mathit{tps}\,´]` and all value parameter clauses `(´\mathit{ps}_1´)...(´\mathit{ps}_{i-1}´)` preceding ´p_{i,j}´. The `´f\$´default´\$´n` methods are inaccessible for user programs. ###### Example @@ -646,22 +546,19 @@ def compare[T](a: T = 0)(b: T = a) = (a == b) ``` the default expression `0` is type-checked with an undefined expected -type. When applying `compare()`, the default value `0` is inserted -and `T` is instantiated to `Int`. The methods computing the default -arguments have the form: +type. +When applying `compare()`, the default value `0` is inserted and `T` is instantiated to `Int`. +The methods computing the default arguments have the form: ```scala def compare´\$´default´\$´1[T]: Int = 0 def compare´\$´default´\$´2[T](a: T): T = a ``` -The scope of a formal value parameter name ´x´ comprises all subsequent -parameter clauses, as well as the method return type and the function body, if -they are given. Both type parameter names and value parameter names must -be pairwise distinct. +The scope of a formal value parameter name ´x´ comprises all subsequent parameter clauses, as well as the method return type and the function body, if they are given. +Both type parameter names and value parameter names must be pairwise distinct. -A default value which depends on earlier parameters uses the actual arguments -if they are provided, not the default arguments. +A default value which depends on earlier parameters uses the actual arguments if they are provided, not the default arguments. ```scala def f(a: Int = 0)(b: Int = a + 1) = b // OK @@ -669,8 +566,7 @@ def f(a: Int = 0)(b: Int = a + 1) = b // OK f(10)() // returns 11 (not 1) ``` -If an [implicit argument](07-implicits.html#implicit-parameters) -is not found by implicit search, it may be supplied using a default argument. +If an [implicit argument](07-implicits.html#implicit-parameters) is not found by implicit search, it may be supplied using a default argument. ```scala implicit val i: Int = 2 @@ -684,16 +580,12 @@ f // "hihi" ParamType ::= ‘=>’ Type ``` -The type of a value parameter may be prefixed by `=>`, e.g. -`´x´: => ´T´`. The type of such a parameter is then the -parameterless method type `=> ´T´`. This indicates that the -corresponding argument is not evaluated at the point of function -application, but instead is evaluated at each use within the -function. That is, the argument is evaluated using _call-by-name_. +The type of a value parameter may be prefixed by `=>`, e.g. `´x´: => ´T´`. +The type of such a parameter is then the parameterless method type `=> ´T´`. +This indicates that the corresponding argument is not evaluated at the point of function application, but instead is evaluated at each use within the function. +That is, the argument is evaluated using _call-by-name_. -The by-name modifier is disallowed for parameters of classes that -carry a `val` or `var` prefix, including parameters of case -classes for which a `val` prefix is implicitly generated. +The by-name modifier is disallowed for parameters of classes that carry a `val` or `var` prefix, including parameters of case classes for which a `val` prefix is implicitly generated. ###### Example The declaration @@ -702,8 +594,7 @@ The declaration def whileLoop (cond: => Boolean) (stat: => Unit): Unit ``` -indicates that both parameters of `whileLoop` are evaluated using -call-by-name. +indicates that both parameters of `whileLoop` are evaluated using call-by-name. ### Repeated Parameters @@ -711,30 +602,16 @@ call-by-name. ParamType ::= Type ‘*’ ``` -The last value parameter of a parameter section may be suffixed by -`'*'`, e.g. `(..., ´x´:´T´*)`. The type of such a -_repeated_ parameter inside the method is then the sequence type -`scala.Seq[´T´]`. Methods with repeated parameters -`´T´*` take a variable number of arguments of type ´T´. -That is, if a method ´m´ with type -`(´p_1:T_1, ..., p_n:T_n, p_s:S´*)´U´` is applied to arguments -´(e_1, ..., e_k)´ where ´k \geq n´, then ´m´ is taken in that application -to have type ´(p_1:T_1, ..., p_n:T_n, p_s:S, ..., p_{s'}:S)U´, with -´k - n´ occurrences of type -´S´ where any parameter names beyond ´p_s´ are fresh. The only exception to -this rule is if the last argument is -marked to be a _sequence argument_ via a `_*` type -annotation. If ´m´ above is applied to arguments -`(´e_1, ..., e_n, e'´: _*)`, then the type of ´m´ in -that application is taken to be -`(´p_1:T_1, ... , p_n:T_n,p_{s}:´scala.Seq[´S´])`. - -It is not allowed to define any default arguments in a parameter section -with a repeated parameter. +The last value parameter of a parameter section may be suffixed by `'*'`, e.g. `(..., ´x´:´T´*)`. +The type of such a _repeated_ parameter inside the method is then the sequence type `scala.Seq[´T´]`. +Methods with repeated parameters `´T´*` take a variable number of arguments of type ´T´. +That is, if a method ´m´ with type `(´p_1:T_1, ..., p_n:T_n, p_s:S´*)´U´` is applied to arguments ´(e_1, ..., e_k)´ where ´k \geq n´, then ´m´ is taken in that application to have type ´(p_1:T_1, ..., p_n:T_n, p_s:S, ..., p_{s'}:S)U´, with ´k - n´ occurrences of type ´S´ where any parameter names beyond ´p_s´ are fresh. The only exception to this rule is if the last argument is marked to be a _sequence argument_ via a `_*` type annotation. +If ´m´ above is applied to arguments `(´e_1, ..., e_n, e'´: _*)`, then the type of ´m´ in that application is taken to be `(´p_1:T_1, ... , p_n:T_n,p_{s}:´scala.Seq[´S´])`. + +It is not allowed to define any default arguments in a parameter section with a repeated parameter. ###### Example -The following method definition computes the sum of the squares of a -variable number of integer arguments. +The following method definition computes the sum of the squares of a variable number of integer arguments. ```scala def sum(args: Int*) = { @@ -744,8 +621,7 @@ def sum(args: Int*) = { } ``` -The following applications of this method yield `0`, `1`, -`6`, in that order. +The following applications of this method yield `0`, `1`, `6`, in that order. ```scala sum() @@ -765,8 +641,7 @@ The following application of method `sum` is ill-formed: sum(xs) // ***** error: expected: Int, found: List[Int] ``` -By contrast, the following application is well formed and yields again -the result `6`: +By contrast, the following application is well formed and yields again the result `6`: ```scala sum(xs: _*) @@ -779,17 +654,12 @@ FunDcl ::= FunSig FunDef ::= FunSig [nl] ‘{’ Block ‘}’ ``` -Special syntax exists for procedures, i.e. functions that return the -`Unit` value `()`. -A _procedure declaration_ is a function declaration where the result type -is omitted. The result type is then implicitly completed to the -`Unit` type. E.g., `def ´f´(´\mathit{ps}´)` is equivalent to -`def ´f´(´\mathit{ps}´): Unit`. +Special syntax exists for procedures, i.e. functions that return the `Unit` value `()`. +A _procedure declaration_ is a function declaration where the result type is omitted. +The result type is then implicitly completed to the `Unit` type. E.g., `def ´f´(´\mathit{ps}´)` is equivalent to `def ´f´(´\mathit{ps}´): Unit`. -A _procedure definition_ is a function definition where the result type -and the equals sign are omitted; its defining expression must be a block. -E.g., `def ´f´(´\mathit{ps}´) {´\mathit{stats}´}` is equivalent to -`def ´f´(´\mathit{ps}´): Unit = {´\mathit{stats}´}`. +A _procedure definition_ is a function definition where the result type and the equals sign are omitted; its defining expression must be a block. +E.g., `def ´f´(´\mathit{ps}´) {´\mathit{stats}´}` is equivalent to `def ´f´(´\mathit{ps}´): Unit = {´\mathit{stats}´}`. ###### Example Here is a declaration and a definition of a procedure named `write`: @@ -816,14 +686,10 @@ object Terminal extends Writer { ### Method Return Type Inference -A class member definition ´m´ that overrides some other function ´m'´ -in a base class of ´C´ may leave out the return type, even if it is -recursive. In this case, the return type ´R'´ of the overridden -function ´m'´, seen as a member of ´C´, is taken as the return type of -´m´ for each recursive invocation of ´m´. That way, a type ´R´ for the -right-hand side of ´m´ can be determined, which is then taken as the -return type of ´m´. Note that ´R´ may be different from ´R'´, as long -as ´R´ conforms to ´R'´. +A class member definition ´m´ that overrides some other function ´m'´ in a base class of ´C´ may leave out the return type, even if it is recursive. +In this case, the return type ´R'´ of the overridden function ´m'´, seen as a member of ´C´, is taken as the return type of ´m´ for each recursive invocation of ´m´. +That way, a type ´R´ for the right-hand side of ´m´ can be determined, which is then taken as the return type of ´m´. +Note that ´R´ may be different from ´R'´, as long as ´R´ conforms to ´R'´. ###### Example Assume the following definitions: @@ -837,8 +703,7 @@ class C extends I { } ``` -Here, it is OK to leave out the result type of `factorial` -in `C`, even though the method is recursive. +Here, it is OK to leave out the result type of `factorial` in `C`, even though the method is recursive. Member definitions fall into two categories: concrete and abstract. -Members of class ´C´ are either _directly defined_ (i.e. they appear in -´C´'s statement sequence ´\mathit{stats}´) or they are _inherited_. There are two rules -that determine the set of members of a class, one for each category: - -A _concrete member_ of a class ´C´ is any concrete definition ´M´ in -some class ´C_i \in \mathcal{L}(C)´, except if there is a preceding class -´C_j \in \mathcal{L}(C)´ where ´j < i´ which directly defines a concrete -member ´M'´ matching ´M´. - -An _abstract member_ of a class ´C´ is any abstract definition ´M´ -in some class ´C_i \in \mathcal{L}(C)´, except if ´C´ contains already a -concrete member ´M'´ matching ´M´, or if there is a preceding class -´C_j \in \mathcal{L}(C)´ where ´j < i´ which directly defines an abstract -member ´M'´ matching ´M´. - -This definition also determines the [overriding](#overriding) relationships -between matching members of a class ´C´ and its parents. +Members of class ´C´ are either _directly defined_ (i.e. they appear in ´C´'s statement sequence ´\mathit{stats}´) or they are _inherited_. +There are two rules that determine the set of members of a class, one for each category: + +A _concrete member_ of a class ´C´ is any concrete definition ´M´ in some class ´C_i \in \mathcal{L}(C)´, except if there is a preceding clas ´C_j \in \mathcal{L}(C)´ where ´j < i´ which directly defines a concrete member ´M'´ matching ´M´. + +An _abstract member_ of a class ´C´ is any abstract definition ´M´ in some class ´C_i \in \mathcal{L}(C)´, except if ´C´ contains already a concrete member ´M'´ matching ´M´, or if there is a preceding class ´C_j \in \mathcal{L}(C)´ where ´j < i´ which directly defines an abstract member ´M'´ matching ´M´. + +This definition also determines the [overriding](#overriding) relationships between matching members of a class ´C´ and its parents. First, a concrete definition always overrides an abstract definition. -Second, for definitions ´M´ and ´M´' which are both concrete or both abstract, -´M´ overrides ´M'´ if ´M´ appears in a class that precedes (in the -linearization of ´C´) the class in which ´M'´ is defined. +Second, for definitions ´M´ and ´M´' which are both concrete or both abstract, ´M´ overrides ´M'´ if ´M´ appears in a class that precedes (in the linearization of ´C´) the class in which ´M'´ is defined. -It is an error if a template directly defines two matching members. It -is also an error if a template contains two members (directly defined -or inherited) with the same name and the same [erased type](03-types.html#type-erasure). -Finally, a template is not allowed to contain two methods (directly -defined or inherited) with the same name which both define default arguments. +It is an error if a template directly defines two matching members. +It is also an error if a template contains two members (directly defined or inherited) with the same name and the same [erased type](03-types.html#type-erasure). +Finally, a template is not allowed to contain two methods (directly defined or inherited) with the same name which both define default arguments. ###### Example Consider the trait definitions: @@ -314,64 +231,46 @@ trait C extends A { override def f: Int = 4 ; def g: Int } trait D extends B with C { def h: Int } ``` -Then trait `D` has a directly defined abstract member `h`. It -inherits member `f` from trait `C` and member `g` from -trait `B`. +Then trait `D` has a directly defined abstract member `h`. +It inherits member `f` from trait `C` and member `g` from trait `B`. ### Overriding -A member ´M´ of class ´C´ that [matches](#class-members) -a non-private member ´M'´ of a -base class of ´C´ is said to _override_ that member. In this case -the binding of the overriding member ´M´ must [subsume](03-types.html#conformance) -the binding of the overridden member ´M'´. -Furthermore, the following restrictions on modifiers apply to ´M´ and -´M'´: +A member ´M´ of class ´C´ that [matches](#class-members) a non-private member ´M'´ of a base class of ´C´ is said to _override_ that member. +In this case the binding of the overriding member ´M´ must [subsume](03-types.html#conformance) the binding of the overridden member ´M'´. +Furthermore, the following restrictions on modifiers apply to ´M´ and ´M'´: - ´M'´ must not be labeled `final`. - ´M´ must not be [`private`](#modifiers). -- If ´M´ is labeled `private[´C´]` for some enclosing class or package ´C´, - then ´M'´ must be labeled `private[´C'´]` for some class or package ´C'´ where - ´C'´ equals ´C´ or ´C'´ is contained in ´C´. +- If ´M´ is labeled `private[´C´]` for some enclosing class or package ´C´, then ´M'´ must be labeled `private[´C'´]` for some class or package ´C'´ where ´C'´ equals ´C´ or ´C'´ is contained in ´C´. -- If ´M´ is labeled `protected`, then ´M'´ must also be - labeled `protected`. +- If ´M´ is labeled `protected`, then ´M'´ must also be labeled `protected`. - If ´M'´ is not an abstract member, then ´M´ must be labeled `override`. - Furthermore, one of two possibilities must hold: +Furthermore, one of two possibilities must hold: - either ´M´ is defined in a subclass of the class where is ´M'´ is defined, - - or both ´M´ and ´M'´ override a third member ´M''´ which is defined - in a base class of both the classes containing ´M´ and ´M'´ -- If ´M'´ is [incomplete](#modifiers) in ´C´ then ´M´ must be - labeled `abstract override`. -- If ´M´ and ´M'´ are both concrete value definitions, then either none - of them is marked `lazy` or both must be marked `lazy`. + - or both ´M´ and ´M'´ override a third member ´M''´ which is defined in a base class of both the classes containing ´M´ and ´M'´ +- If ´M'´ is [incomplete](#modifiers) in ´C´ then ´M´ must be labeled `abstract override`. +- If ´M´ and ´M'´ are both concrete value definitions, then either none of them is marked `lazy` or both must be marked `lazy`. - A stable member can only be overridden by a stable member. - For example, this is not allowed: +For example, this is not allowed: ```scala class X { val stable = 1} class Y extends X { override var stable = 1 } // error ``` -Another restriction applies to abstract type members: An abstract type -member with a [volatile type](03-types.html#volatile-types) as its upper -bound may not override an abstract type member which does not have a -volatile upper bound. +Another restriction applies to abstract type members: +An abstract type member with a [volatile type](03-types.html#volatile-types) as its upper bound may not override an abstract type member which does not have a volatile upper bound. -A special rule concerns parameterless methods. If a parameterless -method defined as `def ´f´: ´T´ = ...` or `def ´f´ = ...` overrides a method of -type ´()T'´ which has an empty parameter list, then ´f´ is also -assumed to have an empty parameter list. +A special rule concerns parameterless methods. +If a parameterless method defined as `def ´f´: ´T´ = ...` or `def ´f´ = ...` overrides a method of type ´()T'´ which has an empty parameter list, then ´f´ is also assumed to have an empty parameter list. -An overriding method inherits all default arguments from the definition -in the superclass. By specifying default arguments in the overriding method -it is possible to add new defaults (if the corresponding parameter in the -superclass does not have a default) or to override the defaults of the -superclass (otherwise). +An overriding method inherits all default arguments from the definition in the superclass. +By specifying default arguments in the overriding method it is possible to add new defaults (if the corresponding parameter in the superclass does not have a default) or to override the defaults of the superclass (otherwise). ###### Example @@ -384,12 +283,9 @@ trait B extends Root { type T <: B } trait C extends A with B ``` -Then the class definition `C` is not well-formed because the -binding of `T` in `C` is -`type T <: B`, -which fails to subsume the binding `type T <: A` of `T` -in type `A`. The problem can be solved by adding an overriding -definition of type `T` in class `C`: +Then the class definition `C` is not well-formed because the binding of `T` in `C` is `type T <: B`, which fails to subsume the binding `type T <: A` of `T` +in type `A`. +The problem can be solved by adding an overriding definition of type `T` in class `C`: ```scala class C extends A with B { type T <: C } @@ -397,18 +293,15 @@ class C extends A with B { type T <: C } ### Inheritance Closure -Let ´C´ be a class type. The _inheritance closure_ of ´C´ is the -smallest set ´\mathscr{S}´ of types such that +Let ´C´ be a class type. +The _inheritance closure_ of ´C´ is the smallest set ´\mathscr{S}´ of types such that - ´C´ is in ´\mathscr{S}´. -- If ´T´ is in ´\mathscr{S}´, then every type ´T'´ which forms syntactically - a part of ´T´ is also in ´\mathscr{S}´. -- If ´T´ is a class type in ´\mathscr{S}´, then all [parents](#templates) - of ´T´ are also in ´\mathscr{S}´. +- If ´T´ is in ´\mathscr{S}´, then every type ´T'´ which forms syntactically a part of ´T´ is also in ´\mathscr{S}´. +- If ´T´ is a class type in ´\mathscr{S}´, then all [parents](#templates) of ´T´ are also in ´\mathscr{S}´. -It is a static error if the inheritance closure of a class type -consists of an infinite number of types. (This restriction is -necessary to make subtyping decidable[^kennedy]). +It is a static error if the inheritance closure of a class type consists of an infinite number of types. +(This restriction is necessary to make subtyping decidable[^kennedy]). [^kennedy]: Kennedy, Pierce. [On Decidability of Nominal Subtyping with Variance.]( https://research.microsoft.com/pubs/64041/fool2007.pdf) in FOOL 2007 @@ -419,9 +312,8 @@ EarlyDefs ::= ‘{’ [EarlyDef {semi EarlyDef}] ‘}’ ‘with’ EarlyDef ::= {Annotation} {Modifier} PatVarDef ``` -A template may start with an _early field definition_ clause, -which serves to define certain field values before the supertype -constructor is called. In a template +A template may start with an _early field definition_ clause, which serves to define certain field values before the supertype constructor is called. +In a template ```scala { val ´p_1´: ´T_1´ = ´e_1´ @@ -430,33 +322,21 @@ constructor is called. In a template } with ´sc´ with ´mt_1´ with ´mt_n´ { ´\mathit{stats}´ } ``` -The initial pattern definitions of ´p_1 , \ldots , p_n´ are called -_early definitions_. They define fields -which form part of the template. Every early definition must define -at least one variable. - -An early definition is type-checked and evaluated in the scope which -is in effect just before the template being defined, augmented by any -type parameters of the enclosing class and by any early definitions -preceding the one being defined. In particular, any reference to -`this` in an early definition refers -to the identity of `this` just outside the template. Consequently, it -is impossible for an early definition to refer to the object being -constructed by the template, or to refer to one of its fields and -methods, except for any other preceding early definition in the same -section. Furthermore, references to preceding early definitions -always refer to the value that's defined there and do not take into account -overriding definitions. In other words, a block of early definitions -is evaluated exactly as if it were a local block containing a number of value -definitions. - -Early definitions are evaluated -before the superclass constructor of the template is called, -in the order they are defined. +The initial pattern definitions of ´p_1 , \ldots , p_n´ are called _early definitions_. +They define fields which form part of the template. +Every early definition must define at least one variable. + +An early definition is type-checked and evaluated in the scope which is in effect just before the template being defined, augmented by any type parameters of the enclosing class and by any early definitions preceding the one being defined. +In particular, any reference to `this` in an early definition refers to the identity of `this` just outside the template. +Consequently, it is impossible for an early definition to refer to the object being constructed by the template, or to refer to one of its fields and methods, except for any other preceding early definition in the same section. +Furthermore, references to preceding early definitions always refer to the value that's defined there and do not take into account overriding definitions. +In other words, a block of early definitions is evaluated exactly as if it were a local block containing a number of value definitions. + +Early definitions are evaluated before the superclass constructor of the template is called, in the order they are defined. ###### Example -Early definitions are particularly useful for -traits, which do not have normal constructor parameters. Example: +Early definitions are particularly useful for traits, which do not have normal constructor parameters. +Example: ```scala trait Greeting { @@ -470,14 +350,11 @@ class C extends { } ``` -In the code above, the field `name` is initialized before the -constructor of `Greeting` is called. Therefore, field `msg` in -class `Greeting` is properly initialized to `"How are you, Bob"`. +In the code above, the field `name` is initialized before the constructor of `Greeting` is called. +Therefore, field `msg` in class `Greeting` is properly initialized to `"How are you, Bob"`. -If `name` had been initialized instead in `C`'s normal class -body, it would be initialized after the constructor of -`Greeting`. In that case, `msg` would be initialized to -`"How are you, "`. +If `name` had been initialized instead in `C`'s normal class body, it would be initialized after the constructor of `Greeting`. +In that case, `msg` would be initialized to `"How are you, "`. ## Modifiers @@ -494,45 +371,30 @@ AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] AccessQualifier ::= ‘[’ (id | ‘this’) ‘]’ ``` -Member definitions may be preceded by modifiers which affect the -accessibility and usage of the identifiers bound by them. If several -modifiers are given, their order does not matter, but the same -modifier may not occur more than once. Modifiers preceding a repeated -definition apply to all constituent definitions. The rules governing -the validity and meaning of a modifier are as follows. +Member definitions may be preceded by modifiers which affect the accessibility and usage of the identifiers bound by them. +If several modifiers are given, their order does not matter, but the same modifier may not occur more than once. +Modifiers preceding a repeated definition apply to all constituent definitions. +The rules governing the validity and meaning of a modifier are as follows. ### `private` -The `private` modifier can be used with any definition or declaration in a -template. Private members of a template can be accessed only from within the -directly enclosing template and its companion module or -[companion class](#object-definitions). - -The `private` modifier is also valid for -[top-level](09-top-level-definitions.html#packagings) templates. - -A `private` modifier can be _qualified_ with an identifier ´C´ (e.g. -`private[´C´]`) that must denote a class or package enclosing the definition. -Members labeled with such a modifier are accessible respectively only from code -inside the package ´C´ or only from code inside the class ´C´ and its -[companion module](#object-definitions). - -A different form of qualification is `private[this]`. A member -´M´ marked with this modifier is called _object-protected_; it can be accessed only from within -the object in which it is defined. That is, a selection ´p.M´ is only -legal if the prefix is `this` or `´O´.this`, for some -class ´O´ enclosing the reference. In addition, the restrictions for -unqualified `private` apply. - -Members marked private without a qualifier are called _class-private_, -whereas members labeled with `private[this]` -are called _object-private_. A member _is private_ if it is -either class-private or object-private, but not if it is marked -`private[´C´]` where ´C´ is an identifier; in the latter -case the member is called _qualified private_. - -Class-private or object-private members may not be abstract, and may -not have `protected` or `override` modifiers. They are not inherited -by subclasses and they may not override definitions in parent classes. +The `private` modifier can be used with any definition or declaration in a template. +Private members of a template can be accessed only from within the directly enclosing template and its companion module or [companion class](#object-definitions). + +The `private` modifier is also valid for [top-level](09-top-level-definitions.html#packagings) templates. + +A `private` modifier can be _qualified_ with an identifier ´C´ (e.g. `private[´C´]`) that must denote a class or package enclosing the definition. +Members labeled with such a modifier are accessible respectively only from code inside the package ´C´ or only from code inside the class ´C´ and its [companion module](#object-definitions). + +A different form of qualification is `private[this]`. +A member ´M´ marked with this modifier is called _object-protected_; it can be accessed only from within the object in which it is defined. +That is, a selection ´p.M´ is only legal if the prefix is `this` or `´O´.this`, for some class ´O´ enclosing the reference. +In addition, the restrictions for unqualified `private` apply. + +Members marked private without a qualifier are called _class-private_, whereas members labeled with `private[this]` are called _object-private_. +A member _is private_ if it is either class-private or object-private, but not if it is marked `private[´C´]` where ´C´ is an identifier; in the latter case the member is called _qualified private_. + +Class-private or object-private members may not be abstract, and may not have `protected` or `override` modifiers. +They are not inherited by subclasses and they may not override definitions in parent classes. ### `protected` The `protected` modifier applies to class member definitions. @@ -541,91 +403,60 @@ Protected members of a class can be accessed from within - all templates that have the defining class as a base class, - the companion module of any of those classes. -A `protected` modifier can be qualified with an identifier ´C´ (e.g. -`protected[´C´]`) that must denote a class or package enclosing the definition. -Members labeled with such a modifier are also accessible respectively from all -code inside the package ´C´ or from all code inside the class ´C´ and its -[companion module](#object-definitions). - -A protected identifier ´x´ may be used as a member name in a selection -`´r´.´x´` only if one of the following applies: - - The access is within the template defining the member, or, if - a qualification ´C´ is given, inside the package ´C´, - or the class ´C´, or its companion module, or - - ´r´ is one of the reserved words `this` and - `super`, or - - ´r´'s type conforms to a type-instance of the - class which contains the access. - -A different form of qualification is `protected[this]`. A member -´M´ marked with this modifier is called _object-protected_; it can be accessed only from within -the object in which it is defined. That is, a selection ´p.M´ is only -legal if the prefix is `this` or `´O´.this`, for some -class ´O´ enclosing the reference. In addition, the restrictions for -unqualified `protected` apply. +A `protected` modifier can be qualified with an identifier ´C´ (e.g. `protected[´C´]`) that must denote a class or package enclosing the definition. +Members labeled with such a modifier are also accessible respectively from all code inside the package ´C´ or from all code inside the class ´C´ and its [companion module](#object-definitions). + +A protected identifier ´x´ may be used as a member name in a selection `´r´.´x´` only if one of the following applies: + - The access is within the template defining the member, or, if a qualification ´C´ is given, inside the package ´C´, or the class ´C´, or its companion module, or + - ´r´ is one of the reserved words `this` and `super`, or + - ´r´'s type conforms to a type-instance of the class which contains the access. + +A different form of qualification is `protected[this]`. +A member ´M´ marked with this modifier is called _object-protected_; it can be accessed only from within the object in which it is defined. That is, a selection ´p.M´ is only legal if the prefix is `this` or `´O´.this`, for some class ´O´ enclosing the reference. In addition, the restrictions for unqualified `protected` apply. ### `override` The `override` modifier applies to class member definitions or declarations. -It is mandatory for member definitions or declarations that override some -other concrete member definition in a parent class. If an `override` -modifier is given, there must be at least one overridden member -definition or declaration (either concrete or abstract). +It is mandatory for member definitions or declarations that override some other concrete member definition in a parent class. +If an `override` modifier is given, there must be at least one overridden member definition or declaration (either concrete or abstract). ### `abstract override` -The `override` modifier has an additional significance when -combined with the `abstract` modifier. That modifier combination -is only allowed for value members of traits. +The `override` modifier has an additional significance when combined with the `abstract` modifier. +That modifier combination is only allowed for value members of traits. -We call a member ´M´ of a template _incomplete_ if it is either -abstract (i.e. defined by a declaration), or it is labeled -`abstract` and `override` and -every member overridden by ´M´ is again incomplete. +We call a member ´M´ of a template _incomplete_ if it is either abstract (i.e. defined by a declaration), or it is labeled `abstract` and `override` and every member overridden by ´M´ is again incomplete. -Note that the `abstract override` modifier combination does not -influence the concept whether a member is concrete or abstract. A -member is _abstract_ if only a declaration is given for it; -it is _concrete_ if a full definition is given. +Note that the `abstract override` modifier combination does not influence the concept whether a member is concrete or abstract. +A member is _abstract_ if only a declaration is given for it; it is _concrete_ if a full definition is given. ### `abstract` -The `abstract` modifier is used in class definitions. It is -redundant for traits, and mandatory for all other classes which have -incomplete members. Abstract classes cannot be -[instantiated](06-expressions.html#instance-creation-expressions) with a constructor invocation -unless followed by mixins and/or a refinement which override all -incomplete members of the class. Only abstract classes and traits can have -abstract term members. - -The `abstract` modifier can also be used in conjunction with -`override` for class member definitions. In that case the -previous discussion applies. +The `abstract` modifier is used in class definitions. +It is redundant for traits, and mandatory for all other classes which have incomplete members. +Abstract classes cannot be [instantiated](06-expressions.html#instance-creation-expressions) with a constructor invocation unless followed by mixins and/or a refinement which override all incomplete members of the class. +Only abstract classes and traits can have abstract term members. + +The `abstract` modifier can also be used in conjunction with `override` for class member definitions. +In that case the previous discussion applies. ### `final` -The `final` modifier applies to class member definitions and to -class definitions. A `final` class member definition may not be -overridden in subclasses. A `final` class may not be inherited by -a template. `final` is redundant for object definitions. Members -of final classes or objects are implicitly also final, so the -`final` modifier is generally redundant for them, too. Note, however, that -[constant value definitions](04-basic-declarations-and-definitions.html#value-declarations-and-definitions) -do require an explicit `final` modifier, -even if they are defined in a final class or object. -`final` is permitted for abstract classes -but it may not be applied to traits or incomplete members, -and it may not be combined in one modifier list with `sealed`. +The `final` modifier applies to class member definitions and to class definitions. +A `final` class member definition may not be overridden in subclasses. +A `final` class may not be inherited by a template. +`final` is redundant for object definitions. +Members of final classes or objects are implicitly also final, so the `final` modifier is generally redundant for them, too. +Note, however, that [constant value definitions](04-basic-declarations-and-definitions.html#value-declarations-and-definitions) do require an explicit `final` modifier, even if they are defined in a final class or object. +`final` is permitted for abstract classes but it may not be applied to traits or incomplete members, and it may not be combined in one modifier list with `sealed`. ### `sealed` -The `sealed` modifier applies to class definitions. A -`sealed` class may not be directly inherited, except if the inheriting -template is defined in the same source file as the inherited class. +The `sealed` modifier applies to class definitions. +A `sealed` class may not be directly inherited, except if the inheriting template is defined in the same source file as the inherited class. However, subclasses of a sealed class can be inherited anywhere. ### `lazy` -The `lazy` modifier applies to value definitions. A `lazy` -value is initialized the first time it is accessed (which might never -happen at all). Attempting to access a lazy value during its -initialization might lead to looping behavior. If an exception is -thrown during initialization, the value is considered uninitialized, -and a later access will retry to evaluate its right hand side. +The `lazy` modifier applies to value definitions. +A `lazy` value is initialized the first time it is accessed (which might never +happen at all). +Attempting to access a lazy value during its initialization might lead to looping behavior. +If an exception is thrown during initialization, the value is considered uninitialized, and a later access will retry to evaluate its right hand side. ###### Example The following code illustrates the use of qualified private: @@ -641,18 +472,12 @@ class Outer { } ``` -Here, accesses to the method `f` can appear anywhere within -`Outer`, but not outside it. Accesses to method -`g` can appear anywhere within the package -`outerpkg.innerpkg`, as would be the case for -package-private methods in Java. Finally, accesses to method -`h` can appear anywhere within package `outerpkg`, -including packages contained in it. +Here, accesses to the method `f` can appear anywhere within `Outer`, but not outside it. +Accesses to method `g` can appear anywhere within the package `outerpkg.innerpkg`, as would be the case for package-private methods in Java. +Finally, accesses to method `h` can appear anywhere within package `outerpkg`, including packages contained in it. ###### Example -A useful idiom to prevent clients of a class from -constructing new instances of that class is to declare the class -`abstract` and `sealed`: +A useful idiom to prevent clients of a class from constructing new instances of that class is to declare the class `abstract` and `sealed`: ```scala object m { @@ -663,18 +488,15 @@ object m { } ``` -For instance, in the code above clients can create instances of class -`m.C` only by calling the `nextC` method of an existing `m.C` -object; it is not possible for clients to create objects of class -`m.C` directly. Indeed the following two lines are both in error: +For instance, in the code above clients can create instances of class `m.C` only by calling the `nextC` method of an existing `m.C` object; it is not possible for clients to create objects of class `m.C` directly. +Indeed the following two lines are both in error: ```scala new m.C(0) // **** error: C is abstract, so it cannot be instantiated. new m.C(0) {} // **** error: illegal inheritance from sealed class. ``` -A similar access restriction can be achieved by marking the primary -constructor `private` ([example](#example-private-constructor)). +A similar access restriction can be achieved by marking the primary constructor `private` ([example](#example-private-constructor)). ## Class Definitions @@ -700,31 +522,23 @@ class ´c´[´\mathit{tps}\,´] ´as´ ´m´(´\mathit{ps}_1´)...(´\mathit{ps} Here, - ´c´ is the name of the class to be defined. - - ´\mathit{tps}´ is a non-empty list of type parameters of the class - being defined. The scope of a type parameter is the whole class - definition including the type parameter section itself. It is - illegal to define two type parameters with the same name. The type - parameter section `[´\mathit{tps}\,´]` may be omitted. A class with a type - parameter section is called _polymorphic_, otherwise it is called - _monomorphic_. - - ´as´ is a possibly empty sequence of - [annotations](11-annotations.html#user-defined-annotations). - If any annotations are given, they apply to the primary constructor of the - class. - - ´m´ is an [access modifier](#modifiers) such as - `private` or `protected`, possibly with a qualification. - If such an access modifier is given it applies to the primary constructor of the class. - - ´(\mathit{ps}\_1)...(\mathit{ps}\_n)´ are formal value parameter clauses for - the _primary constructor_ of the class. The scope of a formal value parameter includes - all subsequent parameter sections and the template ´t´. However, a formal - value parameter may not form part of the types of any of the parent classes or members of the class template ´t´. - It is illegal to define two formal value parameters with the same name. + - ´\mathit{tps}´ is a non-empty list of type parameters of the class being defined. + The scope of a type parameter is the whole class definition including the type parameter section itself. + It is illegal to define two type parameters with the same name. + The type parameter section `[´\mathit{tps}\,´]` may be omitted. + A class with a type parameter section is called _polymorphic_, otherwise it is called _monomorphic_. + - ´as´ is a possibly empty sequence of [annotations](11-annotations.html#user-defined-annotations). + If any annotations are given, they apply to the primary constructor of the class. + - ´m´ is an [access modifier](#modifiers) such as `private` or `protected`, possibly with a qualification. + If such an access modifier is given it applies to the primary constructor of the class. + - ´(\mathit{ps}\_1)...(\mathit{ps}\_n)´ are formal value parameter clauses for the _primary constructor_ of the class. + The scope of a formal value parameter includes all subsequent parameter sections and the template ´t´. + However, a formal value parameter may not form part of the types of any of the parent classes or members of the class template ´t´. + It is illegal to define two formal value parameters with the same name. If a class has no formal parameter section that is not implicit, an empty parameter section `()` is assumed. - If a formal parameter declaration ´x: T´ is preceded by a `val` - or `var` keyword, an accessor (getter) [definition](04-basic-declarations-and-definitions.html#variable-declarations-and-definitions) - for this parameter is implicitly added to the class. + If a formal parameter declaration ´x: T´ is preceded by a `val` or `var` keyword, an accessor (getter) [definition](04-basic-declarations-and-definitions.html#variable-declarations-and-definitions) for this parameter is implicitly added to the class. The getter introduces a value member ´x´ of class ´c´ that is defined as an alias of the parameter. If the introducing keyword is `var`, a setter accessor [`´x´_=`](04-basic-declarations-and-definitions.html#variable-declarations-and-definitions) is also implicitly added to the class. @@ -740,18 +554,11 @@ Here, ´sc´ with ´mt_1´ with ... with ´mt_m´ { ´\mathit{stats}´ } // ´m \geq 0´ ``` - which defines the base classes, behavior and initial state of objects of - the class. The extends clause - `extends ´sc´ with ´mt_1´ with ... with ´mt_m´` - can be omitted, in which case - `extends scala.AnyRef` is assumed. The class body - `{ ´\mathit{stats}´ }` may also be omitted, in which case the empty body - `{}` is assumed. + which defines the base classes, behavior and initial state of objects of the class. + The extends clause `extends ´sc´ with ´mt_1´ with ... with ´mt_m´` can be omitted, in which case `extends scala.AnyRef` is assumed. + The class body `{ ´\mathit{stats}´ }` may also be omitted, in which case the empty body `{}` is assumed. -This class definition defines a type `´c´[´\mathit{tps}\,´]` and a constructor -which when applied to parameters conforming to types ´\mathit{ps}´ -initializes instances of type `´c´[´\mathit{tps}\,´]` by evaluating the template -´t´. +This class definition defines a type `´c´[´\mathit{tps}\,´]` and a constructor which when applied to parameters conforming to types ´\mathit{ps}´ initializes instances of type `´c´[´\mathit{tps}\,´]` by evaluating the template ´t´. ###### Example – `val` and `var` parameters The following example illustrates `val` and `var` parameters of a class `C`: @@ -787,45 +594,21 @@ ConstrBlock ::= ‘{’ SelfInvocation {semi BlockStat} ‘}’ SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} ``` -A class may have additional constructors besides the primary -constructor. These are defined by constructor definitions of the form -`def this(´\mathit{ps}_1´)...(´\mathit{ps}_n´) = ´e´`. Such a -definition introduces an additional constructor for the enclosing -class, with parameters as given in the formal parameter lists ´\mathit{ps}_1 -, ..., \mathit{ps}_n´, and whose evaluation is defined by the constructor -expression ´e´. The scope of each formal parameter is the subsequent -parameter sections and the constructor -expression ´e´. A constructor expression is either a self constructor -invocation `this(´\mathit{args}_1´)...(´\mathit{args}_n´)` or a block -which begins with a self constructor invocation. The self constructor -invocation must construct a generic instance of the class. I.e. if the -class in question has name ´C´ and type parameters -`[´\mathit{tps}\,´]`, then a self constructor invocation must -generate an instance of `´C´[´\mathit{tps}\,´]`; it is not permitted -to instantiate formal type parameters. - -The signature and the self constructor invocation of a constructor -definition are type-checked and evaluated in the scope which is in -effect at the point of the enclosing class definition, augmented by -any type parameters of the enclosing class and by any -[early definitions](#early-definitions) of the enclosing template. -The rest of the -constructor expression is type-checked and evaluated as a function -body in the current class. - -If there are auxiliary constructors of a class ´C´, they form together -with ´C´'s primary [constructor](#class-definitions) -an overloaded constructor -definition. The usual rules for -[overloading resolution](06-expressions.html#overloading-resolution) -apply for constructor invocations of ´C´, -including for the self constructor invocations in the constructor -expressions themselves. However, unlike other methods, constructors -are never inherited. To prevent infinite cycles of constructor -invocations, there is the restriction that every self constructor -invocation must refer to a constructor definition which precedes it -(i.e. it must refer to either a preceding auxiliary constructor or the -primary constructor of the class). +A class may have additional constructors besides the primary constructor. +These are defined by constructor definitions of the form `def this(´\mathit{ps}_1´)...(´\mathit{ps}_n´) = ´e´`. +Such a definition introduces an additional constructor for the enclosing class, with parameters as given in the formal parameter lists ´\mathit{ps}_1 , ..., \mathit{ps}_n´, and whose evaluation is defined by the constructor expression ´e´. +The scope of each formal parameter is the subsequent parameter sections and the constructor expression ´e´. +A constructor expression is either a self constructor invocation `this(´\mathit{args}_1´)...(´\mathit{args}_n´)` or a block which begins with a self constructor invocation. +The self constructor invocation must construct a generic instance of the class. +I.e. if the class in question has name ´C´ and type parameters `[´\mathit{tps}\,´]`, then a self constructor invocation must generate an instance of `´C´[´\mathit{tps}\,´]`; it is not permitted to instantiate formal type parameters. + +The signature and the self constructor invocation of a constructor definition are type-checked and evaluated in the scope which is in effect at the point of the enclosing class definition, augmented by any type parameters of the enclosing class and by any [early definitions](#early-definitions) of the enclosing template. +The rest of the constructor expression is type-checked and evaluated as a function body in the current class. + +If there are auxiliary constructors of a class ´C´, they form together with ´C´'s primary [constructor](#class-definitions) an overloaded constructor definition. +The usual rules for [overloading resolution](06-expressions.html#overloading-resolution) apply for constructor invocations of ´C´, including for the self constructor invocations in the constructor expressions themselves. +However, unlike other methods, constructors are never inherited. +To prevent infinite cycles of constructor invocations, there is the restriction that every self constructor invocation must refer to a constructor definition which precedes it (i.e. it must refer to either a preceding auxiliary constructor or the primary constructor of the class). ###### Example Consider the class definition @@ -839,9 +622,8 @@ class LinkedList[A]() { } ``` -This defines a class `LinkedList` with three constructors. The -second constructor constructs a singleton list, while the -third one constructs a list with a given head and tail. +This defines a class `LinkedList` with three constructors. +The second constructor constructs a singleton list, while the third one constructs a list with a given head and tail. ### Case Classes @@ -849,21 +631,16 @@ third one constructs a list with a given head and tail. TmplDef ::= ‘case’ ‘class’ ClassDef ``` -If a class definition is prefixed with `case`, the class is said -to be a _case class_. +If a class definition is prefixed with `case`, the class is said to be a _case class_. A case class is required to have a parameter section that is not implicit. -The formal parameters in the first parameter section -are called _elements_ and are treated specially. -First, the value of such a parameter can be extracted as a -field of a constructor pattern. Second, a `val` prefix is -implicitly added to such a parameter, unless the parameter already carries -a `val` or `var` modifier. Hence, an accessor -definition for the parameter is [generated](#class-definitions). - -A case class definition of `´c´[´\mathit{tps}\,´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` with type -parameters ´\mathit{tps}´ and value parameters ´\mathit{ps}´ implies -the definition of a companion object, which serves as an [extractor object](08-pattern-matching.html#extractor-patterns). It has the following shape: +The formal parameters in the first parameter section are called _elements_ and are treated specially. +First, the value of such a parameter can be extracted as a field of a constructor pattern. +Second, a `val` prefix is implicitly added to such a parameter, unless the parameter already carries a `val` or `var` modifier. +Hence, an accessor definition for the parameter is [generated](#class-definitions). + +A case class definition of `´c´[´\mathit{tps}\,´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` with type parameters ´\mathit{tps}´ and value parameters ´\mathit{ps}´ implies the definition of a companion object, which serves as an [extractor object](08-pattern-matching.html#extractor-patterns). +It has the following shape: ```scala object ´c´ { @@ -874,62 +651,38 @@ object ´c´ { } ``` -Here, ´\mathit{Ts}´ stands for the vector of types defined in the type -parameter section ´\mathit{tps}´, -each ´\mathit{xs}\_i´ denotes the parameter names of the parameter -section ´\mathit{ps}\_i´, and -´\mathit{xs}\_{11}, ... , \mathit{xs}\_{1k}´ denote the names of all parameters -in the first parameter section ´\mathit{xs}\_1´. +Here, ´\mathit{Ts}´ stands for the vector of types defined in the type parameter section ´\mathit{tps}´, each ´\mathit{xs}\_i´ denotes the parameter names of the parameter section ´\mathit{ps}\_i´, and ´\mathit{xs}\_{11}, ... , \mathit{xs}\_{1k}´ denote the names of all parameters in the first parameter section ´\mathit{xs}\_1´. If a type parameter section is missing in the class, it is also missing in the `apply` and `unapply` methods. -If the companion object ´c´ is already defined, -the `apply` and `unapply` methods are added to the existing object. -If the object ´c´ already has a [matching](#definition-matching) -`apply` (or `unapply`) member, no new definition is added. +If the companion object ´c´ is already defined, the `apply` and `unapply` methods are added to the existing object. +If the object ´c´ already has a [matching](#definition-matching) `apply` (or `unapply`) member, no new definition is added. The definition of `apply` is omitted if class ´c´ is `abstract`. -If the case class definition contains an empty value parameter list, the -`unapply` method returns a `Boolean` instead of an `Option` type and -is defined as follows: +If the case class definition contains an empty value parameter list, the `unapply` method returns a `Boolean` instead of an `Option` type and is defined as follows: ```scala def unapply[´\mathit{tps}\,´](´x´: ´c´[´\mathit{tps}\,´]) = x ne null ``` -The name of the `unapply` method is changed to `unapplySeq` if the first -parameter section ´\mathit{ps}_1´ of ´c´ ends in a -[repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters). +The name of the `unapply` method is changed to `unapplySeq` if the first parameter section ´\mathit{ps}_1´ of ´c´ ends in a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters). -A method named `copy` is implicitly added to every case class unless the -class already has a member (directly defined or inherited) with that name, or the -class has a repeated parameter. The method is defined as follows: +A method named `copy` is implicitly added to every case class unless the class already has a member (directly defined or inherited) with that name, or the class has a repeated parameter. +The method is defined as follows: ```scala def copy[´\mathit{tps}\,´](´\mathit{ps}'_1\,´)...(´\mathit{ps}'_n´): ´c´[´\mathit{tps}\,´] = new ´c´[´\mathit{Ts}\,´](´\mathit{xs}_1\,´)...(´\mathit{xs}_n´) ``` -Again, `´\mathit{Ts}´` stands for the vector of types defined in the type parameter section `´\mathit{tps}´` -and each `´xs_i´` denotes the parameter names of the parameter section `´ps'_i´`. The value -parameters `´ps'_{1,j}´` of first parameter list have the form `´x_{1,j}´:´T_{1,j}´=this.´x_{1,j}´`, -the other parameters `´ps'_{i,j}´` of the `copy` method are defined as `´x_{i,j}´:´T_{i,j}´`. -In all cases `´x_{i,j}´` and `´T_{i,j}´` refer to the name and type of the corresponding class parameter -`´\mathit{ps}_{i,j}´`. - -Every case class implicitly overrides some method definitions of class -[`scala.AnyRef`](12-the-scala-standard-library.html#root-classes) unless a definition of the same -method is already given in the case class itself or a concrete -definition of the same method is given in some base class of the case -class different from `AnyRef`. In particular: - -- Method `equals: (Any)Boolean` is structural equality, where two - instances are equal if they both belong to the case class in question and they - have equal (with respect to `equals`) constructor arguments (restricted to the class's _elements_, i.e., the first parameter section). -- Method `hashCode: Int` computes a hash-code. If the hashCode methods - of the data structure members map equal (with respect to equals) - values to equal hash-codes, then the case class hashCode method does - too. -- Method `toString: String` returns a string representation which - contains the name of the class and its elements. +Again, `´\mathit{Ts}´` stands for the vector of types defined in the type parameter section `´\mathit{tps}´` and each `´xs_i´` denotes the parameter names of the parameter section `´ps'_i´`. +The value parameters `´ps'_{1,j}´` of first parameter list have the form `´x_{1,j}´:´T_{1,j}´=this.´x_{1,j}´`, the other parameters `´ps'_{i,j}´` of the `copy` method are defined as `´x_{i,j}´:´T_{i,j}´`. +In all cases `´x_{i,j}´` and `´T_{i,j}´` refer to the name and type of the corresponding class parameter `´\mathit{ps}_{i,j}´`. + +Every case class implicitly overrides some method definitions of class [`scala.AnyRef`](12-the-scala-standard-library.html#root-classes) unless a definition of the same method is already given in the case class itself or a concrete definition of the same method is given in some base class of the case class different from `AnyRef`. +In particular: + +- Method `equals: (Any)Boolean` is structural equality, where two instances are equal if they both belong to the case class in question and they have equal (with respect to `equals`) constructor arguments (restricted to the class's _elements_, i.e., the first parameter section). +- Method `hashCode: Int` computes a hash-code. If the hashCode methods of the data structure members map equal (with respect to equals) values to equal hash-codes, then the case class hashCode method does too. +- Method `toString: String` returns a string representation which contains the name of the class and its elements. ###### Example Here is the definition of abstract syntax for lambda calculus: @@ -941,9 +694,7 @@ case class Apply (f: Expr, e: Expr) extends Expr case class Lambda(x: String, e: Expr) extends Expr ``` -This defines a class `Expr` with case classes -`Var`, `Apply` and `Lambda`. A call-by-value evaluator -for lambda expressions could then be written as follows. +This defines a class `Expr` with case classes `Var`, `Apply` and `Lambda`. A call-by-value evaluator for lambda expressions could then be written as follows. ```scala type Env = String => Value @@ -961,17 +712,13 @@ def eval(e: Expr, env: Env): Value = e match { } ``` -It is possible to define further case classes that extend type -`Expr` in other parts of the program, for instance +It is possible to define further case classes that extend type `Expr` in other parts of the program, for instance ```scala case class Number(x: Int) extends Expr ``` -This form of extensibility can be excluded by declaring the base class -`Expr` `sealed`; in this case, all classes that -directly extend `Expr` must be in the same source file as -`Expr`. +This form of extensibility can be excluded by declaring the base class `Expr` `sealed`; in this case, all classes that directly extend `Expr` must be in the same source file as `Expr`. ## Traits @@ -981,28 +728,21 @@ TraitDef ::= id [TypeParamClause] TraitTemplateOpt TraitTemplateOpt ::= ‘extends’ TraitTemplate | [[‘extends’] TemplateBody] ``` -A _trait_ is a class that is meant to be added to some other class -as a mixin. Unlike normal classes, traits cannot have -constructor parameters. Furthermore, no constructor arguments are -passed to the superclass of the trait. This is not necessary as traits are -initialized after the superclass is initialized. +A _trait_ is a class that is meant to be added to some other class as a mixin. +Unlike normal classes, traits cannot have constructor parameters. +Furthermore, no constructor arguments are passed to the superclass of the trait. +This is not necessary as traits are initialized after the superclass is initialized. Assume a trait ´D´ defines some aspect of an instance ´x´ of type ´C´ (i.e. ´D´ is a base class of ´C´). -Then the _actual supertype_ of ´D´ in ´x´ is the compound type consisting of all the -base classes in ´\mathcal{L}(C)´ that succeed ´D´. The actual supertype gives -the context for resolving a [`super` reference](06-expressions.html#this-and-super) in a trait. -Note that the actual supertype depends on the type to which the trait is added in a mixin composition; -it is not statically known at the time the trait is defined. +Then the _actual supertype_ of ´D´ in ´x´ is the compound type consisting of all the base classes in ´\mathcal{L}(C)´ that succeed ´D´. +The actual supertype gives the context for resolving a [`super` reference](06-expressions.html#this-and-super) in a trait. +Note that the actual supertype depends on the type to which the trait is added in a mixin composition; it is not statically known at the time the trait is defined. -If ´D´ is not a trait, then its actual supertype is simply its -least proper supertype (which is statically known). +If ´D´ is not a trait, then its actual supertype is simply its least proper supertype (which is statically known). ###### Example -The following trait defines the property -of being comparable to objects of some type. It contains an abstract -method `<` and default implementations of the other -comparison operators `<=`, `>`, and -`>=`. +The following trait defines the property of being comparable to objects of some type. +It contains an abstract method `<` and default implementations of the other comparison operators `<=`, `>`, and `>=`. ```scala trait Comparable[T <: Comparable[T]] { self: T => @@ -1014,13 +754,10 @@ trait Comparable[T <: Comparable[T]] { self: T => ``` ###### Example -Consider an abstract class `Table` that implements maps -from a type of keys `A` to a type of values `B`. The class -has a method `set` to enter a new key / value pair into the table, -and a method `get` that returns an optional value matching a -given key. Finally, there is a method `apply` which is like -`get`, except that it returns a given default value if the table -is undefined for the given key. This class is implemented as follows. +Consider an abstract class `Table` that implements maps from a type of keys `A` to a type of values `B`. +The class has a method `set` to enter a new key / value pair into the table, and a method `get` that returns an optional value matching a given key. +Finally, there is a method `apply` which is like `get`, except that it returns a given default value if the table is undefined for the given key. +This class is implemented as follows. ```scala abstract class Table[A, B](defaultValue: B) { @@ -1043,8 +780,7 @@ class ListTable[A, B](defaultValue: B) extends Table[A, B](defaultValue) { } ``` -Here is a trait that prevents concurrent access to the -`get` and `set` operations of its parent class: +Here is a trait that prevents concurrent access to the `get` and `set` operations of its parent class: ```scala trait SynchronizedTable[A, B] extends Table[A, B] { @@ -1055,27 +791,18 @@ trait SynchronizedTable[A, B] extends Table[A, B] { } ``` -Note that `SynchronizedTable` does not pass an argument to -its superclass, `Table`, even though `Table` is defined with a -formal parameter. Note also that the `super` calls -in `SynchronizedTable`'s `get` and `set` methods -statically refer to abstract methods in class `Table`. This is -legal, as long as the calling method is labeled -[`abstract override`](#modifiers). +Note that `SynchronizedTable` does not pass an argument to its superclass, `Table`, even though `Table` is defined with a formal parameter. +Note also that the `super` calls in `SynchronizedTable`'s `get` and `set` methods statically refer to abstract methods in class `Table`. +This is legal, as long as the calling method is labeled [`abstract override`](#modifiers). -Finally, the following mixin composition creates a synchronized list -table with strings as keys and integers as values and with a default -value `0`: +Finally, the following mixin composition creates a synchronized list table with strings as keys and integers as values and with a default value `0`: ```scala object MyTable extends ListTable[String, Int](0) with SynchronizedTable[String, Int] ``` -The object `MyTable` inherits its `get` and `set` -method from `SynchronizedTable`. The `super` calls in these -methods are re-bound to refer to the corresponding implementations in -`ListTable`, which is the actual supertype of `SynchronizedTable` -in `MyTable`. +The object `MyTable` inherits its `get` and `set` method from `SynchronizedTable`. +The `super` calls in these methods are re-bound to refer to the corresponding implementations in `ListTable`, which is the actual supertype of `SynchronizedTable` in `MyTable`. ## Object Definitions @@ -1083,50 +810,36 @@ in `MyTable`. ObjectDef ::= id ClassTemplate ``` -An _object definition_ defines a single object of a new class. Its -most general form is -`object ´m´ extends ´t´`. Here, -´m´ is the name of the object to be defined, and -´t´ is a [template](#templates) of the form +An _object definition_ defines a single object of a new class. +Its most general form is `object ´m´ extends ´t´`. +Here, ´m´ is the name of the object to be defined, and ´t´ is a [template](#templates) of the form ```scala ´sc´ with ´mt_1´ with ... with ´mt_n´ { ´\mathit{stats}´ } ``` which defines the base classes, behavior and initial state of ´m´. -The extends clause `extends ´sc´ with ´mt_1´ with ... with ´mt_n´` -can be omitted, in which case -`extends scala.AnyRef` is assumed. The class body -`{ ´\mathit{stats}´ }` may also be omitted, in which case the empty body -`{}` is assumed. +The extends clause `extends ´sc´ with ´mt_1´ with ... with ´mt_n´` can be omitted, in which case `extends scala.AnyRef` is assumed. +The class body `{ ´\mathit{stats}´ }` may also be omitted, in which case the empty body `{}` is assumed. -The object definition defines a single object (or: _module_) -conforming to the template ´t´. It is roughly equivalent to the -following definition of a lazy value: +The object definition defines a single object (or: _module_) conforming to the template ´t´. +It is roughly equivalent to the following definition of a lazy value: ```scala lazy val ´m´ = new ´sc´ with ´mt_1´ with ... with ´mt_n´ { this: ´m.type´ => ´\mathit{stats}´ } ``` -Note that the value defined by an object definition is instantiated -lazily. The `new ´m´$cls` constructor is evaluated -not at the point of the object definition, but is instead evaluated -the first time ´m´ is dereferenced during execution of the program -(which might be never at all). An attempt to dereference ´m´ again -during evaluation of the constructor will lead to an infinite loop -or run-time error. -Other threads trying to dereference ´m´ while the -constructor is being evaluated block until evaluation is complete. - -The expansion given above is not accurate for top-level objects. It -cannot be because variable and method definition cannot appear on the -top-level outside of a [package object](09-top-level-definitions.html#package-objects). Instead, -top-level objects are translated to static fields. +Note that the value defined by an object definition is instantiated lazily. +The `new ´m´$cls` constructor is evaluated not at the point of the object definition, but is instead evaluated the first time ´m´ is dereferenced during execution of the program (which might be never at all). +An attempt to dereference ´m´ again during evaluation of the constructor will lead to an infinite loop or run-time error. +Other threads trying to dereference ´m´ while the constructor is being evaluated block until evaluation is complete. + +The expansion given above is not accurate for top-level objects. +It cannot be because variable and method definition cannot appear on the top-level outside of a [package object](09-top-level-definitions.html#package-objects). +Instead, top-level objects are translated to static fields. ###### Example -Classes in Scala do not have static members; however, an equivalent -effect can be achieved by an accompanying object definition -E.g. +Classes in Scala do not have static members; however, an equivalent effect can be achieved by an accompanying object definition E.g. ```scala abstract class Point { @@ -1139,22 +852,13 @@ object Point { } ``` -This defines a class `Point` and an object `Point` which -contains `origin` as a member. Note that the double use of the -name `Point` is legal, since the class definition defines the -name `Point` in the type name space, whereas the object -definition defines a name in the term namespace. - -This technique is applied by the Scala compiler when interpreting a -Java class with static members. Such a class ´C´ is conceptually seen -as a pair of a Scala class that contains all instance members of ´C´ -and a Scala object that contains all static members of ´C´. - -Generally, a _companion module_ of a class is an object which has -the same name as the class and is defined in the same scope and -compilation unit. Conversely, the class is called the _companion class_ -of the module. - -Very much like a concrete class definition, an object definition may -still contain declarations of abstract type members, but not of -abstract term members. +This defines a class `Point` and an object `Point` which contains `origin` as a member. +Note that the double use of the name `Point` is legal, since the class definition defines the name `Point` in the type name space, whereas the object definition defines a name in the term namespace. + +This technique is applied by the Scala compiler when interpreting a Java class with static members. +Such a class ´C´ is conceptually seen as a pair of a Scala class that contains all instance members of ´C´ and a Scala object that contains all static members of ´C´. + +Generally, a _companion module_ of a class is an object which has the same name as the class and is defined in the same scope and compilation unit. +Conversely, the class is called the _companion class_ of the module. + +Very much like a concrete class definition, an object definition may still contain declarations of abstract type members, but not of abstract term members. diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index 36c31af8969c..597756550e6f 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -47,23 +47,21 @@ Ascription ::= ‘:’ InfixType | ‘:’ ‘_’ ‘*’ ``` -Expressions are composed of operators and operands. Expression forms are -discussed subsequently in decreasing order of precedence. +Expressions are composed of operators and operands. +Expression forms are discussed subsequently in decreasing order of precedence. ## Expression Typing -The typing of expressions is often relative to some _expected type_ (which might be undefined). When we write "expression ´e´ is expected to conform to type ´T´", we mean: +The typing of expressions is often relative to some _expected type_ (which might be undefined). +When we write "expression ´e´ is expected to conform to type ´T´", we mean: 1. the expected type of ´e´ is ´T´, and 2. the type of expression ´e´ must conform to ´T´. -The following skolemization rule is applied universally for every -expression: If the type of an expression would be an existential type -´T´, then the type of the expression is assumed instead to be a -[skolemization](03-types.html#existential-types) of ´T´. +The following skolemization rule is applied universally for every expression: +If the type of an expression would be an existential type ´T´, then the type of the expression is assumed instead to be a [skolemization](03-types.html#existential-types) of ´T´. -Skolemization is reversed by type packing. Assume an expression ´e´ of -type ´T´ and let ´t_1[\mathit{tps}\_1] >: L_1 <: U_1, ..., t_n[\mathit{tps}\_n] >: L_n <: U_n´ be -all the type variables created by skolemization of some part of ´e´ which are free in ´T´. +Skolemization is reversed by type packing. +Assume an expression ´e´ of type ´T´ and let ´t_1[\mathit{tps}\_1] >: L_1 <: U_1, ..., t_n[\mathit{tps}\_n] >: L_n <: U_n´ be all the type variables created by skolemization of some part of ´e´ which are free in ´T´. Then the _packed type_ of ´e´ is ```scala @@ -76,8 +74,7 @@ Then the _packed type_ of ´e´ is SimpleExpr ::= Literal ``` -Typing of literals is described along with their [lexical syntax](01-lexical-syntax.html#literals); -their evaluation is immediate. +Typing of literals is described along with their [lexical syntax](01-lexical-syntax.html#literals); their evaluation is immediate. ## The _Null_ Value @@ -85,16 +82,13 @@ The `null` value is of type `scala.Null`, and thus conforms to every reference t It denotes a reference value which refers to a special `null` object. This object implements methods in class `scala.AnyRef` as follows: -- `eq(´x\,´)` and `==(´x\,´)` return `true` iff the - argument ´x´ is also the "null" object. -- `ne(´x\,´)` and `!=(´x\,´)` return true iff the - argument x is not also the "null" object. +- `eq(´x\,´)` and `==(´x\,´)` return `true` iff the argument ´x´ is also the "null" object. +- `ne(´x\,´)` and `!=(´x\,´)` return true iff the argument x is not also the "null" object. - `isInstanceOf[´T\,´]` always returns `false`. - `asInstanceOf[´T\,´]` returns the [default value](04-basic-declarations-and-definitions.html#value-declarations-and-definitions) of type ´T´. - `##` returns ``0``. -A reference to any other member of the "null" object causes a -`NullPointerException` to be thrown. +A reference to any other member of the "null" object causes a `NullPointerException` to be thrown. ## Designators @@ -103,50 +97,31 @@ SimpleExpr ::= Path | SimpleExpr ‘.’ id ``` -A designator refers to a named term. It can be a _simple name_ or -a _selection_. +A designator refers to a named term. It can be a _simple name_ or a _selection_. -A simple name ´x´ refers to a value as specified -[here](02-identifiers-names-and-scopes.html#identifiers,-names-and-scopes). -If ´x´ is bound by a definition or declaration in an enclosing class -or object ´C´, it is taken to be equivalent to the selection -`´C´.this.´x´` where ´C´ is taken to refer to the class containing ´x´ -even if the type name ´C´ is [shadowed](02-identifiers-names-and-scopes.html#identifiers,-names-and-scopes) at the -occurrence of ´x´. +A simple name ´x´ refers to a value as specified [here](02-identifiers-names-and-scopes.html#identifiers,-names-and-scopes). +If ´x´ is bound by a definition or declaration in an enclosing class or object ´C´, it is taken to be equivalent to the selection `´C´.this.´x´` where ´C´ is taken to refer to the class containing ´x´ even if the type name ´C´ is [shadowed](02-identifiers-names-and-scopes.html#identifiers,-names-and-scopes) at the occurrence of ´x´. -If ´r´ is a [stable identifier](03-types.html#paths) of type ´T´, the selection ´r.x´ refers -statically to a term member ´m´ of ´r´ that is identified in ´T´ by -the name ´x´. +If ´r´ is a [stable identifier](03-types.html#paths) of type ´T´, the selection ´r.x´ refers statically to a term member ´m´ of ´r´ that is identified in ´T´ by the name ´x´. -For other expressions ´e´, ´e.x´ is typed as -if it was `{ val ´y´ = ´e´; ´y´.´x´ }`, for some fresh name -´y´. +For other expressions ´e´, ´e.x´ is typed as if it was `{ val ´y´ = ´e´; ´y´.´x´ }`, for some fresh name ´y´. -The expected type of a designator's prefix is always undefined. The -type of a designator is the type ´T´ of the entity it refers to, with -the following exception: The type of a [path](03-types.html#paths) ´p´ -which occurs in a context where a [stable type](03-types.html#singleton-types) -is required is the singleton type `´p´.type`. +The expected type of a designator's prefix is always undefined. +The type of a designator is the type ´T´ of the entity it refers to, with the following exception: The type of a [path](03-types.html#paths) ´p´ which occurs in a context where a [stable type](03-types.html#singleton-types) is required is the singleton type `´p´.type`. -The contexts where a stable type is required are those that satisfy -one of the following conditions: +The contexts where a stable type is required are those that satisfy one of the following conditions: -1. The path ´p´ occurs as the prefix of a selection and it does not -designate a constant, or +1. The path ´p´ occurs as the prefix of a selection and it does not designate a constant, or 1. The expected type ´\mathit{pt}´ is a stable type, or -1. The expected type ´\mathit{pt}´ is an abstract type with a stable type as lower - bound, and the type ´T´ of the entity referred to by ´p´ does not - conform to ´\mathit{pt}´, or +1. The expected type ´\mathit{pt}´ is an abstract type with a stable type as lower bound, and the type ´T´ of the entity referred to by ´p´ does not conform to ´\mathit{pt}´, or 1. The path ´p´ designates a module. -The selection ´e.x´ is evaluated by first evaluating the qualifier -expression ´e´, which yields an object ´r´, say. The selection's -result is then the member of ´r´ that is either defined by ´m´ or defined -by a definition overriding ´m´. +The selection ´e.x´ is evaluated by first evaluating the qualifier expression ´e´, which yields an object ´r´, say. +The selection's result is then the member of ´r´ that is either defined by ´m´ or defined by a definition overriding ´m´. ## This and Super @@ -155,52 +130,34 @@ SimpleExpr ::= [id ‘.’] ‘this’ | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id ``` -The expression `this` can appear in the statement part of a -template or compound type. It stands for the object being defined by -the innermost template or compound type enclosing the reference. If -this is a compound type, the type of `this` is that compound type. -If it is a template of a -class or object definition with simple name ´C´, the type of this -is the same as the type of `´C´.this`. - -The expression `´C´.this` is legal in the statement part of an -enclosing class or object definition with simple name ´C´. It -stands for the object being defined by the innermost such definition. -If the expression's expected type is a stable type, or -`´C´.this` occurs as the prefix of a selection, its type is -`´C´.this.type`, otherwise it is the self type of class ´C´. - -A reference `super.´m´` refers statically to a method or type ´m´ -in the least proper supertype of the innermost template containing the -reference. It evaluates to the member ´m'´ in the actual supertype of -that template which is equal to ´m´ or which overrides ´m´. The -statically referenced member ´m´ must be a type or a -method. +The expression `this` can appear in the statement part of a template or compound type. +It stands for the object being defined by the innermost template or compound type enclosing the reference. +If this is a compound type, the type of `this` is that compound type. +If it is a template of a class or object definition with simple name ´C´, the type of this is the same as the type of `´C´.this`. + +The expression `´C´.this` is legal in the statement part of an enclosing class or object definition with simple name ´C´. +It stands for the object being defined by the innermost such definition. +If the expression's expected type is a stable type, or `´C´.this` occurs as the prefix of a selection, its type is `´C´.this.type`, otherwise it is the self type of class ´C´. + +A reference `super.´m´` refers statically to a method or type ´m´ in the least proper supertype of the innermost template containing the reference. +It evaluates to the member ´m'´ in the actual supertype of that template which is equal to ´m´ or which overrides ´m´. +The statically referenced member ´m´ must be a type or a method. -If it is -a method, it must be concrete, or the template -containing the reference must have a member ´m'´ which overrides ´m´ -and which is labeled `abstract override`. - -A reference `´C´.super.´m´` refers statically to a method -or type ´m´ in the least proper supertype of the innermost enclosing class or -object definition named ´C´ which encloses the reference. It evaluates -to the member ´m'´ in the actual supertype of that class or object -which is equal to ´m´ or which overrides ´m´. The -statically referenced member ´m´ must be a type or a -method. If the statically -referenced member ´m´ is a method, it must be concrete, or the innermost enclosing -class or object definition named ´C´ must have a member ´m'´ which -overrides ´m´ and which is labeled `abstract override`. - -The `super` prefix may be followed by a trait qualifier -`[´T\,´]`, as in `´C´.super[´T\,´].´x´`. This is -called a _static super reference_. In this case, the reference is -to the type or method of ´x´ in the parent trait of ´C´ whose simple -name is ´T´. That member must be uniquely defined. If it is a method, -it must be concrete. +If it is a method, it must be concrete, or the template containing the reference must have a member ´m'´ which overrides ´m´ and which is labeled `abstract override`. + +A reference `´C´.super.´m´` refers statically to a method or type ´m´ in the least proper supertype of the innermost enclosing class or object definition named ´C´ which encloses the reference. +It evaluates to the member ´m'´ in the actual supertype of that class or object +which is equal to ´m´ or which overrides ´m´. +The statically referenced member ´m´ must be a type or a method. +If the statically referenced member ´m´ is a method, it must be concrete, or the innermost enclosing class or object definition named ´C´ must have a member ´m'´ which overrides ´m´ and which is labeled `abstract override`. + +The `super` prefix may be followed by a trait qualifier `[´T\,´]`, as in `´C´.super[´T\,´].´x´`. +This is called a _static super reference_. +In this case, the reference is to the type or method of ´x´ in the parent trait of ´C´ whose simple name is ´T´. +That member must be uniquely defined. +If it is a method, it must be concrete. ###### Example Consider the following class definitions @@ -217,8 +174,7 @@ class D extends A with B { } ``` -The linearization of class `C` is `{C, B, Root}` and -the linearization of class `D` is `{D, B, A, Root}`. +The linearization of class `C` is `{C, B, Root}` and the linearization of class `D` is `{D, B, A, Root}`. Then we have: ```scala @@ -232,8 +188,7 @@ Then we have: (new D).superD == "B" ``` -Note that the `superB` method returns different results -depending on whether `B` is mixed in with class `Root` or `A`. +Note that the `superB` method returns different results depending on whether `B` is mixed in with class `Root` or `A`. ## Function Applications @@ -246,9 +201,13 @@ ArgumentExprs ::= ‘(’ [Exprs] ‘)’ Exprs ::= Expr {‘,’ Expr} ``` -An application `´f(e_1, ..., e_m)´` applies the function `´f´` to the argument expressions `´e_1, ..., e_m´`. For this expression to be well-typed, the function must be *applicable* to its arguments, which is defined next by case analysis on ´f´'s type. +An application `´f(e_1, ..., e_m)´` applies the function `´f´` to the argument expressions `´e_1, ..., e_m´`. +For this expression to be well-typed, the function must be *applicable* to its arguments, which is defined next by case analysis on ´f´'s type. -If ´f´ has a method type `(´p_1´:´T_1, ..., p_n´:´T_n´)´U´`, each argument expression ´e_i´ is typed with the corresponding parameter type ´T_i´ as expected type. Let ´S_i´ be the type of argument ´e_i´ ´(i = 1, ..., m)´. The method ´f´ must be _applicable_ to its arguments ´e_1, ..., e_n´ of types ´S_1, ..., S_n´. We say that an argument expression ´e_i´ is a _named_ argument if it has the form `´x_i=e'_i´` and `´x_i´` is one of the parameter names `´p_1, ..., p_n´`. +If ´f´ has a method type `(´p_1´:´T_1, ..., p_n´:´T_n´)´U´`, each argument expression ´e_i´ is typed with the corresponding parameter type ´T_i´ as expected type. +Let ´S_i´ be the type of argument ´e_i´ ´(i = 1, ..., m)´. +The method ´f´ must be _applicable_ to its arguments ´e_1, ..., e_n´ of types ´S_1, ..., S_n´. +We say that an argument expression ´e_i´ is a _named_ argument if it has the form `´x_i=e'_i´` and `´x_i´` is one of the parameter names `´p_1, ..., p_n´`. Once the types ´S_i´ have been determined, the method ´f´ of the above method type is said to be applicable if all of the following conditions hold: - for every named argument ´p_j=e_i'´ the type ´S_i´ is [compatible](03-types.html#compatibility) with the parameter type ´T_j´; @@ -258,57 +217,37 @@ Once the types ´S_i´ have been determined, the method ´f´ of the above metho If ´f´ is a polymorphic method, [local type inference](#local-type-inference) is used to instantiate ´f´'s type parameters. The polymorphic method is applicable if type inference can determine type arguments so that the instantiated method is applicable. -If ´f´ has some value type, the application is taken to be equivalent to `´f´.apply(´e_1, ..., e_m´)`, -i.e. the application of an `apply` method defined by ´f´. The value `´f´` is applicable to the given arguments if `´f´.apply` is applicable. - - -The application `´f´(´e_1, ..., e_n´)` evaluates ´f´ and then each argument -´e_1, ..., e_n´ from left to right, except for arguments that correspond to -a by-name parameter (see below). Each argument expression is converted to the -type of its corresponding formal parameter. After that, the application is -rewritten to the function's right hand side, with actual arguments substituted -for formal parameters. The result of evaluating the rewritten right-hand side -is finally converted to the function's declared result type, if one is given. - -The case of a formal parameter with a parameterless -method type `=> ´T´` is treated specially. In this case, the -corresponding actual argument expression ´e´ is not evaluated before the -application. Instead, every use of the formal parameter on the -right-hand side of the rewrite rule entails a re-evaluation of ´e´. -In other words, the evaluation order for -`=>`-parameters is _call-by-name_ whereas the evaluation -order for normal parameters is _call-by-value_. -Furthermore, it is required that ´e´'s [packed type](#expression-typing) -conforms to the parameter type ´T´. -The behavior of by-name parameters is preserved if the application is -transformed into a block due to named or default arguments. In this case, -the local value for that parameter has the form `val ´y_i´ = () => ´e´` -and the argument passed to the function is `´y_i´()`. - -The last argument in an application may be marked as a sequence -argument, e.g. `´e´: _*`. Such an argument must correspond -to a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters) of type -`´S´*` and it must be the only argument matching this -parameter (i.e. the number of formal parameters and actual arguments -must be the same). Furthermore, the type of ´e´ must conform to -`scala.Seq[´T´]`, for some type ´T´ which conforms to -´S´. In this case, the argument list is transformed by replacing the -sequence ´e´ with its elements. When the application uses named -arguments, the vararg parameter has to be specified exactly once. - -If only a single argument is supplied, it may be supplied as a block expression -and parentheses can be omitted, in the form `´f´ { block }`. This is valid when -`f` has a single formal parameter or when all other formal parameters have -default values. - -A function application usually allocates a new frame on the program's -run-time stack. However, if a local method or a final method calls -itself as its last action, the call is executed using the stack-frame -of the caller. +If ´f´ has some value type, the application is taken to be equivalent to `´f´.apply(´e_1, ..., e_m´)`, i.e. the application of an `apply` method defined by ´f´. +The value `´f´` is applicable to the given arguments if `´f´.apply` is applicable. + + +The application `´f´(´e_1, ..., e_n´)` evaluates ´f´ and then each argument ´e_1, ..., e_n´ from left to right, except for arguments that correspond to a by-name parameter (see below). +Each argument expression is converted to the type of its corresponding formal parameter. +After that, the application is rewritten to the function's right hand side, with actual arguments substituted for formal parameters. +The result of evaluating the rewritten right-hand side is finally converted to the function's declared result type, if one is given. + +The case of a formal parameter with a parameterless method type `=> ´T´` is treated specially. +In this case, the corresponding actual argument expression ´e´ is not evaluated before the application. +Instead, every use of the formal parameter on the right-hand side of the rewrite rule entails a re-evaluation of ´e´. +In other words, the evaluation order for `=>`-parameters is _call-by-name_ whereas the evaluation order for normal parameters is _call-by-value_. +Furthermore, it is required that ´e´'s [packed type](#expression-typing) conforms to the parameter type ´T´. +The behavior of by-name parameters is preserved if the application is transformed into a block due to named or default arguments. +In this case, the local value for that parameter has the form `val ´y_i´ = () => ´e´` and the argument passed to the function is `´y_i´()`. + +The last argument in an application may be marked as a sequence argument, e.g. `´e´: _*`. +Such an argument must correspond to a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters) of type `´S´*` and it must be the only argument matching this parameter (i.e. the number of formal parameters and actual arguments must be the same). +Furthermore, the type of ´e´ must conform to `scala.Seq[´T´]`, for some type ´T´ which conforms to ´S´. +In this case, the argument list is transformed by replacing the sequence ´e´ with its elements. +When the application uses named arguments, the vararg parameter has to be specified exactly once. + +If only a single argument is supplied, it may be supplied as a block expression and parentheses can be omitted, in the form `´f´ { block }`. +This is valid when `f` has a single formal parameter or when all other formal parameters have default values. + +A function application usually allocates a new frame on the program's run-time stack. +However, if a local method or a final method calls itself as its last action, the call is executed using the stack-frame of the caller. ###### Example -Assume the following method which computes the sum of a -variable number of arguments: +Assume the following method which computes the sum of a variable number of arguments: ```scala def sum(xs: Int*) = xs.foldLeft(0)((x, y) => x + y) @@ -321,7 +260,8 @@ sum(1, 2, 3, 4) sum(List(1, 2, 3, 4): _*) ``` -both yield `10` as result. On the other hand, +both yield `10` as result. +On the other hand, ```scala sum(List(1, 2, 3, 4)) @@ -334,25 +274,15 @@ The keyword is ignored. ### Named and Default Arguments -If an application is to use named arguments ´p = e´ or default -arguments, the following conditions must hold. +If an application is to use named arguments ´p = e´ or default arguments, the following conditions must hold. -- For every named argument ´p_i = e_i´ which appears left of a positional argument - in the argument list ´e_1 ... e_m´, the argument position ´i´ coincides with - the position of parameter ´p_i´ in the parameter list of the applied method. -- The names ´x_i´ of all named arguments are pairwise distinct and no named - argument defines a parameter which is already specified by a - positional argument. -- Every formal parameter ´p_j:T_j´ which is not specified by either a positional - or named argument has a default argument. +- For every named argument ´p_i = e_i´ which appears left of a positional argument in the argument list ´e_1 ... e_m´, the argument position ´i´ coincides with the position of parameter ´p_i´ in the parameter list of the applied method. +- The names ´x_i´ of all named arguments are pairwise distinct and no named argument defines a parameter which is already specified by a positional argument. +- Every formal parameter ´p_j:T_j´ which is not specified by either a positional or named argument has a default argument. -If the application uses named or default -arguments the following transformation is applied to convert it into -an application without named or default arguments. +If the application uses named or default arguments the following transformation is applied to convert it into an application without named or default arguments. -If the method ´f´ -has the form `´p.m´[´\mathit{targs}´]` it is transformed into the -block +If the method ´f´ has the form `´p.m´[´\mathit{targs}´]` it is transformed into the block ```scala { val q = ´p´ @@ -360,9 +290,8 @@ block } ``` -If the method ´f´ is itself an application expression the transformation -is applied recursively on ´f´. The result of transforming ´f´ is a block of -the form +If the method ´f´ is itself an application expression the transformation is applied recursively on ´f´. +The result of transforming ´f´ is a block of the form ```scala { val q = ´p´ @@ -373,20 +302,11 @@ the form } ``` -where every argument in ´(\mathit{args}\_1), ..., (\mathit{args}\_l)´ is a reference to -one of the values ´x_1, ..., x_k´. To integrate the current application -into the block, first a value definition using a fresh name ´y_i´ is created -for every argument in ´e_1, ..., e_m´, which is initialised to ´e_i´ for -positional arguments and to ´e'_i´ for named arguments of the form -`´x_i=e'_i´`. Then, for every parameter which is not specified -by the argument list, a value definition using a fresh name ´z_i´ is created, -which is initialized using the method computing the -[default argument](04-basic-declarations-and-definitions.html#function-declarations-and-definitions) of -this parameter. - -Let ´\mathit{args}´ be a permutation of the generated names ´y_i´ and ´z_i´ such such -that the position of each name matches the position of its corresponding -parameter in the method type `(´p_1:T_1, ..., p_n:T_n´)´U´`. +where every argument in ´(\mathit{args}\_1), ..., (\mathit{args}\_l)´ is a reference to one of the values ´x_1, ..., x_k´. +To integrate the current application into the block, first a value definition using a fresh name ´y_i´ is created for every argument in ´e_1, ..., e_m´, which is initialised to ´e_i´ for positional arguments and to ´e'_i´ for named arguments of the form `´x_i=e'_i´`. +Then, for every parameter which is not specified by the argument list, a value definition using a fresh name ´z_i´ is created, which is initialized using the method computing the [default argument](04-basic-declarations-and-definitions.html#function-declarations-and-definitions) of this parameter. + +Let ´\mathit{args}´ be a permutation of the generated names ´y_i´ and ´z_i´ such such that the position of each name matches the position of its corresponding parameter in the method type `(´p_1:T_1, ..., p_n:T_n´)´U´`. The final result of the transformation is a block of the form ```scala @@ -406,20 +326,15 @@ The final result of the transformation is a block of the form ### Signature Polymorphic Methods -For invocations of signature polymorphic methods of the target platform `´f´(´e_1, ..., e_m´)`, -the invoked method has a different method type `(´p_1´:´T_1, ..., p_n´:´T_n´)´U´` at each call -site. The parameter types `´T_, ..., T_n´` are the types of the argument expressions -`´e_1, ..., e_m´`. If the declared return type `´R´` of the signature polymorphic method is -any type other than `scala.AnyRef`, then the return type `´U´` is `´R´`. -Otherwise, `´U´` is the expected type at the call site. If the expected type is undefined then -`´U´` is `scala.AnyRef`. The parameter names `´p_1, ..., p_n´` are fresh. +For invocations of signature polymorphic methods of the target platform `´f´(´e_1, ..., e_m´)`, the invoked method has a different method type `(´p_1´:´T_1, ..., p_n´:´T_n´)´U´` at each call site. +The parameter types `´T_, ..., T_n´` are the types of the argument expressions `´e_1, ..., e_m´`. +If the declared return type `´R´` of the signature polymorphic method is any type other than `scala.AnyRef`, then the return type `´U´` is `´R´`. +Otherwise, `´U´` is the expected type at the call site. If the expected type is undefined then `´U´` is `scala.AnyRef`. +The parameter names `´p_1, ..., p_n´` are fresh. ###### Note -On the Java platform version 11 and later, signature polymorphic methods are native, -members of `java.lang.invoke.MethodHandle` or `java.lang.invoke.VarHandle`, and have a single -repeated parameter of type `java.lang.Object*`. - +On the Java platform version 11 and later, signature polymorphic methods are native, members of `java.lang.invoke.MethodHandle` or `java.lang.invoke.VarHandle`, and have a single repeated parameter of type `java.lang.Object*`. ## Method Values @@ -428,13 +343,9 @@ SimpleExpr ::= SimpleExpr1 ‘_’ ``` The expression `´e´ _` is well-formed if ´e´ is of method -type or if ´e´ is a call-by-name parameter. If ´e´ is a method with -parameters, `´e´ _` represents ´e´ converted to a function -type by [eta expansion](#eta-expansion-section). If ´e´ is a -parameterless method or call-by-name parameter of type -`=> ´T´`, `´e´ _` represents the function of type -`() => ´T´`, which evaluates ´e´ when it is applied to the empty -parameter list `()`. +type or if ´e´ is a call-by-name parameter. +If ´e´ is a method with parameters, `´e´ _` represents ´e´ converted to a function type by [eta expansion](#eta-expansion-section). +If ´e´ is a parameterless method or call-by-name parameter of type `=> ´T´`, `´e´ _` represents the function of type `() => ´T´`, which evaluates ´e´ when it is applied to the empty parameter list `()`. ###### Example The method values in the left column are each equivalent to the [eta-expanded expressions](#eta-expansion-section) on the right. @@ -447,8 +358,7 @@ The method values in the left column are each equivalent to the [eta-expanded ex |`(1 to 9).fold(z)_` | `{ val eta1 = 1 to 9; val eta2 = z; op => eta1.fold(eta2)(op) }` | |`Some(1).fold(??? : Int)_` | `{ val eta1 = Some(1); val eta2 = () => ???; op => eta1.fold(eta2())(op) }` | -Note that a space is necessary between a method name and the trailing underscore -because otherwise the underscore would be considered part of the name. +Note that a space is necessary between a method name and the trailing underscore because otherwise the underscore would be considered part of the name. ## Type Applications @@ -456,25 +366,15 @@ because otherwise the underscore would be considered part of the name. SimpleExpr ::= SimpleExpr TypeArgs ``` -A _type application_ `´e´[´T_1, ..., T_n´]` instantiates -a polymorphic value ´e´ of type -`[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´S´` -with argument types -`´T_1, ..., T_n´`. Every argument type ´T_i´ must obey -the corresponding bounds ´L_i´ and ´U_i´. That is, for each ´i = 1 -, ..., n´, we must have ´\sigma L_i <: T_i <: \sigma -U_i´, where ´\sigma´ is the substitution ´[a_1 := T_1, ..., a_n -:= T_n]´. The type of the application is ´\sigma S´. +A _type application_ `´e´[´T_1, ..., T_n´]` instantiates a polymorphic value ´e´ of type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´S´` with argument types `´T_1, ..., T_n´`. +Every argument type ´T_i´ must obey the corresponding bounds ´L_i´ and ´U_i´. +That is, for each ´i = 1, ..., n´, we must have ´\sigma L_i <: T_i <: \sigma U_i´, where ´\sigma´ is the substitution ´[a_1 := T_1, ..., a_n +:= T_n]´. +The type of the application is ´\sigma S´. -If the function part ´e´ is of some value type, the type application -is taken to be equivalent to -`´e´.apply[´T_1 , ...,´ T´_n´]`, i.e. the application of an `apply` method defined by -´e´. +If the function part ´e´ is of some value type, the type application is taken to be equivalent to `´e´.apply[´T_1 , ...,´ T´_n´]`, i.e. the application of an `apply` method defined by ´e´. -Type applications can be omitted if -[local type inference](#local-type-inference) can infer best type parameters -for a polymorphic method from the types of the actual method arguments -and the expected result type. +Type applications can be omitted if [local type inference](#local-type-inference) can infer best type parameters for a polymorphic method from the types of the actual method arguments and the expected result type. ## Tuples @@ -482,11 +382,8 @@ and the expected result type. SimpleExpr ::= ‘(’ [Exprs] ‘)’ ``` -A _tuple expression_ `(´e_1´, ..., ´e_n´)` is an alias -for the class instance creation -`scala.Tuple´n´(´e_1´, ..., ´e_n´)`, where ´n \geq 2´. -The empty tuple -`()` is the unique value of type `scala.Unit`. +A _tuple expression_ `(´e_1´, ..., ´e_n´)` is an alias for the class instance creation `scala.Tuple´n´(´e_1´, ..., ´e_n´)`, where ´n \geq 2´. +The empty tuple `()` is the unique value of type `scala.Unit`. ## Instance Creation Expressions @@ -494,44 +391,33 @@ The empty tuple SimpleExpr ::= ‘new’ (ClassTemplate | TemplateBody) ``` -A _simple instance creation expression_ is of the form -`new ´c´` -where ´c´ is a [constructor invocation](05-classes-and-objects.html#constructor-invocations). Let ´T´ be -the type of ´c´. Then ´T´ must -denote a (a type instance of) a non-abstract subclass of -`scala.AnyRef`. Furthermore, the _concrete self type_ of the -expression must conform to the [self type](05-classes-and-objects.html#templates) of the class denoted by -´T´. The concrete self type is normally -´T´, except if the expression `new ´c´` appears as the -right hand side of a value definition +A _simple instance creation expression_ is of the form `new ´c´` where ´c´ is a [constructor invocation](05-classes-and-objects.html#constructor-invocations). +Let ´T´ be the type of ´c´. +Then ´T´ must denote a (a type instance of) a non-abstract subclass of `scala.AnyRef`. +Furthermore, the _concrete self type_ of the expression must conform to the [self type](05-classes-and-objects.html#templates) of the class denoted by ´T´. +The concrete self type is normally ´T´, except if the expression `new ´c´` appears as the right hand side of a value definition ```scala val ´x´: ´S´ = new ´c´ ``` (where the type annotation `: ´S´` may be missing). -In the latter case, the concrete self type of the expression is the -compound type `´T´ with ´x´.type`. +In the latter case, the concrete self type of the expression is the compound type `´T´ with ´x´.type`. -The expression is evaluated by creating a fresh -object of type ´T´ which is initialized by evaluating ´c´. The -type of the expression is ´T´. +The expression is evaluated by creating a fresh object of type ´T´ which is initialized by evaluating ´c´. +The type of the expression is ´T´. -A _general instance creation expression_ is of the form -`new ´t´` for some [class template](05-classes-and-objects.html#templates) ´t´. +A _general instance creation expression_ is of the form `new ´t´` for some [class template](05-classes-and-objects.html#templates) ´t´. Such an expression is equivalent to the block ```scala { class ´a´ extends ´t´; new ´a´ } ``` -where ´a´ is a fresh name of an _anonymous class_ which is -inaccessible to user programs. +where ´a´ is a fresh name of an _anonymous class_ which is inaccessible to user programs. -There is also a shorthand form for creating values of structural -types: If `{´D´}` is a class body, then -`new {´D´}` is equivalent to the general instance creation expression -`new AnyRef{´D´}`. +There is also a shorthand form for creating values of structural types: +If `{´D´}` is a class body, then `new {´D´}` is equivalent to the general instance creation expression `new AnyRef{´D´}`. ###### Example Consider the following structural instance creation expression: @@ -562,45 +448,27 @@ BlockExpr ::= ‘{’ CaseClauses ‘}’ Block ::= BlockStat {semi BlockStat} [ResultExpr] ``` -A _block expression_ `{´s_1´; ...; ´s_n´; ´e\,´}` is -constructed from a sequence of block statements ´s_1, ..., s_n´ -and a final expression ´e´. The statement sequence may not contain -two definitions or declarations that bind the same name in the same -namespace. The final expression can be omitted, in which -case the unit value `()` is assumed. +A _block expression_ `{´s_1´; ...; ´s_n´; ´e\,´}` is constructed from a sequence of block statements ´s_1, ..., s_n´ and a final expression ´e´. +The statement sequence may not contain two definitions or declarations that bind the same name in the same namespace. +The final expression can be omitted, in which case the unit value `()` is assumed. -The expected type of the final expression ´e´ is the expected -type of the block. The expected type of all preceding statements is -undefined. +The expected type of the final expression ´e´ is the expected type of the block. +The expected type of all preceding statements is undefined. -The type of a block `´s_1´; ...; ´s_n´; ´e´` is -`´T´ forSome {´\,Q\,´}`, where ´T´ is the type of ´e´ and ´Q´ -contains [existential clauses](03-types.html#existential-types) -for every value or type name which is free in ´T´ -and which is defined locally in one of the statements ´s_1, ..., s_n´. +The type of a block `´s_1´; ...; ´s_n´; ´e´` is `´T´ forSome {´\,Q\,´}`, where ´T´ is the type of ´e´ and ´Q´ contains [existential clauses](03-types.html#existential-types) for every value or type name which is free in ´T´ and which is defined locally in one of the statements ´s_1, ..., s_n´. We say the existential clause _binds_ the occurrence of the value or type name. Specifically, -- A locally defined type definition `type´\;t = T´` - is bound by the existential clause `type´\;t >: T <: T´`. - It is an error if ´t´ carries type parameters. -- A locally defined value definition `val´\;x: T = e´` is - bound by the existential clause `val´\;x: T´`. -- A locally defined class definition `class´\;c´ extends´\;t´` - is bound by the existential clause `type´\;c <: T´` where - ´T´ is the least class type or refinement type which is a proper - supertype of the type ´c´. It is an error if ´c´ carries type parameters. -- A locally defined object definition `object´\;x\;´extends´\;t´` - is bound by the existential clause `val´\;x: T´` where - ´T´ is the least class type or refinement type which is a proper supertype of the type - `´x´.type`. - -Evaluation of the block entails evaluation of its -statement sequence, followed by an evaluation of the final expression -´e´, which defines the result of the block. - -A block expression `{´c_1´; ...; ´c_n´}` where ´s_1, ..., s_n´ are -case clauses forms a [pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions). +- A locally defined type definition `type´\;t = T´` is bound by the existential clause `type´\;t >: T <: T´`. +It is an error if ´t´ carries type parameters. +- A locally defined value definition `val´\;x: T = e´` is bound by the existential clause `val´\;x: T´`. +- A locally defined class definition `class´\;c´ extends´\;t´` is bound by the existential clause `type´\;c <: T´` where ´T´ is the least class type or refinement type which is a proper supertype of the type ´c´. +It is an error if ´c´ carries type parameters. +- A locally defined object definition `object´\;x\;´extends´\;t´` is bound by the existential clause `val´\;x: T´` where ´T´ is the least class type or refinement type which is a proper supertype of the type `´x´.type`. + +Evaluation of the block entails evaluation of its statement sequence, followed by an evaluation of the final expression ´e´, which defines the result of the block. + +A block expression `{´c_1´; ...; ´c_n´}` where ´s_1, ..., s_n´ are case clauses forms a [pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions). ###### Example Assuming a class `Ref[T](x: T)`, the block @@ -616,9 +484,7 @@ The block { class C extends B {´\ldots´} ; new C } ``` -simply has type `B`, because with the rules [here](03-types.html#simplification-rules) -the existentially quantified type -`_1 forSome { type _1 <: B }` can be simplified to `B`. +simply has type `B`, because with the rules [here](03-types.html#simplification-rules) the existentially quantified type `_1 forSome { type _1 <: B }` can be simplified to `B`. ## Prefix, Infix, and Postfix Operations @@ -633,35 +499,26 @@ Expressions can be constructed from operands and operators. ### Prefix Operations -A prefix operation ´\mathit{op};e´ consists of a prefix operator ´\mathit{op}´, which -must be one of the identifiers ‘`+`’, ‘`-`’, ‘`!`’ or ‘`~`’, -which must not be enclosed in backquotes. -The expression ´\mathit{op};e´ is -equivalent to the postfix method application -`e.unary_´\mathit{op}´`. +A prefix operation ´\mathit{op};e´ consists of a prefix operator ´\mathit{op}´, which must be one of the identifiers ‘`+`’, ‘`-`’, ‘`!`’ or ‘`~`’, which must not be enclosed in backquotes. +The expression ´\mathit{op};e´ is equivalent to the postfix method application `e.unary_´\mathit{op}´`. -Prefix operators are different from normal method applications in -that their operand expression need not be atomic. For instance, the -input sequence `-sin(x)` is read as `-(sin(x))`, whereas the -method application `negate sin(x)` would be parsed as the -application of the infix operator `sin` to the operands -`negate` and `(x)`. +Prefix operators are different from normal method applications in that their operand expression need not be atomic. +For instance, the input sequence `-sin(x)` is read as `-(sin(x))`, whereas the method application `negate sin(x)` would be parsed as the application of the infix operator `sin` to the operands `negate` and `(x)`. ### Postfix Operations -A postfix operator can be an arbitrary identifier. The postfix -operation ´e;\mathit{op}´ is interpreted as ´e.\mathit{op}´. +A postfix operator can be an arbitrary identifier. +The postfix operation ´e;\mathit{op}´ is interpreted as ´e.\mathit{op}´. ### Infix Operations -An infix operator can be an arbitrary identifier. Infix operators have -precedence and associativity defined as follows: +An infix operator can be an arbitrary identifier. +Infix operators have precedence and associativity defined as follows: -The _precedence_ of an infix operator is determined by the operator's first -character. Characters are listed below in increasing order of -precedence, with characters on the same line having the same precedence. +The _precedence_ of an infix operator is determined by the operator's first character. +Characters are listed below in increasing order of precedence, with characters on the same line having the same precedence. ```scala (all letters, as defined in [chapter 1](01-lexical-syntax.html), including `_` and `$`) @@ -676,65 +533,42 @@ precedence, with characters on the same line having the same precedence. (other operator characters, as defined in [chapter 1](01-lexical-syntax.html), including Unicode categories `Sm` and `So`) ``` -That is, operators starting with a letter have lowest precedence, -followed by operators starting with ‘`|`’, etc. +That is, operators starting with a letter have lowest precedence, followed by operators starting with ‘`|`’, etc. -There's one exception to this rule, which concerns -[_assignment operators_](#assignment-operators). -The precedence of an assignment operator is the same as the one -of simple assignment `(=)`. That is, it is lower than the -precedence of any other operator. +There's one exception to this rule, which concerns [_assignment operators_](#assignment-operators). +The precedence of an assignment operator is the same as the one of simple assignment `(=)`. +That is, it is lower than the precedence of any other operator. The _associativity_ of an operator is determined by the operator's -last character. Operators ending in a colon ‘`:`’ are -right-associative. All other operators are left-associative. - -Precedence and associativity of operators determine the grouping of -parts of an expression as follows. - -- If there are several infix operations in an - expression, then operators with higher precedence bind more closely - than operators with lower precedence. -- If there are consecutive infix - operations ´e_0; \mathit{op}\_1; e_1; \mathit{op}\_2 ... \mathit{op}\_n; e_n´ - with operators ´\mathit{op}\_1, ..., \mathit{op}\_n´ of the same precedence, - then all these operators must - have the same associativity. If all operators are left-associative, - the sequence is interpreted as - ´(...(e_0;\mathit{op}\_1;e_1);\mathit{op}\_2...);\mathit{op}\_n;e_n´. - Otherwise, if all operators are right-associative, the - sequence is interpreted as - ´e_0;\mathit{op}\_1;(e_1;\mathit{op}\_2;(... \mathit{op}\_n;e_n)...)´. -- Postfix operators always have lower precedence than infix - operators. E.g. ´e_1;\mathit{op}\_1;e_2;\mathit{op}\_2´ is always equivalent to - ´(e_1;\mathit{op}\_1;e_2);\mathit{op}\_2´. - -The right-hand operand of a left-associative operator may consist of -several arguments enclosed in parentheses, e.g. ´e;\mathit{op};(e_1,...,e_n)´. +last character. +Operators ending in a colon ‘`:`’ are right-associative. +All other operators are left-associative. + +Precedence and associativity of operators determine the grouping of parts of an expression as follows. + +- If there are several infix operations in an expression, then operators with higher precedence bind more closely than operators with lower precedence. +- If there are consecutive infix operations ´e_0; \mathit{op}\_1; e_1; \mathit{op}\_2 ... \mathit{op}\_n; e_n´ with operators ´\mathit{op}\_1, ..., \mathit{op}\_n´ of the same precedence, then all these operators must have the same associativity. +If all operators are left-associative, the sequence is interpreted as ´(...(e_0;\mathit{op}\_1;e_1);\mathit{op}\_2...);\mathit{op}\_n;e_n´. +Otherwise, if all operators are right-associative, the sequence is interpreted as ´e_0;\mathit{op}\_1;(e_1;\mathit{op}\_2;(... \mathit{op}\_n;e_n)...)´. +- Postfix operators always have lower precedence than infix operators. E.g. ´e_1;\mathit{op}\_1;e_2;\mathit{op}\_2´ is always equivalent to ´(e_1;\mathit{op}\_1;e_2);\mathit{op}\_2´. + +The right-hand operand of a left-associative operator may consist of several arguments enclosed in parentheses, e.g. ´e;\mathit{op};(e_1,...,e_n)´. This expression is then interpreted as ´e.\mathit{op}(e_1,...,e_n)´. -A left-associative binary -operation ´e_1;\mathit{op};e_2´ is interpreted as ´e_1.\mathit{op}(e_2)´. If ´\mathit{op}´ is -right-associative and its parameter is passed by name, the same operation is interpreted as -´e_2.\mathit{op}(e_1)´. If ´\mathit{op}´ is right-associative and its parameter is passed by value, -it is interpreted as `{ val ´x´=´e_1´; ´e_2´.´\mathit{op}´(´x\,´) }`, where ´x´ is a fresh name. +A left-associative binary operation ´e_1;\mathit{op};e_2´ is interpreted as ´e_1.\mathit{op}(e_2)´. If ´\mathit{op}´ is right-associative and its parameter is passed by name, the same operation is interpreted as ´e_2.\mathit{op}(e_1)´. +If ´\mathit{op}´ is right-associative and its parameter is passed by value, it is interpreted as `{ val ´x´=´e_1´; ´e_2´.´\mathit{op}´(´x\,´) }`, where ´x´ is a fresh name. ### Assignment Operators -An _assignment operator_ is an operator symbol (syntax category -`op` in [Identifiers](01-lexical-syntax.html#identifiers)) that ends in an equals character -“`=`”, with the following exceptions: +An _assignment operator_ is an operator symbol (syntax category `op` in [Identifiers](01-lexical-syntax.html#identifiers)) that ends in an equals character “`=`”, with the following exceptions: 1. the operator also starts with an equals character, or 1. the operator is one of `(<=)`, `(>=)`, `(!=)`. -Assignment operators are treated specially in that they -can be expanded to assignments if no other interpretation is valid. +Assignment operators are treated specially in that they can be expanded to assignments if no other interpretation is valid. -Let's consider an assignment operator such as `+=` in an infix -operation `´l´ += ´r´`, where ´l´, ´r´ are expressions. -This operation can be re-interpreted as an operation which corresponds -to the assignment +Let's consider an assignment operator such as `+=` in an infix operation `´l´ += ´r´`, where ´l´, ´r´ are expressions. +This operation can be re-interpreted as an operation which corresponds to the assignment ```scala ´l´ = ´l´ + ´r´ @@ -744,14 +578,9 @@ except that the operation's left-hand-side ´l´ is evaluated only once. The re-interpretation occurs if the following two conditions are fulfilled. -1. The left-hand-side ´l´ does not have a member named - `+=`, and also cannot be converted by an - [implicit conversion](#implicit-conversions) - to a value with a member named `+=`. +1. The left-hand-side ´l´ does not have a member named `+=`, and also cannot be converted by an [implicit conversion](#implicit-conversions) to a value with a member named `+=`. 1. The assignment `´l´ = ´l´ + ´r´` is type-correct. - In particular this implies that ´l´ refers to a variable or object - that can be assigned to, and that is convertible to a value with a member - named `+`. +In particular this implies that ´l´ refers to a variable or object that can be assigned to, and that is convertible to a value with a member named `+`. ## Typed Expressions @@ -759,9 +588,9 @@ The re-interpretation occurs if the following two conditions are fulfilled. Expr1 ::= PostfixExpr ‘:’ CompoundType ``` -The _typed expression_ ´e: T´ has type ´T´. The type of -expression ´e´ is expected to conform to ´T´. The result of -the expression is the value of ´e´ converted to type ´T´. +The _typed expression_ ´e: T´ has type ´T´. +The type of expression ´e´ is expected to conform to ´T´. +The result of the expression is the value of ´e´ converted to type ´T´. ###### Example Here are examples of well-typed and ill-typed expressions. @@ -778,9 +607,7 @@ Here are examples of well-typed and ill-typed expressions. Expr1 ::= PostfixExpr ‘:’ Annotation {Annotation} ``` -An _annotated expression_ `´e´: @´a_1´ ... @´a_n´` -attaches [annotations](11-annotations.html#user-defined-annotations) ´a_1, ..., a_n´ to the -expression ´e´. +An _annotated expression_ `´e´: @´a_1´ ... @´a_n´` attaches [annotations](11-annotations.html#user-defined-annotations) ´a_1, ..., a_n´ to the expression ´e´. ## Assignments @@ -790,25 +617,14 @@ Expr1 ::= [SimpleExpr ‘.’] id ‘=’ Expr | SimpleExpr1 ArgumentExprs ‘=’ Expr ``` -The interpretation of an assignment to a simple variable `´x´ = ´e´` -depends on the definition of ´x´. If ´x´ denotes a mutable -variable, then the assignment changes the current value of ´x´ to be -the result of evaluating the expression ´e´. The type of ´e´ is -expected to conform to the type of ´x´. If ´x´ is a parameterless -method defined in some template, and the same template contains a -setter method `´x´_=` as member, then the assignment -`´x´ = ´e´` is interpreted as the invocation -`´x´_=(´e\,´)` of that setter method. Analogously, an -assignment `´f.x´ = ´e´` to a parameterless method ´x´ -is interpreted as the invocation `´f.x´_=(´e\,´)`. -If ´x´ is an application of a unary operator, then the expression -is interpreted as though it were written as the explicit application -`´x´.unary_´\mathit{op}´`, namely, as `´x´.unary_´\mathit{op}´_=(´e\,´)`. - -An assignment `´f´(´\mathit{args}\,´) = ´e´` with a method application to the -left of the ‘`=`’ operator is interpreted as -`´f.´update(´\mathit{args}´, ´e\,´)`, i.e. -the invocation of an `update` method defined by ´f´. +The interpretation of an assignment to a simple variable `´x´ = ´e´` depends on the definition of ´x´. +If ´x´ denotes a mutable variable, then the assignment changes the current value of ´x´ to be the result of evaluating the expression ´e´. +The type of ´e´ is expected to conform to the type of ´x´. +If ´x´ is a parameterless method defined in some template, and the same template contains a setter method `´x´_=` as member, then the assignment `´x´ = ´e´` is interpreted as the invocation `´x´_=(´e\,´)` of that setter method. +Analogously, an assignment `´f.x´ = ´e´` to a parameterless method ´x´ is interpreted as the invocation `´f.x´_=(´e\,´)`. +If ´x´ is an application of a unary operator, then the expression is interpreted as though it were written as the explicit application `´x´.unary_´\mathit{op}´`, namely, as `´x´.unary_´\mathit{op}´_=(´e\,´)`. + +An assignment `´f´(´\mathit{args}\,´) = ´e´` with a method application to the left of the ‘`=`’ operator is interpreted as `´f.´update(´\mathit{args}´, ´e\,´)`, i.e. the invocation of an `update` method defined by ´f´. ###### Example Here are some assignment expressions and their equivalent expansions. @@ -846,8 +662,7 @@ def matmul(xss: Array[Array[Double]], yss: Array[Array[Double]]) = { } ``` -Desugaring the array accesses and assignments yields the following -expanded version: +Desugaring the array accesses and assignments yields the following expanded version: ```scala def matmul(xss: Array[Array[Double]], yss: Array[Array[Double]]) = { @@ -877,25 +692,17 @@ def matmul(xss: Array[Array[Double]], yss: Array[Array[Double]]) = { Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] ``` -The _conditional expression_ `if (´e_1´) ´e_2´ else ´e_3´` chooses -one of the values of ´e_2´ and ´e_3´, depending on the -value of ´e_1´. The condition ´e_1´ is expected to -conform to type `Boolean`. The then-part ´e_2´ and the -else-part ´e_3´ are both expected to conform to the expected -type of the conditional expression. The type of the conditional -expression is the [weak least upper bound](03-types.html#weak-conformance) -of the types of ´e_2´ and -´e_3´. A semicolon preceding the `else` symbol of a -conditional expression is ignored. +The _conditional expression_ `if (´e_1´) ´e_2´ else ´e_3´` chooses one of the values of ´e_2´ and ´e_3´, depending on the value of ´e_1´. +The condition ´e_1´ is expected to conform to type `Boolean`. +The then-part ´e_2´ and the else-part ´e_3´ are both expected to conform to the expected type of the conditional expression. +The type of the conditional expression is the [weak least upper bound](03-types.html#weak-conformance) of the types of ´e_2´ and ´e_3´. +A semicolon preceding the `else` symbol of a conditional expression is ignored. -The conditional expression is evaluated by evaluating first -´e_1´. If this evaluates to `true`, the result of -evaluating ´e_2´ is returned, otherwise the result of -evaluating ´e_3´ is returned. +The conditional expression is evaluated by evaluating first ´e_1´. +If this evaluates to `true`, the result of evaluating ´e_2´ is returned, otherwise the result of evaluating ´e_3´ is returned. -A short form of the conditional expression eliminates the -else-part. The conditional expression `if (´e_1´) ´e_2´` is -evaluated as if it was `if (´e_1´) ´e_2´ else ()`. +A short form of the conditional expression eliminates the else-part. +The conditional expression `if (´e_1´) ´e_2´` is evaluated as if it was `if (´e_1´) ´e_2´ else ()`. ## While Loop Expressions @@ -903,9 +710,7 @@ evaluated as if it was `if (´e_1´) ´e_2´ else ()`. Expr1 ::= ‘while’ ‘(’ Expr ‘)’ {nl} Expr ``` -The _while loop expression_ `while (´e_1´) ´e_2´` is typed and -evaluated as if it was an application of `whileLoop (´e_1´) (´e_2´)` where -the hypothetical method `whileLoop` is defined as follows. +The _while loop expression_ `while (´e_1´) ´e_2´` is typed and evaluated as if it was an application of `whileLoop (´e_1´) (´e_2´)` where the hypothetical method `whileLoop` is defined as follows. ```scala def whileLoop(cond: => Boolean)(body: => Unit): Unit = @@ -918,8 +723,7 @@ def whileLoop(cond: => Boolean)(body: => Unit): Unit = Expr1 ::= ‘do’ Expr [semi] ‘while’ ‘(’ Expr ‘)’ ``` -The _do loop expression_ `do ´e_1´ while (´e_2´)` is typed and -evaluated as if it was the expression `(´e_1´ ; while (´e_2´) ´e_1´)`. +The _do loop expression_ `do ´e_1´ while (´e_2´)` is typed and evaluated as if it was the expression `(´e_1´ ; while (´e_2´) ´e_1´)`. A semicolon preceding the `while` symbol of a do loop expression is ignored. ## For Comprehensions and For Loops @@ -932,57 +736,36 @@ Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pat Guard ::= ‘if’ PostfixExpr ``` -A _for loop_ `for (´\mathit{enums}\,´) ´e´` executes expression ´e´ -for each binding generated by the enumerators ´\mathit{enums}´. -A _for comprehension_ `for (´\mathit{enums}\,´) yield ´e´` evaluates -expression ´e´ for each binding generated by the enumerators ´\mathit{enums}´ -and collects the results. An enumerator sequence always starts with a -generator; this can be followed by further generators, value -definitions, or guards. - -A _generator_ `´p´ <- ´e´` produces bindings from an expression ´e´ which is -matched in some way against pattern ´p´. Optionally, `case` can appear in front -of a generator pattern, this has no meaning in Scala 2 but will be [required in -Scala 3 if `p` is not -irrefutable](https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html). - -A _value definition_ `´p´ = ´e´` -binds the value name ´p´ (or several names in a pattern ´p´) to -the result of evaluating the expression ´e´. A _guard_ -`if ´e´` contains a boolean expression which restricts -enumerated bindings. The precise meaning of generators and guards is -defined by translation to invocations of four methods: `map`, -`withFilter`, `flatMap`, and `foreach`. These methods can -be implemented in different ways for different carrier types. - -The translation scheme is as follows. In a first step, every -generator `´p´ <- ´e´`, where ´p´ is not [irrefutable](08-pattern-matching.html#patterns) -for the type of ´e´ is replaced by +A _for loop_ `for (´\mathit{enums}\,´) ´e´` executes expression ´e´ for each binding generated by the enumerators ´\mathit{enums}´. +A _for comprehension_ `for (´\mathit{enums}\,´) yield ´e´` evaluates expression ´e´ for each binding generated by the enumerators ´\mathit{enums}´ and collects the results. +An enumerator sequence always starts with a generator; this can be followed by further generators, value definitions, or guards. + +A _generator_ `´p´ <- ´e´` produces bindings from an expression ´e´ which is matched in some way against pattern ´p´. +Optionally, `case` can appear in front of a generator pattern, this has no meaning in Scala 2 but will be [required in Scala 3 if `p` is not irrefutable](https://docs.scala-lang.org/scala3/reference/changed-features/pattern-bindings.html). + +A _value definition_ `´p´ = ´e´` binds the value name ´p´ (or several names in a pattern ´p´) to the result of evaluating the expression ´e´. +A _guard_ `if ´e´` contains a boolean expression which restricts enumerated bindings. +The precise meaning of generators and guards is defined by translation to invocations of four methods: `map`, `withFilter`, `flatMap`, and `foreach`. +These methods can be implemented in different ways for different carrier types. + +The translation scheme is as follows. +In a first step, every generator `´p´ <- ´e´`, where ´p´ is not [irrefutable](08-pattern-matching.html#patterns) for the type of ´e´ is replaced by ```scala ´p´ <- ´e´.withFilter { case ´p´ => true; case _ => false } ``` -Then, the following rules are applied repeatedly until all -comprehensions have been eliminated. +Then, the following rules are applied repeatedly until all comprehensions have been eliminated. - - A for comprehension - `for (´p´ <- ´e\,´) yield ´e'´` - is translated to - `´e´.map { case ´p´ => ´e'´ }`. - - A for loop - `for (´p´ <- ´e\,´) ´e'´` - is translated to - `´e´.foreach { case ´p´ => ´e'´ }`. + - A for comprehension `for (´p´ <- ´e\,´) yield ´e'´` is translated to `´e´.map { case ´p´ => ´e'´ }`. + - A for loop `for (´p´ <- ´e\,´) ´e'´` is translated to `´e´.foreach { case ´p´ => ´e'´ }`. - A for comprehension ```scala for (´p´ <- ´e´; ´p'´ <- ´e'; ...´) yield ´e''´ ``` - where `...` is a (possibly empty) - sequence of generators, definitions, or guards, - is translated to + where `...` is a (possibly empty) sequence of generators, definitions, or guards, is translated to ```scala ´e´.flatMap { case ´p´ => for (´p'´ <- ´e'; ...´) yield ´e''´ } @@ -994,30 +777,22 @@ comprehensions have been eliminated. for (´p´ <- ´e´; ´p'´ <- ´e'; ...´) ´e''´ ``` - where `...` is a (possibly empty) - sequence of generators, definitions, or guards, - is translated to + where `...` is a (possibly empty) sequence of generators, definitions, or guards, is translated to ```scala ´e´.foreach { case ´p´ => for (´p'´ <- ´e'; ...´) ´e''´ } ``` - - A generator `´p´ <- ´e´` followed by a guard - `if ´g´` is translated to a single generator - `´p´ <- ´e´.withFilter((´x_1, ..., x_n´) => ´g\,´)` where - ´x_1, ..., x_n´ are the free variables of ´p´. + - A generator `´p´ <- ´e´` followed by a guard `if ´g´` is translated to a single generator `´p´ <- ´e´.withFilter((´x_1, ..., x_n´) => ´g\,´)` where ´x_1, ..., x_n´ are the free variables of ´p´. - - A generator `´p´ <- ´e´` followed by a value definition - `´p'´ = ´e'´` is translated to the following generator of pairs of values, where - ´x´ and ´x'´ are fresh names: + - A generator `´p´ <- ´e´` followed by a value definition `´p'´ = ´e'´` is translated to the following generator of pairs of values, where ´x´ and ´x'´ are fresh names: ```scala (´p´, ´p'´) <- for (´x @ p´ <- ´e´) yield { val ´x' @ p'´ = ´e'´; (´x´, ´x'´) } ``` ###### Example -The following code produces all pairs of numbers between ´1´ and ´n-1´ -whose sums are prime. +The following code produces all pairs of numbers between ´1´ and ´n-1´ whose sums are prime. ```scala for { i <- 1 until n @@ -1037,8 +812,7 @@ The for comprehension is translated to: ``` ###### Example -For comprehensions can be used to express vector -and matrix algorithms concisely. +For comprehensions can be used to express vector and matrix algorithms concisely. For instance, here is a method to compute the transpose of a given matrix: @@ -1072,9 +846,7 @@ def matmul(xss: Array[Array[Double]], yss: Array[Array[Double]]) = { } ``` -The code above makes use of the fact that `map`, `flatMap`, -`withFilter`, and `foreach` are defined for instances of class -`scala.Array`. +The code above makes use of the fact that `map`, `flatMap`, `withFilter`, and `foreach` are defined for instances of class `scala.Array`. ## Return Expressions @@ -1082,32 +854,22 @@ The code above makes use of the fact that `map`, `flatMap`, Expr1 ::= ‘return’ [Expr] ``` -A _return expression_ `return ´e´` must occur inside the body of some -enclosing user defined method. The innermost enclosing method in a -source program, ´m´, must have an explicitly declared result type, and -the type of ´e´ must conform to it. +A _return expression_ `return ´e´` must occur inside the body of some enclosing user defined method. +The innermost enclosing method in a source program, ´m´, must have an explicitly declared result type, and the type of ´e´ must conform to it. -The return expression evaluates the expression ´e´ and returns its -value as the result of ´m´. The evaluation of any statements or -expressions following the return expression is omitted. The type of -a return expression is `scala.Nothing`. +The return expression evaluates the expression ´e´ and returns its value as the result of ´m´. +The evaluation of any statements or expressions following the return expression is omitted. +The type of a return expression is `scala.Nothing`. -The expression ´e´ may be omitted. The return expression -`return` is type-checked and evaluated as if it were `return ()`. +The expression ´e´ may be omitted. +The return expression `return` is type-checked and evaluated as if it were `return ()`. -Returning from the method from within a nested function may be -implemented by throwing and catching a -`scala.runtime.NonLocalReturnControl`. Any exception catches -between the point of return and the enclosing methods might see -and catch that exception. A key comparison makes sure that this -exception is only caught by the method instance which is terminated -by the return. +Returning from the method from within a nested function may be implemented by throwing and catching a `scala.runtime.NonLocalReturnControl`. +Any exception catches between the point of return and the enclosing methods might see and catch that exception. +A key comparison makes sure that this exception is only caught by the method instance which is terminated by the return. -If the return expression is itself part of an anonymous function, it -is possible that the enclosing method ´m´ has already returned -before the return expression is executed. In that case, the thrown -`scala.runtime.NonLocalReturnControl` will not be caught, and will -propagate up the call stack. +If the return expression is itself part of an anonymous function, it is possible that the enclosing method ´m´ has already returned before the return expression is executed. +In that case, the thrown `scala.runtime.NonLocalReturnControl` will not be caught, and will propagate up the call stack. ## Throw Expressions @@ -1115,16 +877,12 @@ propagate up the call stack. Expr1 ::= ‘throw’ Expr ``` -A _throw expression_ `throw ´e´` evaluates the expression -´e´. The type of this expression must conform to -`Throwable`. If ´e´ evaluates to an exception -reference, evaluation is aborted with the thrown exception. If ´e´ -evaluates to `null`, evaluation is instead aborted with a -`NullPointerException`. If there is an active -[`try` expression](#try-expressions) which handles the thrown -exception, evaluation resumes with the handler; otherwise the thread -executing the `throw` is aborted. The type of a throw expression -is `scala.Nothing`. +A _throw expression_ `throw ´e´` evaluates the expression ´e´. +The type of this expression must conform to `Throwable`. +If ´e´ evaluates to an exception reference, evaluation is aborted with the thrown exception. +If ´e´ evaluates to `null`, evaluation is instead aborted with a `NullPointerException`. +If there is an active [`try` expression](#try-expressions) which handles the thrown exception, evaluation resumes with the handler; otherwise the thread executing the `throw` is aborted. +The type of a throw expression is `scala.Nothing`. ## Try Expressions @@ -1132,51 +890,36 @@ is `scala.Nothing`. Expr1 ::= ‘try’ Expr [‘catch’ Expr] [‘finally’ Expr] ``` -A _try expression_ is of the form `try { ´b´ } catch ´h´` -where the handler ´h´ is usually a -[pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions) +A _try expression_ is of the form `try { ´b´ } catch ´h´` where the handler ´h´ is usually a [pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions) ```scala { case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ } ``` -This expression is evaluated by evaluating the block -´b´. If evaluation of ´b´ does not cause an exception to be -thrown, the result of ´b´ is returned. Otherwise the -handler ´h´ is applied to the thrown exception. -If the handler contains a case matching the thrown exception, -the first such case is invoked. If the handler contains -no case matching the thrown exception, the exception is -re-thrown. More generally, if the handler is a `PartialFunction`, -it is applied only if it is defined at the given exception. - -Let ´\mathit{pt}´ be the expected type of the try expression. The block -´b´ is expected to conform to ´\mathit{pt}´. The handler ´h´ -is expected conform to type `scala.Function[scala.Throwable, ´\mathit{pt}\,´]`. -The type of the try expression is the [weak least upper bound](03-types.html#weak-conformance) -of the type of ´b´ and the result type of ´h´. - -A try expression `try { ´b´ } finally ´e´` evaluates the block -´b´. If evaluation of ´b´ does not cause an exception to be -thrown, the expression ´e´ is evaluated. If an exception is thrown -during evaluation of ´e´, the evaluation of the try expression is -aborted with the thrown exception. If no exception is thrown during -evaluation of ´e´, the result of ´b´ is returned as the -result of the try expression. - -If an exception is thrown during evaluation of ´b´, the finally block -´e´ is also evaluated. If another exception ´e´ is thrown -during evaluation of ´e´, evaluation of the try expression is -aborted with the thrown exception. If no exception is thrown during -evaluation of ´e´, the original exception thrown in ´b´ is -re-thrown once evaluation of ´e´ has completed. The block -´b´ is expected to conform to the expected type of the try -expression. The finally expression ´e´ is expected to conform to -type `Unit`. - -A try expression `try { ´b´ } catch ´e_1´ finally ´e_2´` -is a shorthand -for `try { try { ´b´ } catch ´e_1´ } finally ´e_2´`. +This expression is evaluated by evaluating the block ´b´. +If evaluation of ´b´ does not cause an exception to be thrown, the result of ´b´ is returned. +Otherwise the handler ´h´ is applied to the thrown exception. +If the handler contains a case matching the thrown exception, the first such case is invoked. +If the handler contains no case matching the thrown exception, the exception is re-thrown. +More generally, if the handler is a `PartialFunction`, it is applied only if it is defined at the given exception. + +Let ´\mathit{pt}´ be the expected type of the try expression. +The block ´b´ is expected to conform to ´\mathit{pt}´. +The handler ´h´ is expected conform to type `scala.Function[scala.Throwable, ´\mathit{pt}\,´]`. +The type of the try expression is the [weak least upper bound](03-types.html#weak-conformance) of the type of ´b´ and the result type of ´h´. + +A try expression `try { ´b´ } finally ´e´` evaluates the block ´b´. +If evaluation of ´b´ does not cause an exception to be thrown, the expression ´e´ is evaluated. +If an exception is thrown during evaluation of ´e´, the evaluation of the try expression is aborted with the thrown exception. +If no exception is thrown during evaluation of ´e´, the result of ´b´ is returned as the result of the try expression. + +If an exception is thrown during evaluation of ´b´, the finally block ´e´ is also evaluated. +If another exception ´e´ is thrown during evaluation of ´e´, evaluation of the try expression is aborted with the thrown exception. +If no exception is thrown during evaluation of ´e´, the original exception thrown in ´b´ is re-thrown once evaluation of ´e´ has completed. +The block ´b´ is expected to conform to the expected type of the try expression. +The finally expression ´e´ is expected to conform to type `Unit`. + +A try expression `try { ´b´ } catch ´e_1´ finally ´e_2´` is a shorthand for `try { try { ´b´ } catch ´e_1´ } finally ´e_2´`. ## Anonymous Functions @@ -1187,18 +930,27 @@ Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’ Binding ::= (id | ‘_’) [‘:’ Type] ``` -The anonymous function of arity ´n´, `(´x_1´: ´T_1, ..., x_n´: ´T_n´) => e` maps parameters ´x_i´ of types ´T_i´ to a result given by expression ´e´. The scope of each formal parameter ´x_i´ is ´e´. Formal parameters must have pairwise distinct names. +The anonymous function of arity ´n´, `(´x_1´: ´T_1, ..., x_n´: ´T_n´) => e` maps parameters ´x_i´ of types ´T_i´ to a result given by expression ´e´. +The scope of each formal parameter ´x_i´ is ´e´. +Formal parameters must have pairwise distinct names. -In the case of a single untyped formal parameter, `(´x\,´) => ´e´` can be abbreviated to `´x´ => ´e´`. If an anonymous function `(´x´: ´T\,´) => ´e´` with a single typed parameter appears as the result expression of a block, it can be abbreviated to `´x´: ´T´ => e`. +In the case of a single untyped formal parameter, `(´x\,´) => ´e´` can be abbreviated to `´x´ => ´e´`. +If an anonymous function `(´x´: ´T\,´) => ´e´` with a single typed parameter appears as the result expression of a block, it can be abbreviated to `´x´: ´T´ => e`. -A formal parameter may also be a wildcard represented by an underscore `_`. In that case, a fresh name for the parameter is chosen arbitrarily. +A formal parameter may also be a wildcard represented by an underscore `_`. +In that case, a fresh name for the parameter is chosen arbitrarily. -A named parameter of an anonymous function may be optionally preceded by an `implicit` modifier. In that case the parameter is labeled [`implicit`](07-implicits.html#implicit-parameters-and-views); however the parameter section itself does not count as an [implicit parameter section](07-implicits.html#implicit-parameters). Hence, arguments to anonymous functions always have to be given explicitly. +A named parameter of an anonymous function may be optionally preceded by an `implicit` modifier. +In that case the parameter is labeled [`implicit`](07-implicits.html#implicit-parameters-and-views); however the parameter section itself does not count as an [implicit parameter section](07-implicits.html#implicit-parameters). +Hence, arguments to anonymous functions always have to be given explicitly. ### Translation -If the expected type of the anonymous function is of the shape `scala.Function´n´[´S_1´, ..., ´S_n´, ´R\,´]`, or can be [SAM-converted](#sam-conversion) to such a function type, the type `´T_i´` of a parameter `´x_i´` can be omitted, as far as `´S_i´` is defined in the expected type, and `´T_i´ = ´S_i´` is assumed. Furthermore, the expected type when type checking ´e´ is ´R´. +If the expected type of the anonymous function is of the shape `scala.Function´n´[´S_1´, ..., ´S_n´, ´R\,´]`, or can be [SAM-converted](#sam-conversion) to such a function type, the type `´T_i´` of a parameter `´x_i´` can be omitted, as far as `´S_i´` is defined in the expected type, and `´T_i´ = ´S_i´` is assumed. +Furthermore, the expected type when type checking ´e´ is ´R´. -If there is no expected type for the function literal, all formal parameter types `´T_i´` must be specified explicitly, and the expected type of ´e´ is undefined. The type of the anonymous function is `scala.Function´n´[´T_1´, ..., ´T_n´, ´R\,´]`, where ´R´ is the [packed type](#expression-typing) of ´e´. ´R´ must be equivalent to a type which does not refer to any of the formal parameters ´x_i´. +If there is no expected type for the function literal, all formal parameter types `´T_i´` must be specified explicitly, and the expected type of ´e´ is undefined. +The type of the anonymous function is `scala.Function´n´[´T_1´, ..., ´T_n´, ´R\,´]`, where ´R´ is the [packed type](#expression-typing) of ´e´. +´R´ must be equivalent to a type which does not refer to any of the formal parameters ´x_i´. The eventual run-time value of an anonymous function is determined by the expected type: - a subclass of one of the builtin function types, `scala.Function´n´[´S_1, ..., S_n´, ´R\,´]` (with ´S_i´ and ´R´ fully defined), @@ -1218,15 +970,9 @@ The same evaluation holds for a SAM type, except that the instantiated type is g The underlying platform may provide more efficient ways of constructing these instances, such as Java 8's `invokedynamic` bytecode and `LambdaMetaFactory` class. -When a `PartialFunction` is required, an additional member `isDefinedAt` -is synthesized, which simply returns `true`. -However, if the function literal has the shape `x => x match { $...$ }`, -then `isDefinedAt` is derived from the pattern match in the following way: -each case from the match expression evaluates to `true`, -and if there is no default case, -a default case is added that evaluates to `false`. -For more details on how that is implemented see -["Pattern Matching Anonymous Functions"](08-pattern-matching.html#pattern-matching-anonymous-functions). +When a `PartialFunction` is required, an additional member `isDefinedAt` is synthesized, which simply returns `true`. +However, if the function literal has the shape `x => x match { $...$ }`, then `isDefinedAt` is derived from the pattern match in the following way: each case from the match expression evaluates to `true`, and if there is no default case, a default case is added that evaluates to `false`. +For more details on how that is implemented see ["Pattern Matching Anonymous Functions"](08-pattern-matching.html#pattern-matching-anonymous-functions). ###### Example Examples of anonymous functions: @@ -1253,29 +999,18 @@ _ => 5 // The function that ignores its argument SimpleExpr1 ::= ‘_’ ``` -An expression (of syntactic category `Expr`) -may contain embedded underscore symbols `_` at places where identifiers -are legal. Such an expression represents an anonymous function where subsequent -occurrences of underscores denote successive parameters. +An expression (of syntactic category `Expr`) may contain embedded underscore symbols `_` at places where identifiers are legal. +Such an expression represents an anonymous function where subsequent occurrences of underscores denote successive parameters. -Define an _underscore section_ to be an expression of the form -`_:´T´` where ´T´ is a type, or else of the form `_`, -provided the underscore does not appear as the expression part of a -type ascription `_:´T´`. +Define an _underscore section_ to be an expression of the form `_:´T´` where ´T´ is a type, or else of the form `_`, provided the underscore does not appear as the expression part of a type ascription `_:´T´`. -An expression ´e´ of syntactic category `Expr` _binds_ an underscore section -´u´, if the following two conditions hold: (1) ´e´ properly contains ´u´, and -(2) there is no other expression of syntactic category `Expr` -which is properly contained in ´e´ and which itself properly contains ´u´. +An expression ´e´ of syntactic category `Expr` _binds_ an underscore section ´u´, if the following two conditions hold: (1) ´e´ properly contains ´u´, and (2) there is no other expression of syntactic category `Expr` which is properly contained in ´e´ and which itself properly contains ´u´. -If an expression ´e´ binds underscore sections ´u_1, ..., u_n´, in this order, it is equivalent to -the anonymous function `(´u'_1´, ... ´u'_n´) => ´e'´` -where each ´u_i'´ results from ´u_i´ by replacing the underscore with a fresh identifier and -´e'´ results from ´e´ by replacing each underscore section ´u_i´ by ´u_i'´. +If an expression ´e´ binds underscore sections ´u_1, ..., u_n´, in this order, it is equivalent to the anonymous function `(´u'_1´, ... ´u'_n´) => ´e'´` where each ´u_i'´ results from ´u_i´ by replacing the underscore with a fresh identifier and ´e'´ results from ´e´ by replacing each underscore section ´u_i´ by ´u_i'´. ###### Example -The anonymous functions in the left column use placeholder -syntax. Each of these is equivalent to the anonymous function on its right. +The anonymous functions in the left column use placeholder syntax. +Each of these is equivalent to the anonymous function on its right. | | | |---------------------------|----------------------------| @@ -1289,15 +1024,13 @@ syntax. Each of these is equivalent to the anonymous function on its right. ## Constant Expressions Constant expressions are expressions that the Scala compiler can evaluate to a constant. -The definition of "constant expression" depends on the platform, but they -include at least the expressions of the following forms: +The definition of "constant expression" depends on the platform, but they include at least the expressions of the following forms: - A literal of a value class, such as an integer - A string literal - A class constructed with [`Predef.classOf`](12-the-scala-standard-library.html#the-predef-object) - An element of an enumeration from the underlying platform -- A literal array, of the form `Array´(c_1, ..., c_n)´`, - where all of the ´c_i´'s are themselves constant expressions +- A literal array, of the form `Array´(c_1, ..., c_n)´`, where all of the ´c_i´'s are themselves constant expressions - An identifier defined by a [constant value definition](04-basic-declarations-and-definitions.html#value-declarations-and-definitions). ## Statements @@ -1315,40 +1048,31 @@ TemplateStat ::= Import | ``` -Statements occur as parts of blocks and templates. A _statement_ can be -an import, a definition or an expression, or it can be empty. -Statements used in the template of a class definition can also be -declarations. An expression that is used as a statement can have an -arbitrary value type. An expression statement ´e´ is evaluated by -evaluating ´e´ and discarding the result of the evaluation. +Statements occur as parts of blocks and templates. +A _statement_ can be an import, a definition or an expression, or it can be empty. +Statements used in the template of a class definition can also be declarations. +An expression that is used as a statement can have an arbitrary value type. +An expression statement ´e´ is evaluated by evaluating ´e´ and discarding the result of the evaluation. -Block statements may be definitions which bind local names in the -block. The only modifier allowed in all block-local definitions is -`implicit`. When prefixing a class or object definition, -modifiers `abstract`, `final`, and `sealed` are also -permitted. +Block statements may be definitions which bind local names in the block. +The only modifier allowed in all block-local definitions is `implicit`. +When prefixing a class or object definition, modifiers `abstract`, `final`, and `sealed` are also permitted. -Evaluation of a statement sequence entails evaluation of the -statements in the order they are written. +Evaluation of a statement sequence entails evaluation of the statements in the order they are written. ## Implicit Conversions -Implicit conversions can be applied to expressions whose type does not -match their expected type, to qualifiers in selections, and to unapplied methods. The -available implicit conversions are given in the next two sub-sections. +Implicit conversions can be applied to expressions whose type does not match their expected type, to qualifiers in selections, and to unapplied methods. +The available implicit conversions are given in the next two sub-sections. ### Value Conversions -The following seven implicit conversions can be applied to an -expression ´e´ which has some value type ´T´ and which is type-checked with -some expected type ´\mathit{pt}´. +The following seven implicit conversions can be applied to an expression ´e´ which has some value type ´T´ and which is type-checked with some expected type ´\mathit{pt}´. ###### Static Overloading Resolution -If an expression denotes several possible members of a class, -[overloading resolution](#overloading-resolution) -is applied to pick a unique member. +If an expression denotes several possible members of a class, [overloading resolution](#overloading-resolution) is applied to pick a unique member. ###### Type Instantiation An expression ´e´ of polymorphic type @@ -1357,47 +1081,31 @@ An expression ´e´ of polymorphic type [´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´ ``` -which does not appear as the function part of -a type application is converted to a type instance of ´T´ -by determining with [local type inference](#local-type-inference) -instance types `´T_1, ..., T_n´` -for the type variables `´a_1, ..., a_n´` and -implicitly embedding ´e´ in the [type application](#type-applications) -`´e´[´T_1, ..., T_n´]`. +which does not appear as the function part of a type application is converted to a type instance of ´T´ by determining with [local type inference](#local-type-inference) instance types `´T_1, ..., T_n´` for the type variables `´a_1, ..., a_n´` and implicitly embedding ´e´ in the [type application](#type-applications) `´e´[´T_1, ..., T_n´]`. ###### Numeric Widening -If ´e´ has a primitive number type which [weakly conforms](03-types.html#weak-conformance) -to the expected type, it is widened to -the expected type using one of the numeric conversion methods -`toShort`, `toChar`, `toInt`, `toLong`, -`toFloat`, `toDouble` defined [in the standard library](12-the-scala-standard-library.html#numeric-value-types). +If ´e´ has a primitive number type which [weakly conforms](03-types.html#weak-conformance) to the expected type, it is widened to the expected type using one of the numeric conversion methods `toShort`, `toChar`, `toInt`, `toLong`, `toFloat`, `toDouble` defined [in the standard library](12-the-scala-standard-library.html#numeric-value-types). -Since conversions from `Int` to `Float` and from `Long` to `Float` or `Double` -may incur a loss of precision, those implicit conversions are deprecated. -The conversion is permitted for literals if the original value can be recovered, -that is, if conversion back to the original type produces the original value. +Since conversions from `Int` to `Float` and from `Long` to `Float` or `Double` may incur a loss of precision, those implicit conversions are deprecated. +The conversion is permitted for literals if the original value can be recovered, that is, if conversion back to the original type produces the original value. ###### Numeric Literal Narrowing -If the expected type is `Byte`, `Short` or `Char`, and -the expression ´e´ is an integer literal fitting in the range of that -type, it is converted to the same literal in that type. +If the expected type is `Byte`, `Short` or `Char`, and the expression ´e´ is an integer literal fitting in the range of that type, it is converted to the same literal in that type. ###### Value Discarding -If ´e´ has some value type and the expected type is `Unit`, -´e´ is converted to the expected type by embedding it in the -term `{ ´e´; () }`. +If ´e´ has some value type and the expected type is `Unit`, ´e´ is converted to the expected type by embedding it in the term `{ ´e´; () }`. ###### SAM conversion An expression `(p1, ..., pN) => body` of function type `(T1, ..., TN) => T` is sam-convertible to the expected type `S` if the following holds: - the class `C` of `S` declares an abstract method `m` with signature `(p1: A1, ..., pN: AN): R`; - besides `m`, `C` must not declare or inherit any other deferred value members; - the method `m` must have a single argument list; - - there must be a type `U` that is a subtype of `S`, so that the expression - `new U { final def m(p1: A1, ..., pN: AN): R = body }` is well-typed (conforming to the expected type `S`); + - there must be a type `U` that is a subtype of `S`, so that the expression `new U { final def m(p1: A1, ..., pN: AN): R = body }` is well-typed (conforming to the expected type `S`); - for the purpose of scoping, `m` should be considered a static member (`U`'s members are not in scope in `body`); - `(A1, ..., AN) => R` is a subtype of `(T1, ..., TN) => T` (satisfying this condition drives type inference of unknown type parameters in `S`); -Note that a function literal that targets a SAM is not necessarily compiled to the above instance creation expression. This is platform-dependent. +Note that a function literal that targets a SAM is not necessarily compiled to the above instance creation expression. +This is platform-dependent. It follows that: - if class `C` defines a constructor, it must be accessible and must define exactly one, empty, argument list; @@ -1412,106 +1120,71 @@ Finally, we impose some implementation restrictions (these may be lifted in futu - `C` must not be `@specialized`. ###### View Application -If none of the previous conversions applies, and ´e´'s type -does not conform to the expected type ´\mathit{pt}´, it is attempted to convert -´e´ to the expected type with a [view](07-implicits.html#views). +If none of the previous conversions applies, and ´e´'s type does not conform to the expected type ´\mathit{pt}´, it is attempted to convert ´e´ to the expected type with a [view](07-implicits.html#views). ###### Selection on `Dynamic` -If none of the previous conversions applies, and ´e´ is a prefix -of a selection ´e.x´, and ´e´'s type conforms to class `scala.Dynamic`, -then the selection is rewritten according to the rules for -[dynamic member selection](#dynamic-member-selection). +If none of the previous conversions applies, and ´e´ is a prefix of a selection ´e.x´, and ´e´'s type conforms to class `scala.Dynamic`, then the selection is rewritten according to the rules for [dynamic member selection](#dynamic-member-selection). ### Method Conversions -The following four implicit conversions can be applied to methods -which are not applied to some argument list. +The following four implicit conversions can be applied to methods which are not applied to some argument list. ###### Evaluation -A parameterless method ´m´ of type `=> ´T´` is always converted to -type ´T´ by evaluating the expression to which ´m´ is bound. +A parameterless method ´m´ of type `=> ´T´` is always converted to type ´T´ by evaluating the expression to which ´m´ is bound. ###### Implicit Application -If the method takes only implicit parameters, implicit -arguments are passed following the rules [here](07-implicits.html#implicit-parameters). +If the method takes only implicit parameters, implicit arguments are passed following the rules [here](07-implicits.html#implicit-parameters). ###### Eta Expansion -Otherwise, if the method is not a constructor, -and the expected type ´\mathit{pt}´ is a function type, or, -for methods of non-zero arity, a type [sam-convertible](#sam-conversion) to a function type, -´(\mathit{Ts}') \Rightarrow T'´, [eta-expansion](#eta-expansion-section) -is performed on the expression ´e´. +Otherwise, if the method is not a constructor, and the expected type ´\mathit{pt}´ is a function type, or, for methods of non-zero arity, a type [sam-convertible](#sam-conversion) to a function type, ´(\mathit{Ts}') \Rightarrow T'´, [eta-expansion](#eta-expansion-section) is performed on the expression ´e´. (The exception for zero-arity methods is to avoid surprises due to unexpected sam conversion.) ###### Empty Application -Otherwise, if ´e´ has method type ´()T´, it is implicitly applied to the empty -argument list, yielding ´e()´. +Otherwise, if ´e´ has method type ´()T´, it is implicitly applied to the empty argument list, yielding ´e()´. ### Overloading Resolution -If an identifier or selection ´e´ references several members of a -class, the context of the reference is used to identify a unique -member. The way this is done depends on whether or not ´e´ is used as -a function. Let ´\mathscr{A}´ be the set of members referenced by ´e´. +If an identifier or selection ´e´ references several members of a class, the context of the reference is used to identify a unique member. +The way this is done depends on whether or not ´e´ is used as a function. +Let ´\mathscr{A}´ be the set of members referenced by ´e´. -Assume first that ´e´ appears as a function in an application, as in -`´e´(´e_1´, ..., ´e_m´)`. +Assume first that ´e´ appears as a function in an application, as in `´e´(´e_1´, ..., ´e_m´)`. -One first determines the set of functions that is potentially [applicable](#function-applications) -based on the _shape_ of the arguments. +One first determines the set of functions that is potentially [applicable](#function-applications) based on the _shape_ of the arguments. -The *shape* of an argument expression ´e´, written ´\mathit{shape}(e)´, is -a type that is defined as follows: - - For a function expression `(´p_1´: ´T_1, ..., p_n´: ´T_n´) => ´b´: (Any, ..., Any) => ´\mathit{shape}(b)´`, - where `Any` occurs ´n´ times in the argument type. +The *shape* of an argument expression ´e´, written ´\mathit{shape}(e)´, is a type that is defined as follows: + - For a function expression `(´p_1´: ´T_1, ..., p_n´: ´T_n´) => ´b´: (Any, ..., Any) => ´\mathit{shape}(b)´`, where `Any` occurs ´n´ times in the argument type. - For a pattern-matching anonymous function definition `{ case ... }`: `PartialFunction[Any, Nothing]`. - For a named argument `´n´ = ´e´`: ´\mathit{shape}(e)´. - For all other expressions: `Nothing`. -Let ´\mathscr{B}´ be the set of alternatives in ´\mathscr{A}´ that are [_applicable_](#function-applications) -to expressions ´(e_1, ..., e_n)´ of types ´(\mathit{shape}(e_1), ..., \mathit{shape}(e_n))´. +Let ´\mathscr{B}´ be the set of alternatives in ´\mathscr{A}´ that are [_applicable_](#function-applications) to expressions ´(e_1, ..., e_n)´ of types ´(\mathit{shape}(e_1), ..., \mathit{shape}(e_n))´. If there is precisely one alternative in ´\mathscr{B}´, that alternative is chosen. Otherwise, let ´S_1, ..., S_m´ be the list of types obtained by typing each argument as follows. -Normally, an argument is typed without an expected type, except when -all alternatives explicitly specify the same parameter type for this argument (a missing parameter type, -due to e.g. arity differences, is taken as `NoType`, thus resorting to no expected type), -or when trying to propagate more type information to aid inference of higher-order function parameter types, as explained next. - -The intuition for higher-order function parameter type inference is that all arguments must be of a function-like type -(`PartialFunction`, `FunctionN` or some equivalent [SAM type](#sam-conversion)), -which in turn must define the same set of higher-order argument types, so that they can safely be used as -the expected type of a given argument of the overloaded method, without unduly ruling out any alternatives. -The intent is not to steer overloading resolution, but to preserve enough type information to steer type -inference of the arguments (a function literal or eta-expanded method) to this overloaded method. - -Note that the expected type drives eta-expansion (not performed unless a function-like type is expected), -as well as inference of omitted parameter types of function literals. - -More precisely, an argument `´e_i´` is typed with an expected type that is derived from the `´i´`th argument -type found in each alternative (call these `´T_{ij}´` for alternative `´j´` and argument position `´i´`) when -all `´T_{ij}´` are function types `´(A_{1j},..., A_{nj}) => ?´` (or the equivalent `PartialFunction`, or SAM) -of some arity `´n´`, and their argument types `´A_{kj}´` are identical across all overloads `´j´` for a -given `´k´`. Then, the expected type for `´e_i´` is derived as follows: +Normally, an argument is typed without an expected type, except when all alternatives explicitly specify the same parameter type for this argument (a missing parameter type, due to e.g. arity differences, is taken as `NoType`, thus resorting to no expected type), or when trying to propagate more type information to aid inference of higher-order function parameter types, as explained next. + +The intuition for higher-order function parameter type inference is that all arguments must be of a function-like type (`PartialFunction`, `FunctionN` or some equivalent [SAM type](#sam-conversion)), which in turn must define the same set of higher-order argument types, so that they can safely be used as the expected type of a given argument of the overloaded method, without unduly ruling out any alternatives. +The intent is not to steer overloading resolution, but to preserve enough type information to steer type inference of the arguments (a function literal or eta-expanded method) to this overloaded method. + +Note that the expected type drives eta-expansion (not performed unless a function-like type is expected), as well as inference of omitted parameter types of function literals. + +More precisely, an argument `´e_i´` is typed with an expected type that is derived from the `´i´`th argument type found in each alternative (call these `´T_{ij}´` for alternative `´j´` and argument position `´i´`) when all `´T_{ij}´` are function types `´(A_{1j},..., A_{nj}) => ?´` (or the equivalent `PartialFunction`, or SAM) of some arity `´n´`, and their argument types `´A_{kj}´` are identical across all overloads `´j´` for a given `´k´`. +Then, the expected type for `´e_i´` is derived as follows: - we use `´PartialFunction[A_{1j},..., A_{nj}, ?]´` if for some overload `´j´`, `´T_{ij}´`'s type symbol is `PartialFunction`; - else, if for some `´j´`, `´T_{ij}´` is `FunctionN`, the expected type is `´FunctionN[A_{1j},..., A_{nj}, ?]´`; - - else, if for all `´j´`, `´T_{ij}´` is a SAM type of the same class, defining argument types `´A_{1j},..., A_{nj}´` - (and a potentially varying result type), the expected type encodes these argument types and the SAM class. + - else, if for all `´j´`, `´T_{ij}´` is a SAM type of the same class, defining argument types `´A_{1j},..., A_{nj}´` (and a potentially varying result type), the expected type encodes these argument types and the SAM class. -For every member ´m´ in ´\mathscr{B}´ one determines whether it is applicable -to expressions (´e_1, ..., e_m´) of types ´S_1, ..., S_m´. +For every member ´m´ in ´\mathscr{B}´ one determines whether it is applicable to expressions (´e_1, ..., e_m´) of types ´S_1, ..., S_m´. -It is an error if none of the members in ´\mathscr{B}´ is applicable. If there is one -single applicable alternative, that alternative is chosen. Otherwise, let ´\mathscr{CC}´ -be the set of applicable alternatives which don't employ any default argument -in the application to ´e_1, ..., e_m´. +It is an error if none of the members in ´\mathscr{B}´ is applicable. +If there is one single applicable alternative, that alternative is chosen. +Otherwise, let ´\mathscr{CC}´ be the set of applicable alternatives which don't employ any default argument in the application to ´e_1, ..., e_m´. It is again an error if ´\mathscr{CC}´ is empty. -Otherwise, one chooses the _most specific_ alternative among the alternatives -in ´\mathscr{CC}´, according to the following definition of being "as specific as", and -"more specific than": +Otherwise, one chooses the _most specific_ alternative among the alternatives in ´\mathscr{CC}´, according to the following definition of being "as specific as", and "more specific than": -- A parameterized method ´m´ of type `(´p_1:T_1, ..., p_n:T_n´)´U´` is - _as specific as_ some other member ´m'´ of type ´S´ if ´m'´ is [applicable](#function-applications) - to arguments `(´p_1, ..., p_n´)` of types ´T_1, ..., T_n´. -- A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is - as specific as some other member of type ´S´ if ´T´ is as specific as ´S´ - under the assumption that for ´i = 1, ..., n´ each ´a_i´ is an abstract type name - bounded from below by ´L_i´ and from above by ´U_i´. +- A parameterized method ´m´ of type `(´p_1:T_1, ..., p_n:T_n´)´U´` is _as specific as_ some other member ´m'´ of type ´S´ if ´m'´ is [applicable](#function-applications) to arguments `(´p_1, ..., p_n´)` of types ´T_1, ..., T_n´. +- A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is as specific as some other member of type ´S´ if ´T´ is as specific as ´S´ under the assumption that for ´i = 1, ..., n´ each ´a_i´ is an abstract type name bounded from below by ´L_i´ and from above by ´U_i´. - A member of any other type is always as specific as a parameterized method or a polymorphic method. -- Given two members of types ´T´ and ´U´ which are neither parameterized nor polymorphic method types, - the member of type ´T´ is as specific as the member of type ´U´ if - the existential dual of ´T´ conforms to the existential dual of ´U´. - Here, the existential dual of a polymorphic type - `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is - `´T´ forSome { type ´a_1´ >: ´L_1´ <: ´U_1´, ..., type ´a_n´ >: ´L_n´ <: ´U_n´}`. - The existential dual of every other type is the type itself. +- Given two members of types ´T´ and ´U´ which are neither parameterized nor polymorphic method types, the member of type ´T´ is as specific as the member of type ´U´ if the existential dual of ´T´ conforms to the existential dual of ´U´. +Here, the existential dual of a polymorphic type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is `´T´ forSome { type ´a_1´ >: ´L_1´ <: ´U_1´, ..., type ´a_n´ >: ´L_n´ <: ´U_n´}`. +The existential dual of every other type is the type itself. The _relative weight_ of an alternative ´A´ over an alternative ´B´ is a number from 0 to 2, defined as the sum of @@ -1546,34 +1210,26 @@ number from 0 to 2, defined as the sum of - 1 if ´A´ is as specific as ´B´, 0 otherwise, and - 1 if ´A´ is defined in a class or object which is derived from the class or object defining ´B´, 0 otherwise. -A class or object ´C´ is _derived_ from a class or object ´D´ if one of -the following holds: +A class or object ´C´ is _derived_ from a class or object ´D´ if one of the following holds: - ´C´ is a subclass of ´D´, or - ´C´ is a companion object of a class derived from ´D´, or - ´D´ is a companion object of a class from which ´C´ is derived. -An alternative ´A´ is _more specific_ than an alternative ´B´ if -the relative weight of ´A´ over ´B´ is greater than the relative -weight of ´B´ over ´A´. +An alternative ´A´ is _more specific_ than an alternative ´B´ if the relative weight of ´A´ over ´B´ is greater than the relative weight of ´B´ over ´A´. -It is an error if there is no alternative in ´\mathscr{CC}´ which is more -specific than all other alternatives in ´\mathscr{CC}´. +It is an error if there is no alternative in ´\mathscr{CC}´ which is more specific than all other alternatives in ´\mathscr{CC}´. -Assume next that ´e´ appears as a function in a type application, as -in `´e´[´\mathit{targs}\,´]`. Then all alternatives in -´\mathscr{A}´ which take the same number of type parameters as there are type -arguments in ´\mathit{targs}´ are chosen. It is an error if no such alternative exists. -If there are several such alternatives, overloading resolution is -applied again to the whole expression `´e´[´\mathit{targs}\,´]`. +Assume next that ´e´ appears as a function in a type application, as in `´e´[´\mathit{targs}\,´]`. +Then all alternatives in ´\mathscr{A}´ which take the same number of type parameters as there are type arguments in ´\mathit{targs}´ are chosen. +It is an error if no such alternative exists. +If there are several such alternatives, overloading resolution is applied again to the whole expression `´e´[´\mathit{targs}\,´]`. Assume finally that ´e´ does not appear as a function in either an application or a type application. -If an expected type is given, let ´\mathscr{B}´ be the set of those alternatives -in ´\mathscr{A}´ which are [compatible](03-types.html#compatibility) to it. +If an expected type is given, let ´\mathscr{B}´ be the set of those alternatives in ´\mathscr{A}´ which are [compatible](03-types.html#compatibility) to it. Otherwise, let ´\mathscr{B}´ be the same as ´\mathscr{A}´. In this last case we choose the most specific alternative among all alternatives in ´\mathscr{B}´. -It is an error if there is no alternative in ´\mathscr{B}´ which is -more specific than all other alternatives in ´\mathscr{B}´. +It is an error if there is no alternative in ´\mathscr{B}´ which is more specific than all other alternatives in ´\mathscr{B}´. ###### Example Consider the following definitions: @@ -1586,109 +1242,69 @@ val a: A val b: B ``` -Then the application `f(b, b)` refers to the first -definition of ´f´ whereas the application `f(a, a)` -refers to the second. Assume now we add a third overloaded definition +Then the application `f(b, b)` refers to the first definition of ´f´ whereas the application `f(a, a)` refers to the second. +Assume now we add a third overloaded definition ```scala def f(x: B, y: A) = ... ``` -Then the application `f(a, a)` is rejected for being ambiguous, since -no most specific applicable signature exists. +Then the application `f(a, a)` is rejected for being ambiguous, since no most specific applicable signature exists. ### Local Type Inference -Local type inference infers type arguments to be passed to expressions -of polymorphic type. Say ´e´ is of type [´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´ -and no explicit type parameters are given. +Local type inference infers type arguments to be passed to expressions of polymorphic type. +Say ´e´ is of type [´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´ and no explicit type parameters are given. -Local type inference converts this expression to a type -application `´e´[´T_1, ..., T_n´]`. The choice of the -type arguments ´T_1, ..., T_n´ depends on the context in which -the expression appears and on the expected type ´\mathit{pt}´. +Local type inference converts this expression to a type application `´e´[´T_1, ..., T_n´]`. +The choice of the type arguments ´T_1, ..., T_n´ depends on the context in which the expression appears and on the expected type ´\mathit{pt}´. There are three cases. ###### Case 1: Selections -If the expression appears as the prefix of a selection with a name -´x´, then type inference is _deferred_ to the whole expression -´e.x´. That is, if ´e.x´ has type ´S´, it is now treated as having -type [´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´S´, -and local type inference is applied in turn to infer type arguments -for ´a_1, ..., a_n´, using the context in which ´e.x´ appears. +If the expression appears as the prefix of a selection with a name ´x´, then type inference is _deferred_ to the whole expression ´e.x´. +That is, if ´e.x´ has type ´S´, it is now treated as having type [´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´S´, and local type inference is applied in turn to infer type arguments for ´a_1, ..., a_n´, using the context in which ´e.x´ appears. ###### Case 2: Values -If the expression ´e´ appears as a value without being applied to -value arguments, the type arguments are inferred by solving a -constraint system which relates the expression's type ´T´ with the -expected type ´\mathit{pt}´. Without loss of generality we can assume that -´T´ is a value type; if it is a method type we apply -[eta-expansion](#eta-expansion-section) to convert it to a function type. Solving -means finding a substitution ´\sigma´ of types ´T_i´ for the type -parameters ´a_i´ such that - -- None of the inferred types ´T_i´ is a [singleton type](03-types.html#singleton-types) - unless it is a singleton type corresponding to an object or a constant value - definition or the corresponding bound ´U_i´ is a subtype of `scala.Singleton`. -- All type parameter bounds are respected, i.e. - ´\sigma L_i <: \sigma a_i´ and ´\sigma a_i <: \sigma U_i´ for ´i = 1, ..., n´. -- The expression's type conforms to the expected type, i.e. - ´\sigma T <: \sigma \mathit{pt}´. +If the expression ´e´ appears as a value without being applied to value arguments, the type arguments are inferred by solving a constraint system which relates the expression's type ´T´ with the expected type ´\mathit{pt}´. +Without loss of generality we can assume that ´T´ is a value type; if it is a method type we apply [eta-expansion](#eta-expansion-section) to convert it to a function type. +Solving means finding a substitution ´\sigma´ of types ´T_i´ for the type parameters ´a_i´ such that + +- None of the inferred types ´T_i´ is a [singleton type](03-types.html#singleton-types) unless it is a singleton type corresponding to an object or a constant value definition or the corresponding bound ´U_i´ is a subtype of `scala.Singleton`. +- All type parameter bounds are respected, i.e. ´\sigma L_i <: \sigma a_i´ and ´\sigma a_i <: \sigma U_i´ for ´i = 1, ..., n´. +- The expression's type conforms to the expected type, i.e. ´\sigma T <: \sigma \mathit{pt}´. It is a compile time error if no such substitution exists. -If several substitutions exist, local-type inference will choose for -each type variable ´a_i´ a minimal or maximal type ´T_i´ of the -solution space. A _maximal_ type ´T_i´ will be chosen if the type -parameter ´a_i´ appears [contravariantly](04-basic-declarations-and-definitions.html#variance-annotations) in the -type ´T´ of the expression. A _minimal_ type ´T_i´ will be chosen -in all other situations, i.e. if the variable appears covariantly, -non-variantly or not at all in the type ´T´. We call such a substitution -an _optimal solution_ of the given constraint system for the type ´T´. +If several substitutions exist, local-type inference will choose for each type variable ´a_i´ a minimal or maximal type ´T_i´ of the solution space. +A _maximal_ type ´T_i´ will be chosen if the type parameter ´a_i´ appears [contravariantly](04-basic-declarations-and-definitions.html#variance-annotations) in the type ´T´ of the expression. +A _minimal_ type ´T_i´ will be chosen in all other situations, i.e. if the variable appears covariantly, non-variantly or not at all in the type ´T´. +We call such a substitution an _optimal solution_ of the given constraint system for the type ´T´. ###### Case 3: Methods -The last case applies if the expression -´e´ appears in an application ´e(d_1, ..., d_m)´. In that case -´T´ is a method type ´(p_1:R_1, ..., p_m:R_m)T'´. Without loss of -generality we can assume that the result type ´T'´ is a value type; if -it is a method type we apply [eta-expansion](#eta-expansion-section) to -convert it to a function type. One computes first the types ´S_j´ of -the argument expressions ´d_j´, using two alternative schemes. Each -argument expression ´d_j´ is typed first with the expected type ´R_j´, -in which the type parameters ´a_1, ..., a_n´ are taken as type -constants. If this fails, the argument ´d_j´ is typed instead with an -expected type ´R_j'´ which results from ´R_j´ by replacing every type -parameter in ´a_1, ..., a_n´ with _undefined_. - -In a second step, type arguments are inferred by solving a constraint -system which relates the method's type with the expected type -´\mathit{pt}´ and the argument types ´S_1, ..., S_m´. Solving the -constraint system means -finding a substitution ´\sigma´ of types ´T_i´ for the type parameters -´a_i´ such that - -- None of the inferred types ´T_i´ is a [singleton type](03-types.html#singleton-types) - unless it is a singleton type corresponding to an object or a constant value - definition or the corresponding bound ´U_i´ is a subtype of `scala.Singleton`. -- All type parameter bounds are respected, i.e. ´\sigma L_i <: \sigma a_i´ and - ´\sigma a_i <: \sigma U_i´ for ´i = 1, ..., n´. +The last case applies if the expression ´e´ appears in an application ´e(d_1, ..., d_m)´. +In that case ´T´ is a method type ´(p_1:R_1, ..., p_m:R_m)T'´. +Without loss of generality we can assume that the result type ´T'´ is a value type; if it is a method type we apply [eta-expansion](#eta-expansion-section) to +convert it to a function type. +One computes first the types ´S_j´ of the argument expressions ´d_j´, using two alternative schemes. +Each argument expression ´d_j´ is typed first with the expected type ´R_j´, in which the type parameters ´a_1, ..., a_n´ are taken as type constants. +If this fails, the argument ´d_j´ is typed instead with an expected type ´R_j'´ which results from ´R_j´ by replacing every type parameter in ´a_1, ..., a_n´ with _undefined_. + +In a second step, type arguments are inferred by solving a constraint system which relates the method's type with the expected type ´\mathit{pt}´ and the argument types ´S_1, ..., S_m´. +Solving the constraint system means finding a substitution ´\sigma´ of types ´T_i´ for the type parameters ´a_i´ such that + +- None of the inferred types ´T_i´ is a [singleton type](03-types.html#singleton-types) unless it is a singleton type corresponding to an object or a constant value definition or the corresponding bound ´U_i´ is a subtype of `scala.Singleton`. +- All type parameter bounds are respected, i.e. ´\sigma L_i <: \sigma a_i´ and ´\sigma a_i <: \sigma U_i´ for ´i = 1, ..., n´. - The method's result type ´T'´ conforms to the expected type, i.e. ´\sigma T' <: \sigma \mathit{pt}´. -- Each argument type [weakly conforms](03-types.html#weak-conformance) - to the corresponding formal parameter - type, i.e. ´\sigma S_j <:_w \sigma R_j´ for ´j = 1, ..., m´. +- Each argument type [weakly conforms](03-types.html#weak-conformance) to the corresponding formal parameter type, i.e. ´\sigma S_j <:_w \sigma R_j´ for ´j = 1, ..., m´. -It is a compile time error if no such substitution exists. If several -solutions exist, an optimal one for the type ´T'´ is chosen. +It is a compile time error if no such substitution exists. +If several solutions exist, an optimal one for the type ´T'´ is chosen. -All or parts of an expected type ´\mathit{pt}´ may be undefined. The rules for -[conformance](03-types.html#conformance) are extended to this case by adding -the rule that for any type ´T´ the following two statements are always -true: ´\mathit{undefined} <: T´ and ´T <: \mathit{undefined}´ +All or parts of an expected type ´\mathit{pt}´ may be undefined. +The rules for [conformance](03-types.html#conformance) are extended to this case by adding the rule that for any type ´T´ the following two statements are always true: ´\mathit{undefined} <: T´ and ´T <: \mathit{undefined}´ -It is possible that no minimal or maximal solution for a type variable -exists, in which case a compile-time error results. Because ´<:´ is a -pre-order, it is also possible that a solution set has several optimal -solutions for a type. In that case, a Scala compiler is free to pick -any one of them. +It is possible that no minimal or maximal solution for a type variable exists, in which case a compile-time error results. +Because ´<:´ is a pre-order, it is also possible that a solution set has several optimal solutions for a type. +In that case, a Scala compiler is free to pick any one of them. ###### Example Consider the two methods: @@ -1704,33 +1320,26 @@ and the definition val xs = cons(1, nil) ``` -The application of `cons` is typed with an undefined expected -type. This application is completed by local type inference to -`cons[Int](1, nil)`. -Here, one uses the following -reasoning to infer the type argument `Int` for the type -parameter `a`: +The application of `cons` is typed with an undefined expected type. +This application is completed by local type inference to `cons[Int](1, nil)`. +Here, one uses the following reasoning to infer the type argument `Int` for the type parameter `a`: -First, the argument expressions are typed. The first argument `1` -has type `Int` whereas the second argument `nil` is -itself polymorphic. One tries to type-check `nil` with an -expected type `List[a]`. This leads to the constraint system +First, the argument expressions are typed. The first argument `1` has type `Int` whereas the second argument `nil` is itself polymorphic. +One tries to type-check `nil` with an expected type `List[a]`. +This leads to the constraint system ```scala List[b?] <: List[a] ``` -where we have labeled `b?` with a question mark to indicate -that it is a variable in the constraint system. -Because class `List` is covariant, the optimal -solution of this constraint is +where we have labeled `b?` with a question mark to indicate that it is a variable in the constraint system. +Because class `List` is covariant, the optimal solution of this constraint is ```scala b = scala.Nothing ``` -In a second step, one solves the following constraint system for -the type parameter `a` of `cons`: +In a second step, one solves the following constraint system for the type parameter `a` of `cons`: ```scala Int <: a? @@ -1757,16 +1366,14 @@ val ys = cons("abc", xs) where `xs` is defined of type `List[Int]` as before. In this case local type inference proceeds as follows. -First, the argument expressions are typed. The first argument -`"abc"` has type `String`. The second argument `xs` is -first tried to be typed with expected type `List[a]`. This fails, -as `List[Int]` is not a subtype of `List[a]`. Therefore, -the second strategy is tried; `xs` is now typed with expected type -`List[´\mathit{undefined}´]`. This succeeds and yields the argument type -`List[Int]`. +First, the argument expressions are typed. +The first argument `"abc"` has type `String`. +The second argument `xs` is first tried to be typed with expected type `List[a]`. +This fails,as `List[Int]` is not a subtype of `List[a]`. +Therefore, the second strategy is tried; `xs` is now typed with expected type `List[´\mathit{undefined}´]`. +This succeeds and yields the argument type `List[Int]`. -In a second step, one solves the following constraint system for -the type parameter `a` of `cons`: +In a second step, one solves the following constraint system for the type parameter `a` of `cons`: ```scala String <: a? @@ -1784,16 +1391,14 @@ so `scala.Any` is the type inferred for `a`. ### Eta Expansion -_Eta-expansion_ converts an expression of method type to an -equivalent expression of function type. It proceeds in two steps. +_Eta-expansion_ converts an expression of method type to an equivalent expression of function type. +It proceeds in two steps. -First, one identifies the maximal sub-expressions of ´e´; let's -say these are ´e_1, ..., e_m´. For each of these, one creates a -fresh name ´x_i´. Let ´e'´ be the expression resulting from -replacing every maximal subexpression ´e_i´ in ´e´ by the -corresponding fresh name ´x_i´. Second, one creates a fresh name ´y_i´ -for every argument type ´T_i´ of the method (´i = 1 , ..., -n´). The result of eta-conversion is then: +First, one identifies the maximal sub-expressions of ´e´; let's say these are ´e_1, ..., e_m´. +For each of these, one creates a fresh name ´x_i´. +Let ´e'´ be the expression resulting from replacing every maximal subexpression ´e_i´ in ´e´ by the corresponding fresh name ´x_i´. +Second, one creates a fresh name ´y_i´ for every argument type ´T_i´ of the method (´i = 1 , ..., n´). +The result of eta-conversion is then: ```scala { val ´x_1´ = ´e_1´; @@ -1803,13 +1408,12 @@ n´). The result of eta-conversion is then: } ``` -The behavior of [call-by-name parameters](#function-applications) -is preserved under eta-expansion: the corresponding actual argument expression, -a sub-expression of parameterless method type, is not evaluated in the expanded block. +The behavior of [call-by-name parameters](#function-applications) is preserved under eta-expansion: the corresponding actual argument expression, a sub-expression of parameterless method type, is not evaluated in the expanded block. ### Dynamic Member Selection -The standard Scala library defines a marker trait `scala.Dynamic`. Subclasses of this trait are able to intercept selections and applications on their instances by defining methods of the names `applyDynamic`, `applyDynamicNamed`, `selectDynamic`, and `updateDynamic`. +The standard Scala library defines a marker trait `scala.Dynamic`. +Subclasses of this trait are able to intercept selections and applications on their instances by defining methods of the names `applyDynamic`, `applyDynamicNamed`, `selectDynamic`, and `updateDynamic`. The following rewrites are performed, assuming ´e´'s type conforms to `scala.Dynamic`, and the original expression does not type check under the normal rules, as specified fully in the relevant subsection of [implicit conversion](#dynamic-member-selection): diff --git a/docs/_spec/07-implicits.md b/docs/_spec/07-implicits.md index 47dc4ca0b0d4..a0db9dd9b418 100644 --- a/docs/_spec/07-implicits.md +++ b/docs/_spec/07-implicits.md @@ -13,17 +13,13 @@ LocalModifier ::= ‘implicit’ ParamClauses ::= {ParamClause} [nl] ‘(’ ‘implicit’ Params ‘)’ ``` -Template members and parameters labeled with an `implicit` -modifier can be passed to [implicit parameters](#implicit-parameters) -and can be used as implicit conversions called [views](#views). -The `implicit` modifier is illegal for all -type members, as well as for [top-level objects](09-top-level-definitions.html#packagings). +Template members and parameters labeled with an `implicit` modifier can be passed to [implicit parameters](#implicit-parameters) and can be used as implicit conversions called [views](#views). +The `implicit` modifier is illegal for all type members, as well as for [top-level objects](09-top-level-definitions.html#packagings). ###### Example Monoid -The following code defines an abstract class of monoids and -two concrete implementations, `StringMonoid` and -`IntMonoid`. The two implementations are marked implicit. +The following code defines an abstract class of monoids and two concrete implementations, `StringMonoid` and `IntMonoid`. +The two implementations are marked implicit. ```scala abstract class Monoid[A] extends SemiGroup[A] { @@ -44,61 +40,41 @@ object Monoids { ## Implicit Parameters -An _implicit parameter list_ -`(implicit ´p_1´,...,´p_n´)` of a method marks the parameters ´p_1, ..., p_n´ as -implicit. A method or constructor can have only one implicit parameter -list, and it must be the last parameter list given. - -A method with implicit parameters can be applied to arguments just -like a normal method. In this case the `implicit` label has no -effect. However, if such a method misses arguments for its implicit -parameters, such arguments will be automatically provided. - -The actual arguments that are eligible to be passed to an implicit -parameter of type ´T´ fall into two categories. First, eligible are -all identifiers ´x´ that can be accessed at the point of the method -call without a prefix and that denote an -[implicit definition](#the-implicit-modifier) -or an implicit parameter. To be accessible without a prefix, an identifier -must be a local name, a member of an enclosing template or a name introduced by an -[import clause](04-basic-declarations-and-definitions.html#import-clauses). If there are no eligible -identifiers under this rule, then, second, eligible are also all -`implicit` members of some object that belongs to the implicit -scope of the implicit parameter's type, ´T´. +An _implicit parameter list_ `(implicit ´p_1´,...,´p_n´)` of a method marks the parameters ´p_1, ..., p_n´ as implicit. +A method or constructor can have only one implicit parameter list, and it must be the last parameter list given. + +A method with implicit parameters can be applied to arguments just like a normal method. +In this case the `implicit` label has no effect. +However, if such a method misses arguments for its implicit parameters, such arguments will be automatically provided. + +The actual arguments that are eligible to be passed to an implicit parameter of type ´T´ fall into two categories. +First, eligible are all identifiers ´x´ that can be accessed at the point of the method call without a prefix and that denote an [implicit definition](#the-implicit-modifier) or an implicit parameter. +To be accessible without a prefix, an identifier must be a local name, a member of an enclosing template or a name introduced by an [import clause](04-basic-declarations-and-definitions.html#import-clauses). +If there are no eligible identifiers under this rule, then, second, eligible are also all `implicit` members of some object that belongs to the implicit scope of the implicit parameter's type, ´T´. The _implicit scope_ of a type ´T´ consists of all [companion modules](05-classes-and-objects.html#object-definitions) of classes that are associated with the implicit parameter's type. Here, we say a class ´C´ is _associated_ with a type ´T´ if it is a [base class](05-classes-and-objects.html#class-linearization) of some part of ´T´. The _parts_ of a type ´T´ are: -- if ´T´ is a compound type `´T_1´ with ... with ´T_n´`, - the union of the parts of ´T_1, ..., T_n´, as well as ´T´ itself; -- if ´T´ is a parameterized type `´S´[´T_1, ..., T_n´]`, - the union of the parts of ´S´ and ´T_1, ..., T_n´; -- if ´T´ is a singleton type `´p´.type`, - the parts of the type of ´p´; -- if ´T´ is a type projection `´S´#´U´`, - the parts of ´S´ as well as ´T´ itself; +- if ´T´ is a compound type `´T_1´ with ... with ´T_n´`, the union of the parts of ´T_1, ..., T_n´, as well as ´T´ itself; +- if ´T´ is a parameterized type `´S´[´T_1, ..., T_n´]`, the union of the parts of ´S´ and ´T_1, ..., T_n´; +- if ´T´ is a singleton type `´p´.type`, the parts of the type of ´p´; +- if ´T´ is a type projection `´S´#´U´`, the parts of ´S´ as well as ´T´ itself; - if ´T´ is a type alias, the parts of its expansion; - if ´T´ is an abstract type, the parts of its upper bound; -- if ´T´ denotes an implicit conversion to a type with a method with argument types ´T_1, ..., T_n´ and result type ´U´, - the union of the parts of ´T_1, ..., T_n´ and ´U´; +- if ´T´ denotes an implicit conversion to a type with a method with argument types ´T_1, ..., T_n´ and result type ´U´, the union of the parts of ´T_1, ..., T_n´ and ´U´; - the parts of quantified (existential or universal) and annotated types are defined as the parts of the underlying types (e.g., the parts of `T forSome { ... }` are the parts of `T`); - in all other cases, just ´T´ itself. Note that packages are internally represented as classes with companion modules to hold the package members. Thus, implicits defined in a package object are part of the implicit scope of a type prefixed by that package. -If there are several eligible arguments which match the implicit -parameter's type, a most specific one will be chosen using the rules -of static [overloading resolution](06-expressions.html#overloading-resolution). -If the parameter has a default argument and no implicit argument can -be found the default argument is used. +If there are several eligible arguments which match the implicit parameter's type, a most specific one will be chosen using the rules of static [overloading resolution](06-expressions.html#overloading-resolution). +If the parameter has a default argument and no implicit argument can be found the default argument is used. ###### Example -Assuming the classes from the [`Monoid` example](#example-monoid), here is a -method which computes the sum of a list of elements using the -monoid's `add` and `unit` operations. +Assuming the classes from the [`Monoid` example](#example-monoid), here is a method which computes the sum of a list of elements using the monoid's `add` and `unit` operations. ```scala def sum[A](xs: List[A])(implicit m: Monoid[A]): A = @@ -106,23 +82,15 @@ def sum[A](xs: List[A])(implicit m: Monoid[A]): A = else m.add(xs.head, sum(xs.tail)) ``` -The monoid in question is marked as an implicit parameter, and can therefore -be inferred based on the type of the list. -Consider for instance the call `sum(List(1, 2, 3))` -in a context where `stringMonoid` and `intMonoid` -are visible. We know that the formal type parameter `a` of -`sum` needs to be instantiated to `Int`. The only -eligible object which matches the implicit formal parameter type -`Monoid[Int]` is `intMonoid` so this object will -be passed as implicit parameter. +The monoid in question is marked as an implicit parameter, and can therefore be inferred based on the type of the list. +Consider for instance the call `sum(List(1, 2, 3))` in a context where `stringMonoid` and `intMonoid` are visible. +We know that the formal type parameter `a` of `sum` needs to be instantiated to `Int`. +The only eligible object which matches the implicit formal parameter type `Monoid[Int]` is `intMonoid` so this object will be passed as implicit parameter. -This discussion also shows that implicit parameters are inferred after -any type arguments are [inferred](06-expressions.html#local-type-inference). +This discussion also shows that implicit parameters are inferred after any type arguments are [inferred](06-expressions.html#local-type-inference). -Implicit methods can themselves have implicit parameters. An example -is the following method from module `scala.List`, which injects -lists into the `scala.Ordered` class, provided the element -type of the list is also convertible to this type. +Implicit methods can themselves have implicit parameters. +An example is the following method from module `scala.List`, which injects lists into the `scala.Ordered` class, provided the element type of the list is also convertible to this type. ```scala implicit def list2ordered[A](x: List[A]) @@ -136,16 +104,14 @@ Assume in addition a method implicit def int2ordered(x: Int): Ordered[Int] ``` -that injects integers into the `Ordered` class. We can now -define a `sort` method over ordered lists: +that injects integers into the `Ordered` class. +We can now define a `sort` method over ordered lists: ```scala def sort[A](xs: List[A])(implicit a2ordered: A => Ordered[A]) = ... ``` -We can apply `sort` to a list of lists of integers -`yss: List[List[Int]]` -as follows: +We can apply `sort` to a list of lists of integers `yss: List[List[Int]]` as follows: ```scala sort(yss) @@ -157,28 +123,22 @@ The call above will be completed by passing two nested implicit arguments: sort(yss)((xs: List[Int]) => list2ordered[Int](xs)(int2ordered)) ``` -The possibility of passing implicit arguments to implicit arguments -raises the possibility of an infinite recursion. For instance, one -might try to define the following method, which injects _every_ type into the -`Ordered` class: +The possibility of passing implicit arguments to implicit arguments raises the possibility of an infinite recursion. +For instance, one might try to define the following method, which injects _every_ type into the `Ordered` class: ```scala implicit def magic[A](x: A)(implicit a2ordered: A => Ordered[A]): Ordered[A] = a2ordered(x) ``` -Now, if one tried to apply -`sort` to an argument `arg` of a type that did not have -another injection into the `Ordered` class, one would obtain an infinite -expansion: +Now, if one tried to apply `sort` to an argument `arg` of a type that did not have another injection into the `Ordered` class, one would obtain an infinite expansion: ```scala sort(arg)(x => magic(x)(x => magic(x)(x => ... ))) ``` -Such infinite expansions should be detected and reported as errors, however to support the deliberate -implicit construction of recursive values we allow implicit arguments to be marked as by-name. At call -sites recursive uses of implicit values are permitted if they occur in an implicit by-name argument. +Such infinite expansions should be detected and reported as errors, however to support the deliberate implicit construction of recursive values we allow implicit arguments to be marked as by-name. +At call sites recursive uses of implicit values are permitted if they occur in an implicit by-name argument. Consider the following example, @@ -196,8 +156,8 @@ val foo = implicitly[Foo] assert(foo eq foo.next) ``` -As with the `magic` case above this diverges due to the recursive implicit argument `rec` of method -`foo`. If we mark the implicit argument as by-name, +As with the `magic` case above this diverges due to the recursive implicit argument `rec` of method `foo`. +If we mark the implicit argument as by-name, ```scala trait Foo { @@ -215,8 +175,7 @@ assert(foo eq foo.next) the example compiles with the assertion successful. -When compiled, recursive by-name implicit arguments of this sort are extracted out as val members of a -local synthetic object at call sites as follows, +When compiled, recursive by-name implicit arguments of this sort are extracted out as val members of a local synthetic object at call sites as follows, ```scala val foo: Foo = scala.Predef.implicitly[Foo]( @@ -232,46 +191,33 @@ val foo: Foo = scala.Predef.implicitly[Foo]( assert(foo eq foo.next) ``` -Note that the recursive use of `rec$1` occurs within the by-name argument of `foo` and is consequently -deferred. The desugaring matches what a programmer would do to construct such a recursive value -explicitly. - -To prevent infinite expansions, such as the `magic` example above, the compiler keeps track of a stack -of “open implicit types” for which implicit arguments are currently being searched. Whenever an -implicit argument for type ´T´ is searched, ´T´ is added to the stack paired with the implicit -definition which produces it, and whether it was required to satisfy a by-name implicit argument or -not. The type is removed from the stack once the search for the implicit argument either definitely -fails or succeeds. Everytime a type is about to be added to the stack, it is checked against -existing entries which were produced by the same implicit definition and then, - -+ if it is equivalent to some type which is already on the stack and there is a by-name argument between - that entry and the top of the stack. In this case the search for that type succeeds immediately and - the implicit argument is compiled as a recursive reference to the found argument. That argument is - added as an entry in the synthesized implicit dictionary if it has not already been added. -+ otherwise if the _core_ of the type _dominates_ the core of a type already on the stack, then the - implicit expansion is said to _diverge_ and the search for that type fails immediately. +Note that the recursive use of `rec$1` occurs within the by-name argument of `foo` and is consequently deferred. +The desugaring matches what a programmer would do to construct such a recursive value explicitly. + +To prevent infinite expansions, such as the `magic` example above, the compiler keeps track of a stack of “open implicit types” for which implicit arguments are currently being searched. +Whenever an implicit argument for type ´T´ is searched, ´T´ is added to the stack paired with the implicit definition which produces it, and whether it was required to satisfy a by-name implicit argument or not. +The type is removed from the stack once the search for the implicit argument either definitely fails or succeeds. +Everytime a type is about to be added to the stack, it is checked against existing entries which were produced by the same implicit definition and then, + ++ if it is equivalent to some type which is already on the stack and there is a by-name argument between that entry and the top of the stack. +In this case the search for that type succeeds immediately and the implicit argument is compiled as a recursive reference to the found argument. +That argument is added as an entry in the synthesized implicit dictionary if it has not already been added. ++ otherwise if the _core_ of the type _dominates_ the core of a type already on the stack, then the implicit expansion is said to _diverge_ and the search for that type fails immediately. + otherwise it is added to the stack paired with the implicit definition which produces it. - Implicit resolution continues with the implicit arguments of that definition (if any). +Implicit resolution continues with the implicit arguments of that definition (if any). -Here, the _core type_ of ´T´ is ´T´ with aliases expanded, -top-level type [annotations](11-annotations.html#user-defined-annotations) and -[refinements](03-types.html#compound-types) removed, and occurrences of top-level existentially bound -variables replaced by their upper bounds. +Here, the _core type_ of ´T´ is ´T´ with aliases expanded, top-level type [annotations](11-annotations.html#user-defined-annotations) and [refinements](03-types.html#compound-types) removed, and occurrences of top-level existentially bound variables replaced by their upper bounds. -A core type ´T´ _dominates_ a type ´U´ if ´T´ is [equivalent](03-types.html#equivalence) to ´U´, -or if the top-level type constructors of ´T´ and ´U´ have a common element and ´T´ is more complex -than ´U´ and the _covering sets_ of ´T´ and ´U´ are equal. +A core type ´T´ _dominates_ a type ´U´ if ´T´ is [equivalent](03-types.html#equivalence) to ´U´, or if the top-level type constructors of ´T´ and ´U´ have a common element and ´T´ is more complex than ´U´ and the _covering sets_ of ´T´ and ´U´ are equal. -The set of _top-level type constructors_ ´\mathit{ttcs}(T)´ of a type ´T´ depends on the form of -the type: +The set of _top-level type constructors_ ´\mathit{ttcs}(T)´ of a type ´T´ depends on the form of the type: - For a type designator, ´\mathit{ttcs}(p.c) ~=~ \{c\}´; - For a parameterized type, ´\mathit{ttcs}(p.c[\mathit{targs}]) ~=~ \{c\}´; - For a singleton type, ´\mathit{ttcs}(p.type) ~=~ \mathit{ttcs}(T)´, provided ´p´ has type ´T´; - For a compound type, `´\mathit{ttcs}(T_1´ with ... with ´T_n)´` ´~=~ \mathit{ttcs}(T_1) \cup ... \cup \mathit{ttcs}(T_n)´. -The _complexity_ ´\operatorname{complexity}(T)´ of a core type is an integer which also depends on the form of -the type: +The _complexity_ ´\operatorname{complexity}(T)´ of a core type is an integer which also depends on the form of the type: - For a type designator, ´\operatorname{complexity}(p.c) ~=~ 1 + \operatorname{complexity}(p)´ - For a parameterized type, ´\operatorname{complexity}(p.c[\mathit{targs}]) ~=~ 1 + \Sigma \operatorname{complexity}(\mathit{targs})´ @@ -295,9 +241,7 @@ the corresponding covering sets are: - ´\mathit{cs}(C)´: List, Tuple2, Int, String ###### Example -When typing `sort(xs)` for some list `xs` of type `List[List[List[Int]]]`, -the sequence of types for -which implicit arguments are searched is +When typing `sort(xs)` for some list `xs` of type `List[List[List[Int]]]`, the sequence of types for which implicit arguments are searched is ```scala List[List[Int]] => Ordered[List[List[Int]]], @@ -305,20 +249,19 @@ List[Int] => Ordered[List[Int]], Int => Ordered[Int] ``` -All types share the common type constructor `scala.Function1`, -but the complexity of each new type is lower than the complexity of the previous types. +All types share the common type constructor `scala.Function1`, but the complexity of each new type is lower than the complexity of the previous types. Hence, the code typechecks. ###### Example -Let `ys` be a list of some type which cannot be converted -to `Ordered`. For instance: +Let `ys` be a list of some type which cannot be converted to `Ordered`. +For instance: ```scala val ys = List(new IllegalArgumentException, new ClassCastException, new Error) ``` -Assume that the definition of `magic` above is in scope. Then the sequence -of types for which implicit arguments are searched is +Assume that the definition of `magic` above is in scope. +Then the sequence of types for which implicit arguments are searched is ```scala Throwable => Ordered[Throwable], @@ -326,47 +269,31 @@ Throwable => Ordered[Throwable], ... ``` -Since the second type in the sequence is equal to the first, the compiler -will issue an error signalling a divergent implicit expansion. +Since the second type in the sequence is equal to the first, the compiler will issue an error signalling a divergent implicit expansion. ## Views -Implicit parameters and methods can also define implicit conversions -called views. A _view_ from type ´S´ to type ´T´ is -defined by an implicit value which has function type -`´S´ => ´T´` or `(=> ´S´) => ´T´` or by a method convertible to a value of that -type. +Implicit parameters and methods can also define implicit conversions called views. +A _view_ from type ´S´ to type ´T´ is defined by an implicit value which has function type `´S´ => ´T´` or `(=> ´S´) => ´T´` or by a method convertible to a value of that type. Views are applied in three situations: -1. If an expression ´e´ is of type ´T´, and ´T´ does not conform to the - expression's expected type ´\mathit{pt}´. In this case an implicit ´v´ is - searched which is applicable to ´e´ and whose result type conforms to - ´\mathit{pt}´. The search proceeds as in the case of implicit parameters, - where the implicit scope is the one of `´T´ => ´\mathit{pt}´`. If - such a view is found, the expression ´e´ is converted to - `´v´(´e´)`. -1. In a selection ´e.m´ with ´e´ of type ´T´, if the selector ´m´ does - not denote an accessible member of ´T´. In this case, a view ´v´ is searched - which is applicable to ´e´ and whose result contains a member named - ´m´. The search proceeds as in the case of implicit parameters, where - the implicit scope is the one of ´T´. If such a view is found, the - selection ´e.m´ is converted to `´v´(´e´).´m´`. -1. In a selection ´e.m(\mathit{args})´ with ´e´ of type ´T´, if the selector - ´m´ denotes some member(s) of ´T´, but none of these members is applicable to the arguments - ´\mathit{args}´. In this case a view ´v´ is searched which is applicable to ´e´ - and whose result contains a method ´m´ which is applicable to ´\mathit{args}´. - The search proceeds as in the case of implicit parameters, where - the implicit scope is the one of ´T´. If such a view is found, the - selection ´e.m´ is converted to `´v´(´e´).´m(\mathit{args})´`. - -The implicit view, if it is found, can accept its argument ´e´ as a -call-by-value or as a call-by-name parameter. However, call-by-value -implicits take precedence over call-by-name implicits. - -As for implicit parameters, overloading resolution is applied -if there are several possible candidates (of either the call-by-value -or the call-by-name category). +1. If an expression ´e´ is of type ´T´, and ´T´ does not conform to the expression's expected type ´\mathit{pt}´. +In this case an implicit ´v´ is searched which is applicable to ´e´ and whose result type conforms to ´\mathit{pt}´. +The search proceeds as in the case of implicit parameters, where the implicit scope is the one of `´T´ => ´\mathit{pt}´`. +If such a view is found, the expression ´e´ is converted to `´v´(´e´)`. +1. In a selection ´e.m´ with ´e´ of type ´T´, if the selector ´m´ does not denote an accessible member of ´T´. +In this case, a view ´v´ is searched which is applicable to ´e´ and whose result contains a member named ´m´. +The search proceeds as in the case of implicit parameters, where the implicit scope is the one of ´T´. +If such a view is found, the selection ´e.m´ is converted to `´v´(´e´).´m´`. +1. In a selection ´e.m(\mathit{args})´ with ´e´ of type ´T´, if the selector ´m´ denotes some member(s) of ´T´, but none of these members is applicable to the arguments ´\mathit{args}´. +In this case a view ´v´ is searched which is applicable to ´e´ and whose result contains a method ´m´ which is applicable to ´\mathit{args}´. +The search proceeds as in the case of implicit parameters, where the implicit scope is the one of ´T´. If such a view is found, the selection ´e.m´ is converted to `´v´(´e´).´m(\mathit{args})´`. + +The implicit view, if it is found, can accept its argument ´e´ as a call-by-value or as a call-by-name parameter. +However, call-by-value implicits take precedence over call-by-name implicits. + +As for implicit parameters, overloading resolution is applied if there are several possible candidates (of either the call-by-value or the call-by-name category). ###### Example Ordered @@ -376,9 +303,7 @@ Class `scala.Ordered[A]` contains a method def <= [B >: A](that: B)(implicit b2ordered: B => Ordered[B]): Boolean ``` -Assume two lists `xs` and `ys` of type `List[Int]` -and assume that the `list2ordered` and `int2ordered` -methods defined [here](#implicit-parameters) are in scope. +Assume two lists `xs` and `ys` of type `List[Int]` and assume that the `list2ordered` and `int2ordered` methods defined [here](#implicit-parameters) are in scope. Then the operation ```scala @@ -393,10 +318,7 @@ is legal, and is expanded to: (xs => list2ordered(xs)(int2ordered)) ``` -The first application of `list2ordered` converts the list -`xs` to an instance of class `Ordered`, whereas the second -occurrence is part of an implicit parameter passed to the `<=` -method. +The first application of `list2ordered` converts the list `xs` to an instance of class `Ordered`, whereas the second occurrence is part of an implicit parameter passed to the `<=` method. ## Context Bounds and View Bounds @@ -405,20 +327,15 @@ method. {‘<%’ Type} {‘:’ Type} ``` -A type parameter ´A´ of a method or non-trait class may have one or more view -bounds `´A´ <% ´T´`. In this case the type parameter may be -instantiated to any type ´S´ which is convertible by application of a -view to the bound ´T´. +A type parameter ´A´ of a method or non-trait class may have one or more view bounds `´A´ <% ´T´`. +In this case the type parameter may be instantiated to any type ´S´ which is convertible by application of a view to the bound ´T´. -A type parameter ´A´ of a method or non-trait class may also have one -or more context bounds `´A´ : ´T´`. In this case the type parameter may be -instantiated to any type ´S´ for which _evidence_ exists at the -instantiation point that ´S´ satisfies the bound ´T´. Such evidence -consists of an implicit value with type ´T[S]´. +A type parameter ´A´ of a method or non-trait class may also have one or more context bounds `´A´ : ´T´`. +In this case the type parameter may be instantiated to any type ´S´ for which _evidence_ exists at the instantiation point that ´S´ satisfies the bound ´T´. +Such evidence consists of an implicit value with type ´T[S]´. -A method or class containing type parameters with view or context bounds is treated as being -equivalent to a method with implicit parameters. Consider first the case of a -single parameter with view and/or context bounds such as: +A method or class containing type parameters with view or context bounds is treated as being equivalent to a method with implicit parameters. +Consider first the case of a single parameter with view and/or context bounds such as: ```scala def ´f´[´A´ <% ´T_1´ ... <% ´T_m´ : ´U_1´ : ´U_n´](´\mathit{ps}´): ´R´ = ... @@ -431,14 +348,11 @@ def ´f´[´A´](´\mathit{ps}´)(implicit ´v_1´: ´A´ => ´T_1´, ..., ´v_m ´w_1´: ´U_1´[´A´], ..., ´w_n´: ´U_n´[´A´]): ´R´ = ... ``` -where the ´v_i´ and ´w_j´ are fresh names for the newly introduced implicit parameters. These -parameters are called _evidence parameters_. +where the ´v_i´ and ´w_j´ are fresh names for the newly introduced implicit parameters. +These parameters are called _evidence parameters_. -If a class or method has several view- or context-bounded type parameters, each -such type parameter is expanded into evidence parameters in the order -they appear and all the resulting evidence parameters are concatenated -in one implicit parameter section. Since traits do not take -constructor parameters, this translation does not work for them. +If a class or method has several view- or context-bounded type parameters, each such type parameter is expanded into evidence parameters in the order they appear and all the resulting evidence parameters are concatenated in one implicit parameter section. +Since traits do not take constructor parameters, this translation does not work for them. Consequently, type-parameters in traits may not be view- or context-bounded. Evidence parameters are prepended to the existing implicit parameter section, if one exists. @@ -452,8 +366,7 @@ def foo[A: M](implicit b: B): C ``` ###### Example -The `<=` method from the [`Ordered` example](#example-ordered) can be declared -more concisely as follows: +The `<=` method from the [`Ordered` example](#example-ordered) can be declared more concisely as follows: ```scala def <= [B >: A <% Ordered[B]](that: B): Boolean @@ -461,11 +374,9 @@ def <= [B >: A <% Ordered[B]](that: B): Boolean ## Manifests -Manifests are type descriptors that can be automatically generated by -the Scala compiler as arguments to implicit parameters. The Scala -standard library contains a hierarchy of four manifest classes, -with `OptManifest` -at the top. Their signatures follow the outline below. +Manifests are type descriptors that can be automatically generated by the Scala compiler as arguments to implicit parameters. +The Scala standard library contains a hierarchy of four manifest classes, with `OptManifest` at the top. +Their signatures follow the outline below. ```scala trait OptManifest[+T] @@ -474,52 +385,23 @@ trait ClassManifest[T] extends OptManifest[T] trait Manifest[T] extends ClassManifest[T] ``` -If an implicit parameter of a method or constructor is of a subtype ´M[T]´ of -class `OptManifest[T]`, _a manifest is determined for ´M[S]´_, -according to the following rules. +If an implicit parameter of a method or constructor is of a subtype ´M[T]´ of class `OptManifest[T]`, _a manifest is determined for ´M[S]´_, according to the following rules. -First if there is already an implicit argument that matches ´M[T]´, this -argument is selected. +First if there is already an implicit argument that matches ´M[T]´, this argument is selected. -Otherwise, let ´\mathit{Mobj}´ be the companion object `scala.reflect.Manifest` -if ´M´ is trait `Manifest`, or be -the companion object `scala.reflect.ClassManifest` otherwise. Let ´M'´ be the trait -`Manifest` if ´M´ is trait `Manifest`, or be the trait `OptManifest` otherwise. +Otherwise, let ´\mathit{Mobj}´ be the companion object `scala.reflect.Manifest` if ´M´ is trait `Manifest`, or be the companion object `scala.reflect.ClassManifest` otherwise. +Let ´M'´ be the trait `Manifest` if ´M´ is trait `Manifest`, or be the trait `OptManifest` otherwise. Then the following rules apply. -1. If ´T´ is a value class or one of the classes `Any`, `AnyVal`, `Object`, - `Null`, or `Nothing`, - a manifest for it is generated by selecting - the corresponding manifest value `Manifest.´T´`, which exists in the - `Manifest` module. -1. If ´T´ is an instance of `Array[´S´]`, a manifest is generated - with the invocation `´\mathit{Mobj}´.arrayType[S](m)`, where ´m´ is the manifest - determined for ´M[S]´. -1. If ´T´ is some other class type ´S´#´C[U_1, ..., U_n]´ where the prefix - type ´S´ cannot be statically determined from the class ´C´, - a manifest is generated with the invocation `´\mathit{Mobj}´.classType[T](´m_0´, classOf[T], ´ms´)` - where ´m_0´ is the manifest determined for ´M'[S]´ and ´ms´ are the - manifests determined for ´M'[U_1], ..., M'[U_n]´. -1. If ´T´ is some other class type with type arguments ´U_1, ..., U_n´, - a manifest is generated - with the invocation `´\mathit{Mobj}´.classType[T](classOf[T], ´ms´)` - where ´ms´ are the - manifests determined for ´M'[U_1], ..., M'[U_n]´. -1. If ´T´ is a singleton type `´p´.type`, a manifest is generated with - the invocation `´\mathit{Mobj}´.singleType[T](´p´)` +1. If ´T´ is a value class or one of the classes `Any`, `AnyVal`, `Object`, `Null`, or `Nothing`, a manifest for it is generated by selecting the corresponding manifest value `Manifest.´T´`, which exists in the `Manifest` module. +1. If ´T´ is an instance of `Array[´S´]`, a manifest is generated with the invocation `´\mathit{Mobj}´.arrayType[S](m)`, where ´m´ is the manifest determined for ´M[S]´. +1. If ´T´ is some other class type ´S´#´C[U_1, ..., U_n]´ where the prefix type ´S´ cannot be statically determined from the class ´C´, a manifest is generated with the invocation `´\mathit{Mobj}´.classType[T](´m_0´, classOf[T], ´ms´)` where ´m_0´ is the manifest determined for ´M'[S]´ and ´ms´ are the manifests determined for ´M'[U_1], ..., M'[U_n]´. +1. If ´T´ is some other class type with type arguments ´U_1, ..., U_n´, a manifest is generated with the invocation `´\mathit{Mobj}´.classType[T](classOf[T], ´ms´)` where ´ms´ are the manifests determined for ´M'[U_1], ..., M'[U_n]´. +1. If ´T´ is a singleton type `´p´.type`, a manifest is generated with the invocation `´\mathit{Mobj}´.singleType[T](´p´)` 1. If ´T´ is a refined type ´T' \{ R \}´, a manifest is generated for ´T'´. - (That is, refinements are never reflected in manifests). -1. If ´T´ is an intersection type - `´T_1´ with ... with ´T_n´` - where ´n > 1´, the result depends on whether a full manifest is - to be determined or not. - If ´M´ is trait `Manifest`, then - a manifest is generated with the invocation - `Manifest.intersectionType[T](´ms´)` where ´ms´ are the manifests - determined for ´M[T_1], ..., M[T_n]´. - Otherwise, if ´M´ is trait `ClassManifest`, - then a manifest is generated for the [intersection dominator](03-types.html#type-erasure) - of the types ´T_1, ..., T_n´. -1. If ´T´ is some other type, then if ´M´ is trait `OptManifest`, - a manifest is generated from the designator `scala.reflect.NoManifest`. - If ´M´ is a type different from `OptManifest`, a static error results. +(That is, refinements are never reflected in manifests). +1. If ´T´ is an intersection type `´T_1´ with ... with ´T_n´` where ´n > 1´, the result depends on whether a full manifest is to be determined or not. +If ´M´ is trait `Manifest`, then a manifest is generated with the invocation `Manifest.intersectionType[T](´ms´)` where ´ms´ are the manifests determined for ´M[T_1], ..., M[T_n]´. +Otherwise, if ´M´ is trait `ClassManifest`, then a manifest is generated for the [intersection dominator](03-types.html#type-erasure) of the types ´T_1, ..., T_n´. +1. If ´T´ is some other type, then if ´M´ is trait `OptManifest`, a manifest is generated from the designator `scala.reflect.NoManifest`. +If ´M´ is a type different from `OptManifest`, a static error results. diff --git a/docs/_spec/08-pattern-matching.md b/docs/_spec/08-pattern-matching.md index f7203e831438..45d777f584a0 100644 --- a/docs/_spec/08-pattern-matching.md +++ b/docs/_spec/08-pattern-matching.md @@ -28,30 +28,20 @@ chapter: 8 Patterns ::= Pattern {‘,’ Patterns} ``` -A pattern is built from constants, constructors, variables and type -tests. Pattern matching tests whether a given value (or sequence of values) -has the shape defined by a pattern, and, if it does, binds the -variables in the pattern to the corresponding components of the value -(or sequence of values). The same variable name may not be bound more -than once in a pattern. +A pattern is built from constants, constructors, variables and type tests. +Pattern matching tests whether a given value (or sequence of values) has the shape defined by a pattern, and, if it does, binds the variables in the pattern to the corresponding components of the value (or sequence of values). +The same variable name may not be bound more than once in a pattern. ###### Example Some examples of patterns are: - 1. The pattern `ex: IOException` matches all instances of class - `IOException`, binding variable `ex` to the instance. - 1. The pattern `Some(x)` matches values of the form `Some(´v´)`, - binding `x` to the argument value ´v´ of the `Some` constructor. - 1. The pattern `(x, _)` matches pairs of values, binding `x` to - the first component of the pair. The second component is matched - with a wildcard pattern. - 1. The pattern `x :: y :: xs` matches lists of length ´\geq 2´, - binding `x` to the list's first element, `y` to the list's - second element, and `xs` to the remainder. + 1. The pattern `ex: IOException` matches all instances of class `IOException`, binding variable `ex` to the instance. + 1. The pattern `Some(x)` matches values of the form `Some(´v´)`, binding `x` to the argument value ´v´ of the `Some` constructor. + 1. The pattern `(x, _)` matches pairs of values, binding `x` to the first component of the pair. The second component is matched with a wildcard pattern. + 1. The pattern `x :: y :: xs` matches lists of length ´\geq 2´, binding `x` to the list's first element, `y` to the list's second element, and `xs` to the remainder. 1. The pattern `1 | 2 | 3` matches the integers between 1 and 3. -Pattern matching is always done in a context which supplies an -expected type of the pattern. We distinguish the following kinds of -patterns. +Pattern matching is always done in a context which supplies an expected type of the pattern. +We distinguish the following kinds of patterns. ### Variable Patterns @@ -60,11 +50,10 @@ patterns. | varid ``` -A _variable pattern_ ´x´ is a simple identifier which starts with a -lower case letter. It matches any value, and binds the variable name -to that value. The type of ´x´ is the expected type of the pattern as -given from outside. A special case is the wild-card pattern `_` -which is treated as if it was a fresh variable on each occurrence. +A _variable pattern_ ´x´ is a simple identifier which starts with a lower case letter. +It matches any value, and binds the variable name to that value. +The type of ´x´ is the expected type of the pattern as given from outside. +A special case is the wild-card pattern `_` which is treated as if it was a fresh variable on each occurrence. ### Typed Patterns @@ -73,12 +62,9 @@ which is treated as if it was a fresh variable on each occurrence. | ‘_’ ‘:’ TypePat ``` -A _typed pattern_ ´x: T´ consists of a pattern variable ´x´ and a -type pattern ´T´. The type of ´x´ is the type pattern ´T´, where -each type variable and wildcard is replaced by a fresh, unknown type. -This pattern matches any value matched by the [type pattern](#type-patterns) -´T´; it binds the variable name to -that value. +A _typed pattern_ ´x: T´ consists of a pattern variable ´x´ and a type pattern ´T´. +The type of ´x´ is the type pattern ´T´, where each type variable and wildcard is replaced by a fresh, unknown type. +This pattern matches any value matched by the [type pattern](#type-patterns) ´T´; it binds the variable name to that value. ### Pattern Binders @@ -86,11 +72,9 @@ that value. Pattern2 ::= varid ‘@’ Pattern3 ``` -A _pattern binder_ `´x´@´p´` consists of a pattern variable ´x´ and a -pattern ´p´. The type of the variable ´x´ is the static type ´T´ implied -by the pattern ´p´. -This pattern matches any value ´v´ matched by the pattern ´p´, -and it binds the variable name to that value. +A _pattern binder_ `´x´@´p´` consists of a pattern variable ´x´ and a pattern ´p´. +The type of the variable ´x´ is the static type ´T´ implied by the pattern ´p´. +This pattern matches any value ´v´ matched by the pattern ´p´, and it binds the variable name to that value. A pattern ´p´ _implies_ a type ´T´ if the pattern matches only values of the type ´T´. @@ -100,9 +84,8 @@ A pattern ´p´ _implies_ a type ´T´ if the pattern matches only values of the SimplePattern ::= Literal ``` -A _literal pattern_ ´L´ matches any value that is equal (in terms of -`==`) to the literal ´L´. The type of ´L´ must conform to the -expected type of the pattern. +A _literal pattern_ ´L´ matches any value that is equal (in terms of `==`) to the literal ´L´. +The type of ´L´ must conform to the expected type of the pattern. ### Interpolated string patterns @@ -110,9 +93,8 @@ expected type of the pattern. Literal ::= interpolatedString ``` -The expansion of interpolated string literals in patterns is the same as -in expressions. If it occurs in a pattern, a interpolated string literal -of either of the forms +The expansion of interpolated string literals in patterns is the same as in expressions. +If it occurs in a pattern, a interpolated string literal of either of the forms ``` id"text0{ pat1 }text1 ... { patn }textn" id"""text0{ pat1 }text1 ... { patn }textn""" @@ -121,13 +103,10 @@ is equivalent to: ``` StringContext("""text0""", ..., """textn""").id(pat1, ..., patn) ``` -You could define your own `StringContext` to shadow the default one that's -in the `scala` package. +You could define your own `StringContext` to shadow the default one that's in the `scala` package. -This expansion is well-typed if the member `id` evaluates to an extractor -object. If the extractor object has `apply` as well as `unapply` or -`unapplySeq` methods, processed strings can be used as either expressions -or patterns. +This expansion is well-typed if the member `id` evaluates to an extractor object. +If the extractor object has `apply` as well as `unapply` or `unapplySeq` methods, processed strings can be used as either expressions or patterns. Taking XML as an example ```scala @@ -159,14 +138,11 @@ where linktext is a variable bound by the pattern. ``` A _stable identifier pattern_ is a [stable identifier](03-types.html#paths) ´r´. -The type of ´r´ must conform to the expected -type of the pattern. The pattern matches any value ´v´ such that -`´r´ == ´v´` (see [here](12-the-scala-standard-library.html#root-classes)). +The type of ´r´ must conform to the expected type of the pattern. +The pattern matches any value ´v´ such that `´r´ == ´v´` (see [here](12-the-scala-standard-library.html#root-classes)). -To resolve the syntactic overlap with a variable pattern, a -stable identifier pattern may not be a simple name starting with a lower-case -letter. However, it is possible to enclose such a variable name in -backquotes; then it is treated as a stable identifier pattern. +To resolve the syntactic overlap with a variable pattern, a stable identifier pattern may not be a simple name starting with a lower-case letter. +However, it is possible to enclose such a variable name in backquotes; then it is treated as a stable identifier pattern. ###### Example Consider the following class definition: @@ -185,8 +161,7 @@ class C { c => } ``` -Here, the first three patterns are stable identifier patterns, while the last -one is a variable pattern. +Here, the first three patterns are stable identifier patterns, while the last one is a variable pattern. ### Constructor Patterns @@ -194,25 +169,15 @@ one is a variable pattern. SimplePattern ::= StableId ‘(’ [Patterns] ‘)’ ``` -A _constructor pattern_ is of the form ´c(p_1, ..., p_n)´ where ´n -\geq 0´. It consists of a stable identifier ´c´, followed by element -patterns ´p_1, ..., p_n´. The constructor ´c´ is a simple or -qualified name which denotes a [case class](05-classes-and-objects.html#case-classes). -If the case class is monomorphic, then it -must conform to the expected type of the pattern, and the formal -parameter types of ´x´'s [primary constructor](05-classes-and-objects.html#class-definitions) -are taken as the expected types of the element patterns ´p_1, ..., -p_n´. If the case class is polymorphic, then its type parameters are -instantiated so that the instantiation of ´c´ conforms to the expected -type of the pattern. The instantiated formal parameter types of ´c´'s -primary constructor are then taken as the expected types of the -component patterns ´p_1, ..., p_n´. The pattern matches all -objects created from constructor invocations ´c(v_1, ..., v_n)´ -where each element pattern ´p_i´ matches the corresponding value -´v_i´. - -A special case arises when ´c´'s formal parameter types end in a -repeated parameter. This is further discussed [here](#pattern-sequences). +A _constructor pattern_ is of the form ´c(p_1, ..., p_n)´ where ´n \geq 0´. It consists of a stable identifier ´c´, followed by element patterns ´p_1, ..., p_n´. +The constructor ´c´ is a simple or qualified name which denotes a [case class](05-classes-and-objects.html#case-classes). +If the case class is monomorphic, then it must conform to the expected type of the pattern, and the formal parameter types of ´x´'s [primary constructor](05-classes-and-objects.html#class-definitions) are taken as the expected types of the element patterns ´p_1, ..., p_n´. +If the case class is polymorphic, then its type parameters are instantiated so that the instantiation of ´c´ conforms to the expected type of the pattern. +The instantiated formal parameter types of ´c´'s primary constructor are then taken as the expected types of the component patterns ´p_1, ..., p_n´. +The pattern matches all objects created from constructor invocations ´c(v_1, ..., v_n)´ where each element pattern ´p_i´ matches the corresponding value ´v_i´. + +A special case arises when ´c´'s formal parameter types end in a repeated parameter. +This is further discussed [here](#pattern-sequences). ### Tuple Patterns @@ -220,10 +185,7 @@ repeated parameter. This is further discussed [here](#pattern-sequences). SimplePattern ::= ‘(’ [Patterns] ‘)’ ``` -A _tuple pattern_ `(´p_1´, ..., ´p_n´)` is an alias -for the constructor pattern `scala.Tuple´n´(´p_1´, ..., ´p_n´)`, -where ´n \geq 2´. The empty tuple -`()` is the unique value of type `scala.Unit`. +A _tuple pattern_ `(´p_1´, ..., ´p_n´)` is an alias for the constructor pattern `scala.Tuple´n´(´p_1´, ..., ´p_n´)`, where ´n \geq 2´. The empty tuple `()` is the unique value of type `scala.Unit`. ### Extractor Patterns @@ -231,44 +193,26 @@ where ´n \geq 2´. The empty tuple SimplePattern ::= StableId ‘(’ [Patterns] ‘)’ ``` -An _extractor pattern_ ´x(p_1, ..., p_n)´ where ´n \geq 0´ is of -the same syntactic form as a constructor pattern. However, instead of -a case class, the stable identifier ´x´ denotes an object which has a -member method named `unapply` or `unapplySeq` that matches -the pattern. - -An extractor pattern cannot match the value `null`. The implementation -ensures that the `unapply`/`unapplySeq` method is not applied to `null`. - -A type is said to be an _extractor type_ for some type `T` if it has a -method `get` with return type `T`, and a method `isEmpty` with a return type -that conforms to `Boolean`. `Option[T]` is an extractor type for type `T`. - -An `unapply` method in an object ´x´ _matches_ the pattern -´x(p_1, ..., p_n)´ if it has a single parameter (and, optionally, an -implicit parameter list) and one of the following applies: - -* ´n=0´ and `unapply`'s result type conforms to `Boolean`. In this case - the extractor pattern matches all values ´v´ for which - `´x´.unapply(´v´)` yields `true`. -* ´n=1´ and `unapply`'s result type is an extractor type for some - type ´T´. In this case, the (only) argument pattern ´p_1´ is typed in - turn with expected type ´T´. The extractor pattern matches then all - values ´v´ for which `´x´.unapply(´v´)` yields a value ´u´ for which `´u´.isEmpty` yields - `false`, `´u´.get` yields a value ´v_1´, and ´p_1´ matches ´v_1´. -* ´n>1´ and `unapply`'s result type is - an extractor type for some type ´T´ with members ´\_1, ..., \_n´ returning - types ´T_1, ..., T_n´. In this case, the argument patterns ´p_1 - , ..., p_n´ are typed in turn with expected types ´T_1 , ..., - T_n´. The extractor pattern matches then all values ´v´ for which - `´x´.unapply(´v´)` yields a value ´u´ for which - `´u´.isEmpty` yields `false`, `´u´.get` yields some value ´t´, and each pattern - ´p_i´ matches the corresponding value ´t._1´ from - ´t._1, ..., t._n´. - -An `unapplySeq` method in an object ´x´ matches the pattern -´x(q_1, ..., q_m, p_1, ..., p_n)´ if it takes exactly one argument -and its result type is of the form `Option[(´T_1, ..., T_m´, Seq[S])]` (if `m = 0`, the type `Option[Seq[S]]` is also accepted). +An _extractor pattern_ ´x(p_1, ..., p_n)´ where ´n \geq 0´ is of the same syntactic form as a constructor pattern. +However, instead of a case class, the stable identifier ´x´ denotes an object which has a member method named `unapply` or `unapplySeq` that matches the pattern. + +An extractor pattern cannot match the value `null`. The implementation ensures that the `unapply`/`unapplySeq` method is not applied to `null`. + +A type is said to be an _extractor type_ for some type `T` if it has a method `get` with return type `T`, and a method `isEmpty` with a return type that conforms to `Boolean`. +`Option[T]` is an extractor type for type `T`. + +An `unapply` method in an object ´x´ _matches_ the pattern ´x(p_1, ..., p_n)´ if it has a single parameter (and, optionally, an implicit parameter list) and one of the following applies: + +* ´n=0´ and `unapply`'s result type conforms to `Boolean`. +In this case the extractor pattern matches all values ´v´ for which `´x´.unapply(´v´)` yields `true`. +* ´n=1´ and `unapply`'s result type is an extractor type for some type ´T´. +In this case, the (only) argument pattern ´p_1´ is typed in turn with expected type ´T´. +The extractor pattern matches then all values ´v´ for which `´x´.unapply(´v´)` yields a value ´u´ for which `´u´.isEmpty` yields `false`, `´u´.get` yields a value ´v_1´, and ´p_1´ matches ´v_1´. +* ´n>1´ and `unapply`'s result type is an extractor type for some type ´T´ with members ´\_1, ..., \_n´ returning types ´T_1, ..., T_n´. +In this case, the argument patterns ´p_1, ..., p_n´ are typed in turn with expected types ´T_1 , ..., T_n´. +The extractor pattern matches then all values ´v´ for which `´x´.unapply(´v´)` yields a value ´u´ for which `´u´.isEmpty` yields `false`, `´u´.get` yields some value ´t´, and each pattern ´p_i´ matches the corresponding value ´t._1´ from ´t._1, ..., t._n´. + +An `unapplySeq` method in an object ´x´ matches the pattern ´x(q_1, ..., q_m, p_1, ..., p_n)´ if it takes exactly one argument and its result type is of the form `Option[(´T_1, ..., T_m´, Seq[S])]` (if `m = 0`, the type `Option[Seq[S]]` is also accepted). This case is further discussed [below](#pattern-sequences). ###### Example 1 @@ -282,8 +226,7 @@ object Pair { } ``` -This means that the name `Pair` can be used in place of `Tuple2` for tuple -formation as well as for deconstruction of tuples in patterns. +This means that the name `Pair` can be used in place of `Tuple2` for tuple formation as well as for deconstruction of tuples in patterns. Hence, the following is possible: ```scala @@ -306,12 +249,9 @@ class NameBased[A, B](a: A, b: B) { } ``` -Then `NameBased` is an extractor type for `NameBased` itself, since it has a -member `isEmpty` returning a value of type Boolean, and it has a member `get` -returning a value of type `NameBased`. +Then `NameBased` is an extractor type for `NameBased` itself, since it has a member `isEmpty` returning a value of type Boolean, and it has a member `get` returning a value of type `NameBased`. -Since it also has members `_1` and `_2`, it can be used in an extractor pattern -with n = 2 as follows: +Since it also has members `_1` and `_2`, it can be used in an extractor pattern with n = 2 as follows: ```scala object Extractor { @@ -332,18 +272,12 @@ SimplePattern ::= StableId ‘(’ [Patterns ‘,’] [varid ‘@’] ‘_’ A _pattern sequence_ ´p_1, ..., p_n´ appears in two contexts. First, in a constructor pattern ´c(q_1, ..., q_m, p_1, ..., p_n)´, where ´c´ is a case class which has ´m+1´ primary constructor parameters, ending in a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters) of type `S*`. -Second, in an extractor pattern ´x(q_1, ..., q_m, p_1, ..., p_n)´ if the extractor object ´x´ does not have an `unapply` method, -but it does define an `unapplySeq` method with a result type that is an extractor type for type `(T_1, ... , T_m, Seq[S])` (if `m = 0`, an extractor type for the type `Seq[S]` is also accepted). The expected type for the patterns ´p_i´ is ´S´. +Second, in an extractor pattern ´x(q_1, ..., q_m, p_1, ..., p_n)´ if the extractor object ´x´ does not have an `unapply` method, but it does define an `unapplySeq` method with a result type that is an extractor type for type `(T_1, ... , T_m, Seq[S])` (if `m = 0`, an extractor type for the type `Seq[S]` is also accepted). The expected type for the patterns ´p_i´ is ´S´. The last pattern in a pattern sequence may be a _sequence wildcard_ `_*`. -Each element pattern ´p_i´ is type-checked with -´S´ as expected type, unless it is a sequence wildcard. If a final -sequence wildcard is present, the pattern matches all values ´v´ that -are sequences which start with elements matching patterns -´p_1, ..., p_{n-1}´. If no final sequence wildcard is given, the -pattern matches all values ´v´ that are sequences of -length ´n´ which consist of elements matching patterns ´p_1 , ..., -p_n´. +Each element pattern ´p_i´ is type-checked with ´S´ as expected type, unless it is a sequence wildcard. +If a final sequence wildcard is present, the pattern matches all values ´v´ that are sequences which start with elements matching patterns ´p_1, ..., p_{n-1}´. +If no final sequence wildcard is given, the pattern matches all values ´v´ that are sequences of length ´n´ which consist of elements matching patterns ´p_1, ..., p_n´. ### Infix Operation Patterns @@ -352,13 +286,10 @@ p_n´. ``` An _infix operation pattern_ ´p;\mathit{op};q´ is a shorthand for the -constructor or extractor pattern ´\mathit{op}(p, q)´. The precedence and -associativity of operators in patterns is the same as in -[expressions](06-expressions.html#prefix,-infix,-and-postfix-operations). +constructor or extractor pattern ´\mathit{op}(p, q)´. +The precedence and associativity of operators in patterns is the same as in [expressions](06-expressions.html#prefix,-infix,-and-postfix-operations). -An infix operation pattern ´p;\mathit{op};(q_1, ..., q_n)´ is a -shorthand for the constructor or extractor pattern ´\mathit{op}(p, q_1 -, ..., q_n)´. +An infix operation pattern ´p;\mathit{op};(q_1, ..., q_n)´ is a shorthand for the constructor or extractor pattern ´\mathit{op}(p, q_1, ..., q_n)´. ### Pattern Alternatives @@ -366,11 +297,10 @@ shorthand for the constructor or extractor pattern ´\mathit{op}(p, q_1 Pattern ::= Pattern1 { ‘|’ Pattern1 } ``` -A _pattern alternative_ `´p_1´ | ... | ´p_n´` -consists of a number of alternative patterns ´p_i´. All alternative -patterns are type checked with the expected type of the pattern. They -may not bind variables other than wildcards. The alternative pattern -matches a value ´v´ if at least one its alternatives matches ´v´. +A _pattern alternative_ `´p_1´ | ... | ´p_n´` consists of a number of alternative patterns ´p_i´. +All alternative patterns are type checked with the expected type of the pattern. +They may not bind variables other than wildcards. +The alternative pattern matches a value ´v´ if at least one its alternatives matches ´v´. ### XML Patterns @@ -380,17 +310,10 @@ XML patterns are treated [here](10-xml-expressions-and-patterns.html#xml-pattern Regular expression patterns have been discontinued in Scala from version 2.0. -Later version of Scala provide a much simplified version of regular -expression patterns that cover most scenarios of non-text sequence -processing. A _sequence pattern_ is a pattern that stands in a -position where either (1) a pattern of a type `T` which is -conforming to -`Seq[A]` for some `A` is expected, or (2) a case -class constructor that has an iterated formal parameter -`A*`. A wildcard star pattern `_*` in the -rightmost position stands for arbitrary long sequences. It can be -bound to variables using `@`, as usual, in which case the variable will have the -type `Seq[A]`. +Later version of Scala provide a much simplified version of regular expression patterns that cover most scenarios of non-text sequence processing. +A _sequence pattern_ is a pattern that stands in a position where either (1) a pattern of a type `T` which is conforming to `Seq[A]` for some `A` is expected, or (2) a case class constructor that has an iterated formal parameter `A*`. +A wildcard star pattern `_*` in the rightmost position stands for arbitrary long sequences. +It can be bound to variables using `@`, as usual, in which case the variable will have the type `Seq[A]`. ### Irrefutable Patterns @@ -398,16 +321,10 @@ A pattern ´p´ is _irrefutable_ for a type ´T´, if one of the following appli 1. ´p´ is a variable pattern, 1. ´p´ is a typed pattern ´x: T'´, and ´T <: T'´, -1. ´p´ is a constructor pattern ´c(p_1, ..., p_n)´, the type ´T´ - is an instance of class ´c´, the [primary constructor](05-classes-and-objects.html#class-definitions) - of type ´T´ has argument types ´T_1, ..., T_n´, and each ´p_i´ is - irrefutable for ´T_i´. -1. ´p´ is an extractor pattern for which the extractor type is `Some[´T´]` for - some type ´T´ -1. ´p´ is an extractor pattern for which the extractor types `isEmpty` method - is the singleton type `false` -1. ´p´ is an extractor pattern for which the return type is the singleton type - `true` +1. ´p´ is a constructor pattern ´c(p_1, ..., p_n)´, the type ´T´ is an instance of class ´c´, the [primary constructor](05-classes-and-objects.html#class-definitions) of type ´T´ has argument types ´T_1, ..., T_n´, and each ´p_i´ is irrefutable for ´T_i´. +1. ´p´ is an extractor pattern for which the extractor type is `Some[´T´]` for some type ´T´ +1. ´p´ is an extractor pattern for which the extractor types `isEmpty` method is the singleton type `false` +1. ´p´ is an extractor pattern for which the return type is the singleton type `true` ## Type Patterns @@ -418,70 +335,50 @@ A pattern ´p´ is _irrefutable_ for a type ´T´, if one of the following appli Type patterns consist of types, type variables, and wildcards. A type pattern ´T´ is of one of the following forms: -* A reference to a class ´C´, ´p.C´, or `´T´#´C´`. This - type pattern matches any non-null instance of the given class. - Note that the prefix of the class, if it exists, is relevant for determining - class instances. For instance, the pattern ´p.C´ matches only - instances of classes ´C´ which were created with the path ´p´ as - prefix. This also applies to prefixes which are not given syntactically. - For example, if ´C´ refers to a class defined in the nearest enclosing - class and is thus equivalent to ´this.C´, it is considered to have a prefix. - - The bottom types `scala.Nothing` and `scala.Null` cannot - be used as type patterns, because they would match nothing in any case. - -* A singleton type `´p´.type`. This type pattern matches only the value - denoted by the path ´p´ (the `eq` method is used to compare the matched value - to ´p´). - -* A literal type `´lit´`. This type pattern matches only the value - denoted by the literal ´lit´ (the `==` method is used to compare the matched - value to ´lit´). - -* A compound type pattern `´T_1´ with ... with ´T_n´` where each ´T_i´ is a - type pattern. This type pattern matches all values that are matched by each of - the type patterns ´T_i´. - -* A parameterized type pattern ´T[a_1, ..., a_n]´, where the ´a_i´ - are type variable patterns or wildcards `_`. - This type pattern matches all values which match ´T´ for - some arbitrary instantiation of the type variables and wildcards. The - bounds or alias type of these type variable are determined as - described [here](#type-parameter-inference-in-patterns). - -* A parameterized type pattern `scala.Array´[T_1]´`, where - ´T_1´ is a type pattern. This type pattern matches any non-null instance - of type `scala.Array´[U_1]´`, where ´U_1´ is a type matched by ´T_1´. - -Types which are not of one of the forms described above are also -accepted as type patterns. However, such type patterns will be translated to their -[erasure](03-types.html#type-erasure). The Scala -compiler will issue an "unchecked" warning for these patterns to -flag the possible loss of type-safety. - -A _type variable pattern_ is a simple identifier which starts with -a lower case letter. +* A reference to a class ´C´, ´p.C´, or `´T´#´C´`. +This type pattern matches any non-null instance of the given class. +Note that the prefix of the class, if it exists, is relevant for determining class instances. +For instance, the pattern ´p.C´ matches only instances of classes ´C´ which were created with the path ´p´ as prefix. +This also applies to prefixes which are not given syntactically. +For example, if ´C´ refers to a class defined in the nearest enclosing class and is thus equivalent to ´this.C´, it is considered to have a prefix. + +The bottom types `scala.Nothing` and `scala.Null` cannot be used as type patterns, because they would match nothing in any case. + +* A singleton type `´p´.type`. This type pattern matches only the value denoted by the path ´p´ (the `eq` method is used to compare the matched value to ´p´). + +* A literal type `´lit´`. This type pattern matches only the value denoted by the literal ´lit´ (the `==` method is used to compare the matched value to ´lit´). + +* A compound type pattern `´T_1´ with ... with ´T_n´` where each ´T_i´ is a type pattern. +This type pattern matches all values that are matched by each of the type patterns ´T_i´. + +* A parameterized type pattern ´T[a_1, ..., a_n]´, where the ´a_i´ are type variable patterns or wildcards `_`. +This type pattern matches all values which match ´T´ for some arbitrary instantiation of the type variables and wildcards. +The bounds or alias type of these type variable are determined as described [here](#type-parameter-inference-in-patterns). + +* A parameterized type pattern `scala.Array´[T_1]´`, where ´T_1´ is a type pattern. +This type pattern matches any non-null instance of type `scala.Array´[U_1]´`, where ´U_1´ is a type matched by ´T_1´. + +Types which are not of one of the forms described above are also accepted as type patterns. +However, such type patterns will be translated to their [erasure](03-types.html#type-erasure). +The Scala compiler will issue an "unchecked" warning for these patterns to flag the possible loss of type-safety. + +A _type variable pattern_ is a simple identifier which starts with a lower case letter. ## Type Parameter Inference in Patterns -Type parameter inference is the process of finding bounds for the -bound type variables in a typed pattern or constructor -pattern. Inference takes into account the expected type of the -pattern. +Type parameter inference is the process of finding bounds for the bound type variables in a typed pattern or constructor pattern. +Inference takes into account the expected type of the pattern. ### Type parameter inference for typed patterns -Assume a typed pattern ´p: T'´. Let ´T´ result from ´T'´ where all wildcards in -´T'´ are renamed to fresh variable names. Let ´a_1, ..., a_n´ be -the type variables in ´T´. These type variables are considered bound -in the pattern. Let the expected type of the pattern be ´\mathit{pt}´. +Assume a typed pattern ´p: T'´. Let ´T´ result from ´T'´ where all wildcards in ´T'´ are renamed to fresh variable names. +Let ´a_1, ..., a_n´ be the type variables in ´T´. +These type variables are considered bound in the pattern. +Let the expected type of the pattern be ´\mathit{pt}´. -Type parameter inference constructs first a set of subtype constraints over -the type variables ´a_i´. The initial constraints set ´\mathcal{C}\_0´ reflects -just the bounds of these type variables. That is, assuming ´T´ has -bound type variables ´a_1, ..., a_n´ which correspond to class -type parameters ´a_1', ..., a_n'´ with lower bounds ´L_1, ..., L_n´ -and upper bounds ´U_1, ..., U_n´, ´\mathcal{C}_0´ contains the constraints +Type parameter inference constructs first a set of subtype constraints over the type variables ´a_i´. +The initial constraints set ´\mathcal{C}\_0´ reflects just the bounds of these type variables. +That is, assuming ´T´ has bound type variables ´a_1, ..., a_n´ which correspond to class type parameters ´a_1', ..., a_n'´ with lower bounds ´L_1, ..., L_n´ and upper bounds ´U_1, ..., U_n´, ´\mathcal{C}_0´ contains the constraints $$ \begin{cases} @@ -492,54 +389,35 @@ $$ where ´\sigma´ is the substitution ´[a_1' := a_1, ..., a_n' :=a_n]´. -The set ´\mathcal{C}_0´ is then augmented by further subtype constraints. There are two -cases. +The set ´\mathcal{C}_0´ is then augmented by further subtype constraints. +There are two cases. ###### Case 1 -If there exists a substitution ´\sigma´ over the type variables ´a_i, ..., a_n´ such that ´\sigma T´ conforms to ´\mathit{pt}´, one determines the weakest subtype constraints -´\mathcal{C}\_1´ over the type variables ´a_1, ..., a_n´ such that ´\mathcal{C}\_0 \wedge \mathcal{C}_1´ implies that ´T´ conforms to ´\mathit{pt}´. +If there exists a substitution ´\sigma´ over the type variables ´a_i, ..., a_n´ such that ´\sigma T´ conforms to ´\mathit{pt}´, one determines the weakest subtype constraints ´\mathcal{C}\_1´ over the type variables ´a_1, ..., a_n´ such that ´\mathcal{C}\_0 \wedge \mathcal{C}_1´ implies that ´T´ conforms to ´\mathit{pt}´. ###### Case 2 -Otherwise, if ´T´ can not be made to conform to ´\mathit{pt}´ by -instantiating its type variables, one determines all type variables in -´\mathit{pt}´ which are defined as type parameters of a method enclosing -the pattern. Let the set of such type parameters be ´b_1 , ..., -b_m´. Let ´\mathcal{C}\_0'´ be the subtype constraints reflecting the bounds of the -type variables ´b_i´. If ´T´ denotes an instance type of a final -class, let ´\mathcal{C}\_2´ be the weakest set of subtype constraints over the type -variables ´a_1, ..., a_n´ and ´b_1, ..., b_m´ such that -´\mathcal{C}\_0 \wedge \mathcal{C}\_0' \wedge \mathcal{C}\_2´ implies that ´T´ conforms to -´\mathit{pt}´. If ´T´ does not denote an instance type of a final class, -let ´\mathcal{C}\_2´ be the weakest set of subtype constraints over the type variables -´a_1, ..., a_n´ and ´b_1, ..., b_m´ such that ´\mathcal{C}\_0 \wedge -\mathcal{C}\_0' \wedge \mathcal{C}\_2´ implies that it is possible to construct a type -´T'´ which conforms to both ´T´ and ´\mathit{pt}´. It is a static error if -there is no satisfiable set of constraints ´\mathcal{C}\_2´ with this property. - -The final step consists in choosing type bounds for the type -variables which imply the established constraint system. The process -is different for the two cases above. +Otherwise, if ´T´ can not be made to conform to ´\mathit{pt}´ by instantiating its type variables, one determines all type variables in ´\mathit{pt}´ which are defined as type parameters of a method enclosing the pattern. +Let the set of such type parameters be ´b_1 , ..., b_m´. +Let ´\mathcal{C}\_0'´ be the subtype constraints reflecting the bounds of the type variables ´b_i´. +If ´T´ denotes an instance type of a final class, let ´\mathcal{C}\_2´ be the weakest set of subtype constraints over the type variables ´a_1, ..., a_n´ and ´b_1, ..., b_m´ such that ´\mathcal{C}\_0 \wedge \mathcal{C}\_0' \wedge \mathcal{C}\_2´ implies that ´T´ conforms to ´\mathit{pt}´. +If ´T´ does not denote an instance type of a final class, let ´\mathcal{C}\_2´ be the weakest set of subtype constraints over the type variables ´a_1, ..., a_n´ and ´b_1, ..., b_m´ such that ´\mathcal{C}\_0 \wedge \mathcal{C}\_0' \wedge \mathcal{C}\_2´ implies that it is possible to construct a type ´T'´ which conforms to both ´T´ and ´\mathit{pt}´. +It is a static error if there is no satisfiable set of constraints ´\mathcal{C}\_2´ with this property. + +The final step consists in choosing type bounds for the type variables which imply the established constraint system. +The process is different for the two cases above. ###### Case 1 We take ´a_i >: L_i <: U_i´ where each ´L_i´ is minimal and each ´U_i´ is maximal wrt ´<:´ such that ´a_i >: L_i <: U_i´ for ´i = 1, ..., n´ implies ´\mathcal{C}\_0 \wedge \mathcal{C}\_1´. ###### Case 2 -We take ´a_i >: L_i <: U_i´ and ´b\_i >: L_i' <: U_i' ´ where each ´L_i´ -and ´L_j'´ is minimal and each ´U_i´ and ´U_j'´ is maximal such that -´a_i >: L_i <: U_i´ for ´i = 1, ..., n´ and -´b_j >: L_j' <: U_j'´ for ´j = 1, ..., m´ -implies ´\mathcal{C}\_0 \wedge \mathcal{C}\_0' \wedge \mathcal{C}_2´. +We take ´a_i >: L_i <: U_i´ and ´b\_i >: L_i' <: U_i' ´ where each ´L_i´ and ´L_j'´ is minimal and each ´U_i´ and ´U_j'´ is maximal such that ´a_i >: L_i <: U_i´ for ´i = 1, ..., n´ and ´b_j >: L_j' <: U_j'´ for ´j = 1, ..., m´ implies ´\mathcal{C}\_0 \wedge \mathcal{C}\_0' \wedge \mathcal{C}_2´. -In both cases, local type inference is permitted to limit the -complexity of inferred bounds. Minimality and maximality of types have -to be understood relative to the set of types of acceptable -complexity. +In both cases, local type inference is permitted to limit the complexity of inferred bounds. +Minimality and maximality of types have to be understood relative to the set of types of acceptable complexity. ### Type parameter inference for constructor patterns -Assume a constructor pattern ´C(p_1, ..., p_n)´ where class ´C´ -has type parameters ´a_1, ..., a_n´. These type parameters -are inferred in the same way as for the typed pattern -`(_: ´C[a_1, ..., a_n]´)`. +Assume a constructor pattern ´C(p_1, ..., p_n)´ where class ´C´ has type parameters ´a_1, ..., a_n´. +These type parameters are inferred in the same way as for the typed pattern `(_: ´C[a_1, ..., a_n]´)`. ###### Example Consider the program fragment: @@ -551,12 +429,11 @@ x match { } ``` -Here, the type pattern `List[a]` is matched against the -expected type `Any`. The pattern binds the type variable -`a`. Since `List[a]` conforms to `Any` -for every type argument, there are no constraints on `a`. -Hence, `a` is introduced as an abstract type with no -bounds. The scope of `a` is right-hand side of its case clause. +Here, the type pattern `List[a]` is matched against the expected type `Any`. +The pattern binds the type variable `a`. +Since `List[a]` conforms to `Any` for every type argument, there are no constraints on `a`. +Hence, `a` is introduced as an abstract type with no bounds. +The scope of `a` is right-hand side of its case clause. On the other hand, if `x` is declared as @@ -564,11 +441,8 @@ On the other hand, if `x` is declared as val x: List[List[String]], ``` -this generates the constraint -`List[a] <: List[List[String]]`, which simplifies to -`a <: List[String]`, because `List` is covariant. Hence, -`a` is introduced with upper bound -`List[String]`. +this generates the constraint `List[a] <: List[List[String]]`, which simplifies to `a <: List[String]`, because `List` is covariant. +Hence, `a` is introduced with upper bound `List[String]`. ###### Example Consider the program fragment: @@ -580,16 +454,10 @@ x match { } ``` -Scala does not maintain information about type arguments at run-time, -so there is no way to check that `x` is a list of strings. -Instead, the Scala compiler will [erase](03-types.html#type-erasure) the -pattern to `List[_]`; that is, it will only test whether the -top-level runtime-class of the value `x` conforms to -`List`, and the pattern match will succeed if it does. This -might lead to a class cast exception later on, in the case where the -list `x` contains elements other than strings. The Scala -compiler will flag this potential loss of type-safety with an -"unchecked" warning message. +Scala does not maintain information about type arguments at run-time, so there is no way to check that `x` is a list of strings. +Instead, the Scala compiler will [erase](03-types.html#type-erasure) the pattern to `List[_]`; that is, it will only test whether the top-level runtime-class of the value `x` conforms to `List`, and the pattern match will succeed if it does. +This might lead to a class cast exception later on, in the case where the list `x` contains elements other than strings. +The Scala compiler will flag this potential loss of type-safety with an "unchecked" warning message. ###### Example Consider the program fragment @@ -602,17 +470,11 @@ def f[B](t: Term[B]): B = t match { } ``` -The expected type of the pattern `y: Number` is -`Term[B]`. The type `Number` does not conform to -`Term[B]`; hence Case 2 of the rules above -applies. This means that `B` is treated as another type -variable for which subtype constraints are inferred. In our case the -applicable constraint is `Number <: Term[B]`, which -entails `B = Int`. Hence, `B` is treated in -the case clause as an abstract type with lower and upper bound -`Int`. Therefore, the right hand side of the case clause, -`y.n`, of type `Int`, is found to conform to the -function's declared result type, `Number`. +The expected type of the pattern `y: Number` is `Term[B]`. +The type `Number` does not conform to `Term[B]`; hence Case 2 of the rules above applies. +This means that `B` is treated as another type variable for which subtype constraints are inferred. +In our case the applicable constraint is `Number <: Term[B]`, which entails `B = Int`. Hence, `B` is treated in the case clause as an abstract type with lower and upper bound `Int`. +Therefore, the right hand side of the case clause, `y.n`, of type `Int`, is found to conform to the function's declared result type, `Number`. ## Pattern Matching Expressions @@ -628,70 +490,46 @@ A _pattern matching expression_ e match { case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ } ``` -consists of a selector expression ´e´ and a number ´n > 0´ of -cases. Each case consists of a (possibly guarded) pattern ´p_i´ and a -block ´b_i´. Each ´p_i´ might be complemented by a guard -`if ´e´` where ´e´ is a boolean expression. -The scope of the pattern -variables in ´p_i´ comprises the pattern's guard and the corresponding block ´b_i´. - -Let ´T´ be the type of the selector expression ´e´ and let ´a_1 -, ..., a_m´ be the type parameters of all methods enclosing -the pattern matching expression. For every ´a_i´, let ´L_i´ be its -lower bound and ´U_i´ be its higher bound. Every pattern ´p \in \{p_1,, ..., p_n\}´ -can be typed in two ways. First, it is attempted -to type ´p´ with ´T´ as its expected type. If this fails, ´p´ is -instead typed with a modified expected type ´T'´ which results from -´T´ by replacing every occurrence of a type parameter ´a_i´ by -*undefined*. If this second step fails also, a compile-time -error results. If the second step succeeds, let ´T_p´ be the type of -pattern ´p´ seen as an expression. One then determines minimal bounds -´L_11, ..., L_m'´ and maximal bounds ´U_1', ..., U_m'´ such -that for all ´i´, ´L_i <: L_i'´ and ´U_i' <: U_i´ and the following -constraint system is satisfied: +consists of a selector expression ´e´ and a number ´n > 0´ of cases. +Each case consists of a (possibly guarded) pattern ´p_i´ and a block ´b_i´. +Each ´p_i´ might be complemented by a guard `if ´e´` where ´e´ is a boolean expression. +The scope of the pattern variables in ´p_i´ comprises the pattern's guard and the corresponding block ´b_i´. + +Let ´T´ be the type of the selector expression ´e´ and let ´a_1, ..., a_m´ be the type parameters of all methods enclosing the pattern matching expression. +For every ´a_i´, let ´L_i´ be its lower bound and ´U_i´ be its higher bound. +Every pattern ´p \in \{p_1,, ..., p_n\}´ can be typed in two ways. +First, it is attempted to type ´p´ with ´T´ as its expected type. +If this fails, ´p´ is instead typed with a modified expected type ´T'´ which results from ´T´ by replacing every occurrence of a type parameter ´a_i´ by +*undefined*. +If this second step fails also, a compile-time error results. +If the second step succeeds, let ´T_p´ be the type of pattern ´p´ seen as an expression. +One then determines minimal bounds ´L_11, ..., L_m'´ and maximal bounds ´U_1', ..., U_m'´ such that for all ´i´, ´L_i <: L_i'´ and ´U_i' <: U_i´ and the following constraint system is satisfied: $$ L_1 <: a_1 <: U_1\;\wedge\;...\;\wedge\;L_m <: a_m <: U_m \ \Rightarrow\ T_p <: T $$ -If no such bounds can be found, a compile time error results. If such -bounds are found, the pattern matching clause starting with ´p´ is -then typed under the assumption that each ´a_i´ has lower bound ´L_i'´ -instead of ´L_i´ and has upper bound ´U_i'´ instead of ´U_i´. - -The expected type of every block ´b_i´ is the expected type of the -whole pattern matching expression. The type of the pattern matching -expression is then the [weak least upper bound](03-types.html#weak-conformance) -of the types of all blocks -´b_i´. - -When applying a pattern matching expression to a selector value, -patterns are tried in sequence until one is found which matches the -[selector value](#patterns). Say this case is `case ´p_i \Rightarrow b_i´`. -The result of the whole expression is the result of evaluating ´b_i´, -where all pattern variables of ´p_i´ are bound to -the corresponding parts of the selector value. If no matching pattern -is found, a `scala.MatchError` exception is thrown. - -The pattern in a case may also be followed by a guard suffix -`if e` with a boolean expression ´e´. The guard expression is -evaluated if the preceding pattern in the case matches. If the guard -expression evaluates to `true`, the pattern match succeeds as -normal. If the guard expression evaluates to `false`, the pattern -in the case is considered not to match and the search for a matching -pattern continues. - -In the interest of efficiency the evaluation of a pattern matching -expression may try patterns in some other order than textual -sequence. This might affect evaluation through -side effects in guards. However, it is guaranteed that a guard -expression is evaluated only if the pattern it guards matches. - -If the selector of a pattern match is an instance of a -[`sealed` class](05-classes-and-objects.html#modifiers), -the compilation of pattern matching can emit warnings which diagnose -that a given set of patterns is not exhaustive, i.e. that there is a -possibility of a `MatchError` being raised at run-time. +If no such bounds can be found, a compile time error results. +If such bounds are found, the pattern matching clause starting with ´p´ is then typed under the assumption that each ´a_i´ has lower bound ´L_i'´ instead of ´L_i´ and has upper bound ´U_i'´ instead of ´U_i´. + +The expected type of every block ´b_i´ is the expected type of the whole pattern matching expression. +The type of the pattern matching expression is then the [weak least upper bound](03-types.html#weak-conformance) of the types of all blocks ´b_i´. + +When applying a pattern matching expression to a selector value, patterns are tried in sequence until one is found which matches the [selector value](#patterns). +Say this case is `case ´p_i \Rightarrow b_i´`. +The result of the whole expression is the result of evaluating ´b_i´, where all pattern variables of ´p_i´ are bound to the corresponding parts of the selector value. +If no matching pattern is found, a `scala.MatchError` exception is thrown. + +The pattern in a case may also be followed by a guard suffix `if e` with a boolean expression ´e´. +The guard expression is evaluated if the preceding pattern in the case matches. +If the guard expression evaluates to `true`, the pattern match succeeds as normal. +If the guard expression evaluates to `false`, the pattern in the case is considered not to match and the search for a matching pattern continues. + +In the interest of efficiency the evaluation of a pattern matching expression may try patterns in some other order than textual sequence. +This might affect evaluation through side effects in guards. +However, it is guaranteed that a guard expression is evaluated only if the pattern it guards matches. + +If the selector of a pattern match is an instance of a [`sealed` class](05-classes-and-objects.html#modifiers), the compilation of pattern matching can emit warnings which diagnose that a given set of patterns is not exhaustive, i.e. that there is a possibility of a `MatchError` being raised at run-time. ###### Example @@ -707,9 +545,8 @@ case class If[T](c: Term[Boolean], t2: Term[T]) extends Term[T] ``` -There are terms to represent numeric literals, incrementation, a zero -test, and a conditional. Every term carries as a type parameter the -type of the expression it represents (either `Int` or `Boolean`). +There are terms to represent numeric literals, incrementation, a zero test, and a conditional. +Every term carries as a type parameter the type of the expression it represents (either `Int` or `Boolean`). A type-safe evaluator for such terms can be written as follows. @@ -722,16 +559,11 @@ def eval[T](t: Term[T]): T = t match { } ``` -Note that the evaluator makes crucial use of the fact that type -parameters of enclosing methods can acquire new bounds through pattern -matching. +Note that the evaluator makes crucial use of the fact that type parameters of enclosing methods can acquire new bounds through pattern matching. -For instance, the type of the pattern in the second case, -`Succ(u)`, is `Int`. It conforms to the selector type -`T` only if we assume an upper and lower bound of `Int` for `T`. -Under the assumption `Int <: T <: Int` we can also -verify that the type right hand side of the second case, `Int` -conforms to its expected type, `T`. +For instance, the type of the pattern in the second case, `Succ(u)`, is `Int`. +It conforms to the selector type `T` only if we assume an upper and lower bound of `Int` for `T`. +Under the assumption `Int <: T <: Int` we can also verify that the type right hand side of the second case, `Int` conforms to its expected type, `T`. ## Pattern Matching Anonymous Functions @@ -745,16 +577,11 @@ An anonymous function can be defined by a sequence of cases { case ´p_1´ => ´b_1´ ... case ´p_n´ => ´b_n´ } ``` -which appear as an expression without a prior `match`. The -expected type of such an expression must in part be defined. It must -be either `scala.Function´k´[´S_1, ..., S_k´, ´R´]` for some ´k > 0´, -or `scala.PartialFunction[´S_1´, ´R´]`, where the -argument type(s) ´S_1, ..., S_k´ must be fully determined, but the result type -´R´ may be undetermined. +which appear as an expression without a prior `match`. +The expected type of such an expression must in part be defined. +It must be either `scala.Function´k´[´S_1, ..., S_k´, ´R´]` for some ´k > 0´, or `scala.PartialFunction[´S_1´, ´R´]`, where the argument type(s) ´S_1, ..., S_k´ must be fully determined, but the result type ´R´ may be undetermined. -If the expected type is [SAM-convertible](06-expressions.html#sam-conversion) -to `scala.Function´k´[´S_1, ..., S_k´, ´R´]`, -the expression is taken to be equivalent to the anonymous function: +If the expected type is [SAM-convertible](06-expressions.html#sam-conversion) to `scala.Function´k´[´S_1, ..., S_k´, ´R´]`, the expression is taken to be equivalent to the anonymous function: ```scala (´x_1: S_1, ..., x_k: S_k´) => (´x_1, ..., x_k´) match { @@ -763,9 +590,7 @@ the expression is taken to be equivalent to the anonymous function: ``` Here, each ´x_i´ is a fresh name. -As was shown [here](06-expressions.html#anonymous-functions), this anonymous function is in turn -equivalent to the following instance creation expression, where - ´T´ is the weak least upper bound of the types of all ´b_i´. +As was shown [here](06-expressions.html#anonymous-functions), this anonymous function is in turn equivalent to the following instance creation expression, where ´T´ is the weak least upper bound of the types of all ´b_i´. ```scala new scala.Function´k´[´S_1, ..., S_k´, ´T´] { @@ -775,8 +600,7 @@ new scala.Function´k´[´S_1, ..., S_k´, ´T´] { } ``` -If the expected type is `scala.PartialFunction[´S´, ´R´]`, -the expression is taken to be equivalent to the following instance creation expression: +If the expected type is `scala.PartialFunction[´S´, ´R´]`, the expression is taken to be equivalent to the following instance creation expression: ```scala new scala.PartialFunction[´S´, ´T´] { @@ -790,15 +614,11 @@ new scala.PartialFunction[´S´, ´T´] { } ``` -Here, ´x´ is a fresh name and ´T´ is the weak least upper bound of the -types of all ´b_i´. The final default case in the `isDefinedAt` -method is omitted if one of the patterns ´p_1, ..., p_n´ is -already a variable or wildcard pattern. +Here, ´x´ is a fresh name and ´T´ is the weak least upper bound of the types of all ´b_i´. +The final default case in the `isDefinedAt` method is omitted if one of the patterns ´p_1, ..., p_n´ is already a variable or wildcard pattern. ###### Example -Here's an example which uses -`foldLeft` to compute the scalar product of -two vectors: +Here's an example which uses `foldLeft` to compute the scalar product of two vectors: ```scala def scalarProduct(xs: Array[Double], ys: Array[Double]) = @@ -807,8 +627,7 @@ def scalarProduct(xs: Array[Double], ys: Array[Double]) = } ``` -The case clauses in this code are equivalent to the following -anonymous function: +The case clauses in this code are equivalent to the following anonymous function: ```scala (x, y) => (x, y) match { diff --git a/docs/_spec/09-top-level-definitions.md b/docs/_spec/09-top-level-definitions.md index 3588d26f6034..8406c0180533 100644 --- a/docs/_spec/09-top-level-definitions.md +++ b/docs/_spec/09-top-level-definitions.md @@ -19,9 +19,7 @@ TopStat ::= {Annotation} {Modifier} TmplDef QualId ::= id {‘.’ id} ``` -A compilation unit consists of a sequence of packagings, import -clauses, and class and object definitions, which may be preceded by a -package clause. +A compilation unit consists of a sequence of packagings, import clauses, and class and object definitions, which may be preceded by a package clause. A _compilation unit_ @@ -32,9 +30,7 @@ package ´p_n´; ´\mathit{stats}´ ``` -starting with one or more package -clauses is equivalent to a compilation unit consisting of the -packaging +starting with one or more package clauses is equivalent to a compilation unit consisting of the packaging ```scala package ´p_1´ { ... @@ -59,20 +55,16 @@ The exception to the implicit import of `scala.Predef` can be useful to hide, e. Packaging ::= ‘package’ QualId [nl] ‘{’ TopStatSeq ‘}’ ``` -A _package_ is a special object which defines a set of member classes, -objects and packages. Unlike other objects, packages are not introduced -by a definition. Instead, the set of members of a package is determined by -packagings. +A _package_ is a special object which defines a set of member classes, objects and packages. +Unlike other objects, packages are not introduced by a definition. +Instead, the set of members of a package is determined by packagings. -A packaging `package ´p´ { ´\mathit{ds}´ }` injects all -definitions in ´\mathit{ds}´ as members into the package whose qualified name -is ´p´. Members of a package are called _top-level_ definitions. -If a definition in ´\mathit{ds}´ is labeled `private`, it is -visible only for other members in the package. +A packaging `package ´p´ { ´\mathit{ds}´ }` injects all definitions in ´\mathit{ds}´ as members into the package whose qualified name is ´p´. +Members of a package are called _top-level_ definitions. +If a definition in ´\mathit{ds}´ is labeled `private`, it is visible only for other members in the package. -Inside the packaging, all members of package ´p´ are visible under their -simple names. However this rule does not extend to members of enclosing -packages of ´p´ that are designated by a prefix of the path ´p´. +Inside the packaging, all members of package ´p´ are visible under their simple names. +However this rule does not extend to members of enclosing packages of ´p´ that are designated by a prefix of the path ´p´. ```scala package org.net.prj { @@ -80,19 +72,15 @@ package org.net.prj { } ``` -all members of package `org.net.prj` are visible under their -simple names, but members of packages `org` or `org.net` require -explicit qualification or imports. +all members of package `org.net.prj` are visible under their simple names, but members of packages `org` or `org.net` require explicit qualification or imports. -Selections ´p´.´m´ from ´p´ as well as imports from ´p´ -work as for objects. However, unlike other objects, packages may not -be used as values. It is illegal to have a package with the same fully -qualified name as a module or a class. +Selections ´p´.´m´ from ´p´ as well as imports from ´p´ work as for objects. +However, unlike other objects, packages may not be used as values. +It is illegal to have a package with the same fully qualified name as a module or a class. -Top-level definitions outside a packaging are assumed to be injected -into a special empty package. That package cannot be named and -therefore cannot be imported. However, members of the empty package -are visible to each other without qualification. +Top-level definitions outside a packaging are assumed to be injected into a special empty package. +That package cannot be named and therefore cannot be imported. +However, members of the empty package are visible to each other without qualification. ## Package Objects @@ -100,17 +88,13 @@ are visible to each other without qualification. PackageObject ::= ‘package’ ‘object’ ObjectDef ``` -A _package object_ `package object ´p´ extends ´t´` adds the -members of template ´t´ to the package ´p´. There can be only one -package object per package. The standard naming convention is to place -the definition above in a file named `package.scala` that's -located in the directory corresponding to package ´p´. +A _package object_ `package object ´p´ extends ´t´` adds the members of template ´t´ to the package ´p´. +There can be only one package object per package. +The standard naming convention is to place the definition above in a file named `package.scala` that's located in the directory corresponding to package ´p´. -The package object should not define a member with the same name as -one of the top-level objects or classes defined in package ´p´. If -there is a name conflict, the behavior of the program is currently -undefined. It is expected that this restriction will be lifted in a -future version of Scala. +The package object should not define a member with the same name as one of the top-level objects or classes defined in package ´p´. +If there is a name conflict, the behavior of the program is currently undefined. +It is expected that this restriction will be lifted in a future version of Scala. ## Package References @@ -119,18 +103,12 @@ QualId ::= id {‘.’ id} ``` A reference to a package takes the form of a qualified identifier. -Like all other references, package references are relative. That is, -a package reference starting in a name ´p´ will be looked up in the -closest enclosing scope that defines a member named ´p´. +Like all other references, package references are relative. +That is, a package reference starting in a name ´p´ will be looked up in the closest enclosing scope that defines a member named ´p´. -If a package name is shadowed, it's possible to refer to its -fully-qualified name by prefixing it with -the special predefined name `_root_`, which refers to the -outermost root package that contains all top-level packages. +If a package name is shadowed, it's possible to refer to its fully-qualified name by prefixing it with the special predefined name `_root_`, which refers to the outermost root package that contains all top-level packages. -The name `_root_` has this special denotation only when -used as the first element of a qualifier; it is an ordinary -identifier otherwise. +The name `_root_` has this special denotation only when used as the first element of a qualifier; it is an ordinary identifier otherwise. ###### Example Consider the following program: @@ -154,29 +132,20 @@ package a { ``` -Here, the reference `_root_.b.B` refers to class `B` in the -toplevel package `b`. If the `_root_` prefix had been -omitted, the name `b` would instead resolve to the package -`a.b`, and, provided that package does not also -contain a class `B`, a compiler-time error would result. +Here, the reference `_root_.b.B` refers to class `B` in the toplevel package `b`. +If the `_root_` prefix had been omitted, the name `b` would instead resolve to the package `a.b`, and, provided that package does not also contain a class `B`, a compiler-time error would result. ## Programs -A _program_ is a top-level object that has a member method -_main_ of type `(Array[String])Unit`. Programs can be -executed from a command shell. The program's command arguments are -passed to the `main` method as a parameter of type -`Array[String]`. +A _program_ is a top-level object that has a member method _main_ of type `(Array[String])Unit`. Programs can be executed from a command shell. +The program's command arguments are passed to the `main` method as a parameter of type `Array[String]`. -The `main` method of a program can be directly defined in the -object, or it can be inherited. The scala library defines a special class -`scala.App` whose body acts as a `main` method. -An objects ´m´ inheriting from this class is thus a program, -which executes the initialization code of the object ´m´. +The `main` method of a program can be directly defined in the object, or it can be inherited. +The scala library defines a special class `scala.App` whose body acts as a `main` method. +An objects ´m´ inheriting from this class is thus a program, which executes the initialization code of the object ´m´. ###### Example -The following example will create a hello world program by defining -a method `main` in module `test.HelloWorld`. +The following example will create a hello world program by defining a method `main` in module `test.HelloWorld`. ```scala package test @@ -199,8 +168,7 @@ java test.HelloWorld would work as well. -`HelloWorld` can also be defined without a `main` method -by inheriting from `App` instead: +`HelloWorld` can also be defined without a `main` method by inheriting from `App` instead: ```scala package test diff --git a/docs/_spec/10-xml-expressions-and-patterns.md b/docs/_spec/10-xml-expressions-and-patterns.md index a9b7edb14fff..c929e24fe93d 100644 --- a/docs/_spec/10-xml-expressions-and-patterns.md +++ b/docs/_spec/10-xml-expressions-and-patterns.md @@ -9,28 +9,22 @@ chapter: 10 __By Burak Emir__ This chapter describes the syntactic structure of XML expressions and patterns. -It follows as closely as possible the XML 1.0 specification, -changes being mandated by the possibility of embedding Scala code fragments. +It follows as closely as possible the XML 1.0 specification, changes being mandated by the possibility of embedding Scala code fragments. ## XML expressions -XML expressions are expressions generated by the following production, where the -opening bracket `<` of the first element must be in a position to start the lexical -[XML mode](01-lexical-syntax.html#xml-mode). +XML expressions are expressions generated by the following production, where the opening bracket `<` of the first element must be in a position to start the lexical [XML mode](01-lexical-syntax.html#xml-mode). ```ebnf XmlExpr ::= XmlContent {Element} ``` -Well-formedness constraints of the XML specification apply, which -means for instance that start tags and end tags must match, and -attributes may only be defined once, except for constraints -related to entity resolution. +Well-formedness constraints of the XML specification apply, which means for instance that start tags and end tags must match, and attributes may only be defined once, except for constraints related to entity resolution. -The following productions describe Scala's extensible markup language, -designed as close as possible to the W3C extensible markup language -standard. Only the productions for attribute values and character data are changed. -Scala does not support declarations. Entity references are not resolved at runtime. +The following productions describe Scala's extensible markup language, designed as close as possible to the W3C extensible markup language standard. +Only the productions for attribute values and character data are changed. +Scala does not support declarations. +Entity references are not resolved at runtime. ```ebnf Element ::= EmptyElemTag @@ -50,21 +44,13 @@ XmlContent ::= Element | Comment ``` -If an XML expression is a single element, its value is a runtime -representation of an XML node (an instance of a subclass of -`scala.xml.Node`). If the XML expression consists of more -than one element, then its value is a runtime representation of a -sequence of XML nodes (an instance of a subclass of -`scala.Seq[scala.xml.Node]`). +If an XML expression is a single element, its value is a runtime representation of an XML node (an instance of a subclass of `scala.xml.Node`). +If the XML expression consists of more than one element, then its value is a runtime representation of a sequence of XML nodes (an instance of a subclass of `scala.Seq[scala.xml.Node]`). -If an XML expression is an entity reference, CDATA section, processing -instruction, or a comment, it is represented by an instance of the -corresponding Scala runtime class. +If an XML expression is an entity reference, CDATA section, processing instruction, or a comment, it is represented by an instance of the corresponding Scala runtime class. -By default, beginning and trailing whitespace in element content is removed, -and consecutive occurrences of whitespace are replaced by a single space -character `\u0020`. This behavior can be changed to preserve all whitespace -with a compiler option. +By default, beginning and trailing whitespace in element content is removed, and consecutive occurrences of whitespace are replaced by a single space character `\u0020`. +This behavior can be changed to preserve all whitespace with a compiler option. ```ebnf Attribute ::= Name Eq AttValue @@ -80,10 +66,9 @@ CharData ::= { CharNoRef } ´\textit{ without}´ {CharNoRef}‘{’CharB ``` -XML expressions may contain Scala expressions as attribute values or -within nodes. In the latter case, these are embedded using a single opening -brace `{` and ended by a closing brace `}`. To express a single opening braces -within XML text as generated by CharData, it must be doubled. +XML expressions may contain Scala expressions as attribute values or within nodes. +In the latter case, these are embedded using a single opening brace `{` and ended by a closing brace `}`. +To express a single opening braces within XML text as generated by CharData, it must be doubled. Thus, `{{` represents the XML text `{` and does not introduce an embedded Scala expression. @@ -104,9 +89,7 @@ XNameStart ::= ‘_’ | BaseChar | Ideographic ## XML patterns -XML patterns are patterns generated by the following production, where -the opening bracket `<` of the element patterns must be in a position -to start the lexical [XML mode](01-lexical-syntax.html#xml-mode). +XML patterns are patterns generated by the following production, where the opening bracket `<` of the element patterns must be in a position to start the lexical [XML mode](01-lexical-syntax.html#xml-mode). ```ebnf XmlPattern ::= ElementPattern @@ -114,18 +97,14 @@ XmlPattern ::= ElementPattern Well-formedness constraints of the XML specification apply. -An XML pattern has to be a single element pattern. It -matches exactly those runtime -representations of an XML tree -that have the same structure as described by the pattern. +An XML pattern has to be a single element pattern. +It matches exactly those runtime representations of an XML tree that have the same structure as described by the pattern. XML patterns may contain [Scala patterns](08-pattern-matching.html#pattern-matching-expressions). Whitespace is treated the same way as in XML expressions. -By default, beginning and trailing whitespace in element content is removed, -and consecutive occurrences of whitespace are replaced by a single space -character `\u0020`. This behavior can be changed to preserve all whitespace -with a compiler option. +By default, beginning and trailing whitespace in element content is removed, and consecutive occurrences of whitespace are replaced by a single space character `\u0020`. +This behavior can be changed to preserve all whitespace with a compiler option. ```ebnf ElemPattern ::= EmptyElemTagP diff --git a/docs/_spec/11-annotations.md b/docs/_spec/11-annotations.md index 9f806ff5a471..11325a1639f0 100644 --- a/docs/_spec/11-annotations.md +++ b/docs/_spec/11-annotations.md @@ -15,16 +15,14 @@ chapter: 11 Annotations associate meta-information with definitions. A simple annotation has the form `@´c´` or `@´c(a_1, ..., a_n)´`. -Here, ´c´ is a constructor of a class ´C´, which must conform -to the class `scala.Annotation`. +Here, ´c´ is a constructor of a class ´C´, which must conform to the class `scala.Annotation`. -Annotations may apply to definitions or declarations, types, or -expressions. An annotation of a definition or declaration appears in -front of that definition. An annotation of a type appears after -that type. An annotation of an expression ´e´ appears after the -expression ´e´, separated by a colon. More than one annotation clause -may apply to an entity. The order in which these annotations are given -does not matter. +Annotations may apply to definitions or declarations, types, or expressions. +An annotation of a definition or declaration appears in front of that definition. +An annotation of a type appears after that type. +An annotation of an expression ´e´ appears after the expression ´e´, separated by a colon. +More than one annotation clause may apply to an entity. +The order in which these annotations are given does not matter. Examples: @@ -39,138 +37,90 @@ String @local // Type annotation ### Java Platform Annotations -The meaning of annotation clauses is implementation-dependent. On the -Java platform, the following annotations have a standard meaning. +The meaning of annotation clauses is implementation-dependent. +On the Java platform, the following annotations have a standard meaning. - * `@transient` Marks a field to be non-persistent; this is - equivalent to the `transient` - modifier in Java. +* `@transient` Marks a field to be non-persistent; this is equivalent to the `transient` modifier in Java. - * `@volatile` Marks a field which can change its value - outside the control of the program; this - is equivalent to the `volatile` - modifier in Java. +* `@volatile` Marks a field which can change its value outside the control of the program; this is equivalent to the `volatile` modifier in Java. - * `@SerialVersionUID()` Attaches a serial version identifier (a - `long` constant) to a class. - This is equivalent to the following field - definition in Java: +* `@SerialVersionUID()` Attaches a serial version identifier (a `long` constant) to a class. +This is equivalent to the following field definition in Java: - ```java - private final static SerialVersionUID = - ``` +```java +private final static SerialVersionUID = +``` - * `@throws()` A Java compiler checks that a program contains handlers for checked exceptions - by analyzing which checked exceptions can result from the execution of a method or - constructor. For each checked exception which is a possible result, the - `throws` - clause for the method or constructor must mention the class of that exception - or one of the superclasses of the class of that exception. +* `@throws()` A Java compiler checks that a program contains handlers for checked exceptions by analyzing which checked exceptions can result from the execution of a method or constructor. +For each checked exception which is a possible result, the `throws` clause for the method or constructor must mention the class of that exception or one of the superclasses of the class of that exception. ### Java Beans Annotations - * `@scala.beans.BeanProperty` When prefixed to a definition of some variable `X`, this - annotation causes getter and setter methods `getX`, `setX` - in the Java bean style to be added in the class containing the - variable. The first letter of the variable appears capitalized after - the `get` or `set`. When the annotation is added to the - definition of an immutable value definition `X`, only a getter is - generated. The construction of these methods is part of - code-generation; therefore, these methods become visible only once a - classfile for the containing class is generated. +* `@scala.beans.BeanProperty` When prefixed to a definition of some variable `X`, this annotation causes getter and setter methods `getX`, `setX` in the Java bean style to be added in the class containing the variable. +The first letter of the variable appears capitalized after the `get` or `set`. +When the annotation is added to the definition of an immutable value definition `X`, only a getter is generated. +The construction of these methods is part of code-generation; therefore, these methods become visible only once a classfile for the containing class is generated. - * `@scala.beans.BooleanBeanProperty` This annotation is equivalent to `scala.reflect.BeanProperty`, but - the generated getter method is named `isX` instead of `getX`. +* `@scala.beans.BooleanBeanProperty` This annotation is equivalent to `scala.reflect.BeanProperty`, but the generated getter method is named `isX` instead of `getX`. ### Deprecation Annotations - * `@deprecated(message: , since: )`
      - Marks a definition as deprecated. Accesses to the - defined entity will then cause a deprecated warning mentioning the - _message_ `` to be issued from the compiler. - The argument _since_ documents since when the definition should be considered deprecated.
      - Deprecated warnings are suppressed in code that belongs itself to a definition - that is labeled deprecated. +* `@deprecated(message: , since: )`
      +Marks a definition as deprecated. +Accesses to the defined entity will then cause a deprecated warning mentioning the _message_ `` to be issued from the compiler. +The argument _since_ documents since when the definition should be considered deprecated.
      +Deprecated warnings are suppressed in code that belongs itself to a definition that is labeled deprecated. - * `@deprecatedName(name: , since: )`
      - Marks a formal parameter name as deprecated. Invocations of this entity - using named parameter syntax referring to the deprecated parameter name cause a deprecation warning. +* `@deprecatedName(name: , since: )`
      +Marks a formal parameter name as deprecated. +Invocations of this entity using named parameter syntax referring to the deprecated parameter name cause a deprecation warning. ### Scala Compiler Annotations - * `@unchecked` When applied to the selector of a `match` expression, - this attribute suppresses any warnings about non-exhaustive pattern - matches that would otherwise be emitted. For instance, no warnings - would be produced for the method definition below. - - ```scala - def f(x: Option[Int]) = (x: @unchecked) match { - case Some(y) => y - } - ``` - - Without the `@unchecked` annotation, a Scala compiler could - infer that the pattern match is non-exhaustive, and could produce a - warning because `Option` is a `sealed` class. - - * `@uncheckedStable` When applied a value declaration or definition, it allows the defined - value to appear in a path, even if its type is [volatile](03-types.html#volatile-types). - For instance, the following member definitions are legal: - - ```scala - type A { type T } - type B - @uncheckedStable val x: A with B // volatile type - val y: x.T // OK since `x' is still a path - ``` - - Without the `@uncheckedStable` annotation, the designator `x` - would not be a path since its type `A with B` is volatile. Hence, - the reference `x.T` would be malformed. - - When applied to value declarations or definitions that have non-volatile - types, the annotation has no effect. - - * `@specialized` When applied to the definition of a type parameter, this annotation causes - the compiler - to generate specialized definitions for primitive types. An optional list of - primitive - types may be given, in which case specialization takes into account only - those types. - For instance, the following code would generate specialized traits for - `Unit`, `Int` and `Double` - - ```scala - trait Function0[@specialized(Unit, Int, Double) T] { - def apply: T - } - ``` - - Whenever the static type of an expression matches a specialized variant of - a definition, the compiler will instead use the specialized version. - See the [specialization sid](https://docs.scala-lang.org/sips/scala-specialization.html) for more details of the implementation. +* `@unchecked` When applied to the selector of a `match` expression, this attribute suppresses any warnings about non-exhaustive pattern matches that would otherwise be emitted. +For instance, no warnings would be produced for the method definition below. +```scala +def f(x: Option[Int]) = (x: @unchecked) match { + case Some(y) => y +} +``` +Without the `@unchecked` annotation, a Scala compiler could infer that the pattern match is non-exhaustive, and could produce a warning because `Option` is a `sealed` class. + +* `@uncheckedStable` When applied a value declaration or definition, it allows the defined value to appear in a path, even if its type is [volatile](03-types.html#volatile-types). +For instance, the following member definitions are legal: +```scala +type A { type T } +type B +@uncheckedStable val x: A with B // volatile type +val y: x.T // OK since `x' is still a path +``` +Without the `@uncheckedStable` annotation, the designator `x` would not be a path since its type `A with B` is volatile. +Hence, the reference `x.T` would be malformed. + +When applied to value declarations or definitions that have non-volatile types, the annotation has no effect. + +* `@specialized` When applied to the definition of a type parameter, this annotation causes the compiler to generate specialized definitions for primitive types. +An optional list of primitive types may be given, in which case specialization takes into account only those types. +For instance, the following code would generate specialized traits for `Unit`, `Int` and `Double` +```scala +trait Function0[@specialized(Unit, Int, Double) T] { + def apply: T +} +``` +Whenever the static type of an expression matches a specialized variant of a definition, the compiler will instead use the specialized version. +See the [specialization sid](https://docs.scala-lang.org/sips/scala-specialization.html) for more details of the implementation. ## User-defined Annotations -Other annotations may be interpreted by platform- or application-dependent -tools. The class `scala.annotation.Annotation` is the base class for -user-defined annotations. It has two sub-traits: -- `scala.annotation.StaticAnnotation`: Instances of a subclass of this trait - will be stored in the generated class files, and therefore accessible to - runtime reflection and later compilation runs. -- `scala.annotation.ConstantAnnotation`: Instances of a subclass of this trait - may only have arguments which are - [constant expressions](06-expressions.html#constant-expressions), and are - also stored in the generated class files. -- If an annotation class inherits from neither `scala.ConstantAnnotation` nor - `scala.StaticAnnotation`, its instances are visible only locally during the - compilation run that analyzes them. +Other annotations may be interpreted by platform- or application-dependent tools. +The class `scala.annotation.Annotation` is the base class for user-defined annotations. It has two sub-traits: +- `scala.annotation.StaticAnnotation`: Instances of a subclass of this trait will be stored in the generated class files, and therefore accessible to runtime reflection and later compilation runs. +- `scala.annotation.ConstantAnnotation`: Instances of a subclass of this trait may only have arguments which are [constant expressions](06-expressions.html#constant-expressions), and are also stored in the generated class files. +- If an annotation class inherits from neither `scala.ConstantAnnotation` nor `scala.StaticAnnotation`, its instances are visible only locally during the compilation run that analyzes them. ## Host-platform Annotations -The host platform may define its own annotation format. These annotations do not -extend any of the classes in the `scala.annotation` package, but can generally -be used in the same way as Scala annotations. The host platform may impose -additional restrictions on the expressions which are valid as annotation -arguments. +The host platform may define its own annotation format. +These annotations do not extend any of the classes in the `scala.annotation` package, but can generally be used in the same way as Scala annotations. +The host platform may impose additional restrictions on the expressions which are valid as annotation arguments. diff --git a/docs/_spec/12-the-scala-standard-library.md b/docs/_spec/12-the-scala-standard-library.md index 9bbc12e463dd..086d396ba4dd 100644 --- a/docs/_spec/12-the-scala-standard-library.md +++ b/docs/_spec/12-the-scala-standard-library.md @@ -6,39 +6,28 @@ chapter: 12 # The Scala Standard Library -The Scala standard library consists of the package `scala` with a -number of classes and modules. Some of these classes are described in -the following. +The Scala standard library consists of the package `scala` with a number of classes and modules. +Some of these classes are described in the following. ![Class hierarchy of Scala](public/images/classhierarchy.png) ## Root Classes The root of this hierarchy is formed by class `Any`. -Every class in a Scala execution environment inherits directly or -indirectly from this class. Class `Any` has two direct -subclasses: `AnyRef` and `AnyVal`. +Every class in a Scala execution environment inherits directly or indirectly from this class. +Class `Any` has two direct subclasses: `AnyRef` and `AnyVal`. -The subclass `AnyRef` represents all values which are represented -as objects in the underlying host system. Classes written in other languages -inherit from `scala.AnyRef`. +The subclass `AnyRef` represents all values which are represented as objects in the underlying host system. +Classes written in other languages inherit from `scala.AnyRef`. -The predefined subclasses of class `AnyVal` describe -values which are not implemented as objects in the underlying host -system. +The predefined subclasses of class `AnyVal` describe values which are not implemented as objects in the underlying host system. -User-defined Scala classes which do not explicitly inherit from -`AnyVal` inherit directly or indirectly from `AnyRef`. They cannot -inherit from both `AnyRef` and `AnyVal`. +User-defined Scala classes which do not explicitly inherit from `AnyVal` inherit directly or indirectly from `AnyRef`. +They cannot inherit from both `AnyRef` and `AnyVal`. -Classes `AnyRef` and `AnyVal` are required to provide only -the members declared in class `Any`, but implementations may add -host-specific methods to these classes (for instance, an -implementation may identify class `AnyRef` with its own root -class for objects). +Classes `AnyRef` and `AnyVal` are required to provide only the members declared in class `Any`, but implementations may add host-specific methods to these classes (for instance, an implementation may identify class `AnyRef` with its own root class for objects). -The signatures of these root classes are described by the following -definitions. +The signatures of these root classes are described by the following definitions. ```scala package scala @@ -88,8 +77,7 @@ class AnyRef extends Any { } ``` -The type test `´x´.isInstanceOf[´T´]` is equivalent to a typed -pattern match +The type test `´x´.isInstanceOf[´T´]` is equivalent to a typed pattern match ```scala ´x´ match { @@ -98,33 +86,25 @@ pattern match } ``` -where the type ´T'´ is the same as ´T´ except if ´T´ is -of the form ´D´ or ´D[\mathit{tps}]´ where ´D´ is a type member of some outer class ´C´. +where the type ´T'´ is the same as ´T´ except if ´T´ is of the form ´D´ or ´D[\mathit{tps}]´ where ´D´ is a type member of some outer class ´C´. In this case ´T'´ is `´C´#´D´` (or `´C´#´D[tps]´`, respectively), whereas ´T´ itself would expand to `´C´.this.´D[tps]´`. In other words, an `isInstanceOf` test does not check that types have the same enclosing instance. -The test `´x´.asInstanceOf[´T´]` is treated specially if ´T´ is a -[numeric value type](#value-classes). In this case the cast will -be translated to an application of a [conversion method](#numeric-value-types) -`x.to´T´`. For non-numeric values ´x´ the operation will raise a -`ClassCastException`. +The test `´x´.asInstanceOf[´T´]` is treated specially if ´T´ is a [numeric value type](#value-classes). +In this case the cast will be translated to an application of a [conversion method](#numeric-value-types) `x.to´T´`. +For non-numeric values ´x´ the operation will raise a `ClassCastException`. ## Value Classes -Value classes are classes whose instances are not represented as -objects by the underlying host system. All value classes inherit from -class `AnyVal`. Scala implementations need to provide the -value classes `Unit`, `Boolean`, `Double`, `Float`, -`Long`, `Int`, `Char`, `Short`, and `Byte` -(but are free to provide others as well). +Value classes are classes whose instances are not represented as objects by the underlying host system. +All value classes inherit from class `AnyVal`. +Scala implementations need to provide the value classes `Unit`, `Boolean`, `Double`, `Float`, `Long`, `Int`, `Char`, `Short`, and `Byte` (but are free to provide others as well). The signatures of these classes are defined in the following. ### Numeric Value Types -Classes `Double`, `Float`, -`Long`, `Int`, `Char`, `Short`, and `Byte` -are together called _numeric value types_. Classes `Byte`, -`Short`, or `Char` are called _subrange types_. +Classes `Double`, `Float`, `Long`, `Int`, `Char`, `Short`, and `Byte` are together called _numeric value types_. +Classes `Byte`, `Short`, or `Char` are called _subrange types_. Subrange types, as well as `Int` and `Long` are called _integer types_, whereas `Float` and `Double` are called _floating point types_. Numeric value types are ranked in the following partial order: @@ -137,81 +117,52 @@ Byte - Short Char ``` -`Byte` and `Short` are the lowest-ranked types in this order, -whereas `Double` is the highest-ranked. Ranking does _not_ -imply a [conformance relationship](03-types.html#conformance); for -instance `Int` is not a subtype of `Long`. However, object -[`Predef`](#the-predef-object) defines [views](07-implicits.html#views) -from every numeric value type to all higher-ranked numeric value types. -Therefore, lower-ranked types are implicitly converted to higher-ranked types -when required by the [context](06-expressions.html#implicit-conversions). - -Given two numeric value types ´S´ and ´T´, the _operation type_ of -´S´ and ´T´ is defined as follows: If both ´S´ and ´T´ are subrange -types then the operation type of ´S´ and ´T´ is `Int`. Otherwise -the operation type of ´S´ and ´T´ is the larger of the two types wrt -ranking. Given two numeric values ´v´ and ´w´ the operation type of -´v´ and ´w´ is the operation type of their run-time types. +`Byte` and `Short` are the lowest-ranked types in this order, whereas `Double` is the highest-ranked. +Ranking does _not_ +imply a [conformance relationship](03-types.html#conformance); for instance `Int` is not a subtype of `Long`. +However, object [`Predef`](#the-predef-object) defines [views](07-implicits.html#views) from every numeric value type to all higher-ranked numeric value types. +Therefore, lower-ranked types are implicitly converted to higher-ranked types when required by the [context](06-expressions.html#implicit-conversions). + +Given two numeric value types ´S´ and ´T´, the _operation type_ of ´S´ and ´T´ is defined as follows: If both ´S´ and ´T´ are subrange types then the operation type of ´S´ and ´T´ is `Int`. +Otherwise the operation type of ´S´ and ´T´ is the larger of the two types wrt +ranking. +Given two numeric values ´v´ and ´w´ the operation type of ´v´ and ´w´ is the operation type of their run-time types. Any numeric value type ´T´ supports the following methods. - * Comparison methods for equals (`==`), not-equals (`!=`), - less-than (`<`), greater-than (`>`), less-than-or-equals - (`<=`), greater-than-or-equals (`>=`), which each exist in 7 - overloaded alternatives. Each alternative takes a parameter of some - numeric value type. Its result type is type `Boolean`. The - operation is evaluated by converting the receiver and its argument to - their operation type and performing the given comparison operation of - that type. - * Arithmetic methods addition (`+`), subtraction (`-`), - multiplication (`*`), division (`/`), and remainder - (`%`), which each exist in 7 overloaded alternatives. Each - alternative takes a parameter of some numeric value type ´U´. Its - result type is the operation type of ´T´ and ´U´. The operation is - evaluated by converting the receiver and its argument to their - operation type and performing the given arithmetic operation of that - type. - * Parameterless arithmetic methods identity (`+`) and negation - (`-`), with result type ´T´. The first of these returns the - receiver unchanged, whereas the second returns its negation. - * Conversion methods `toByte`, `toShort`, `toChar`, - `toInt`, `toLong`, `toFloat`, `toDouble` which - convert the receiver object to the target type, using the rules of - Java's numeric type cast operation. The conversion might truncate the - numeric value (as when going from `Long` to `Int` or from - `Int` to `Byte`) or it might lose precision (as when going - from `Double` to `Float` or when converting between - `Long` and `Float`). +* Comparison methods for equals (`==`), not-equals (`!=`), less-than (`<`), greater-than (`>`), less-than-or-equals (`<=`), greater-than-or-equals (`>=`), which each exist in 7 overloaded alternatives. +Each alternative takes a parameter of some numeric value type. +Its result type is type `Boolean`. +The operation is evaluated by converting the receiver and its argument to their operation type and performing the given comparison operation of that type. +* Arithmetic methods addition (`+`), subtraction (`-`), multiplication (`*`), division (`/`), and remainder (`%`), which each exist in 7 overloaded alternatives. +Each alternative takes a parameter of some numeric value type ´U´. +Its result type is the operation type of ´T´ and ´U´. +The operation is evaluated by converting the receiver and its argument to their operation type and performing the given arithmetic operation of that type. +* Parameterless arithmetic methods identity (`+`) and negation (`-`), with result type ´T´. +The first of these returns the receiver unchanged, whereas the second returns its negation. +* Conversion methods `toByte`, `toShort`, `toChar`, `toInt`, `toLong`, `toFloat`, `toDouble` which convert the receiver object to the target type, using the rules of Java's numeric type cast operation. +The conversion might truncate the numeric value (as when going from `Long` to `Int` or from `Int` to `Byte`) or it might lose precision (as when going from `Double` to `Float` or when converting between `Long` and `Float`). Integer numeric value types support in addition the following operations: - * Bit manipulation methods bitwise-and (`&`), bitwise-or - {`|`}, and bitwise-exclusive-or (`^`), which each exist in 5 - overloaded alternatives. Each alternative takes a parameter of some - integer numeric value type. Its result type is the operation type of - ´T´ and ´U´. The operation is evaluated by converting the receiver and - its argument to their operation type and performing the given bitwise - operation of that type. - - * A parameterless bit-negation method (`~`). Its result type is - the receiver type ´T´ or `Int`, whichever is larger. - The operation is evaluated by converting the receiver to the result - type and negating every bit in its value. - * Bit-shift methods left-shift (`<<`), arithmetic right-shift - (`>>`), and unsigned right-shift (`>>>`). Each of these - methods has two overloaded alternatives, which take a parameter ´n´ - of type `Int`, respectively `Long`. The result type of the - operation is the receiver type ´T´, or `Int`, whichever is larger. - The operation is evaluated by converting the receiver to the result - type and performing the specified shift by ´n´ bits. - -Numeric value types also implement operations `equals`, -`hashCode`, and `toString` from class `Any`. - -The `equals` method tests whether the argument is a numeric value -type. If this is true, it will perform the `==` operation which -is appropriate for that type. That is, the `equals` method of a -numeric value type can be thought of being defined as follows: +* Bit manipulation methods bitwise-and (`&`), bitwise-or {`|`}, and bitwise-exclusive-or (`^`), which each exist in 5 overloaded alternatives. +Each alternative takes a parameter of some integer numeric value type. +Its result type is the operation type of ´T´ and ´U´. +The operation is evaluated by converting the receiver and its argument to their operation type and performing the given bitwise operation of that type. + +* A parameterless bit-negation method (`~`). +Its result type is the receiver type ´T´ or `Int`, whichever is larger. +The operation is evaluated by converting the receiver to the result type and negating every bit in its value. +* Bit-shift methods left-shift (`<<`), arithmetic right-shift (`>>`), and unsigned right-shift (`>>>`). +Each of these methods has two overloaded alternatives, which take a parameter ´n´ of type `Int`, respectively `Long`. +The result type of the operation is the receiver type ´T´, or `Int`, whichever is larger. +The operation is evaluated by converting the receiver to the result type and performing the specified shift by ´n´ bits. + +Numeric value types also implement operations `equals`, `hashCode`, and `toString` from class `Any`. + +The `equals` method tests whether the argument is a numeric value type. +If this is true, it will perform the `==` operation which is appropriate for that type. +That is, the `equals` method of a numeric value type can be thought of being defined as follows: ```scala def equals(other: Any): Boolean = other match { @@ -226,12 +177,10 @@ def equals(other: Any): Boolean = other match { } ``` -The `hashCode` method returns an integer hashcode that maps equal -numeric values to equal results. It is guaranteed to be the identity -for type `Int` and for all subrange types. +The `hashCode` method returns an integer hashcode that maps equal numeric values to equal results. +It is guaranteed to be the identity for type `Int` and for all subrange types. -The `toString` method displays its receiver as an integer or -floating point number. +The `toString` method displays its receiver as an integer or floating point number. ###### Example @@ -285,9 +234,8 @@ abstract sealed class Int extends AnyVal { ### Class `Boolean` -Class `Boolean` has only two values: `true` and -`false`. It implements operations as given in the following -class definition. +Class `Boolean` has only two values: `true` and `false`. +It implements operations as given in the following class definition. ```scala package scala @@ -309,47 +257,36 @@ abstract sealed class Boolean extends AnyVal { } ``` -The class also implements operations `equals`, `hashCode`, -and `toString` from class `Any`. +The class also implements operations `equals`, `hashCode`, and `toString` from class `Any`. -The `equals` method returns `true` if the argument is the -same boolean value as the receiver, `false` otherwise. The -`hashCode` method returns a fixed, implementation-specific hash-code when invoked on `true`, -and a different, fixed, implementation-specific hash-code when invoked on `false`. The `toString` method -returns the receiver converted to a string, i.e. either `"true"` or `"false"`. +The `equals` method returns `true` if the argument is the same boolean value as the receiver, `false` otherwise. +The `hashCode` method returns a fixed, implementation-specific hash-code when invoked on `true`, and a different, fixed, implementation-specific hash-code when invoked on `false`. +The `toString` method returns the receiver converted to a string, i.e. either `"true"` or `"false"`. ### Class `Unit` -Class `Unit` has only one value: `()`. It implements only -the three methods `equals`, `hashCode`, and `toString` -from class `Any`. +Class `Unit` has only one value: `()`. +It implements only the three methods `equals`, `hashCode`, and `toString` from class `Any`. -The `equals` method returns `true` if the argument is the -unit value `()`, `false` otherwise. The -`hashCode` method returns a fixed, implementation-specific hash-code, +The `equals` method returns `true` if the argument is the unit value `()`, `false` otherwise. +The `hashCode` method returns a fixed, implementation-specific hash-code. The `toString` method returns `"()"`. ## Standard Reference Classes -This section presents some standard Scala reference classes which are -treated in a special way by the Scala compiler – either Scala provides -syntactic sugar for them, or the Scala compiler generates special code -for their operations. Other classes in the standard Scala library are -documented in the Scala library documentation by HTML pages. +This section presents some standard Scala reference classes which are treated in a special way by the Scala compiler – either Scala provides syntactic sugar for them, or the Scala compiler generates special code for their operations. +Other classes in the standard Scala library are documented in the Scala library documentation by HTML pages. ### Class `String` -Scala's `String` class is usually derived from the standard String -class of the underlying host system (and may be identified with -it). For Scala clients the class is taken to support in each case a -method +Scala's `String` class is usually derived from the standard String class of the underlying host system (and may be identified with it). +For Scala clients the class is taken to support in each case a method ```scala def + (that: Any): String ``` -which concatenates its left operand with the textual representation of its -right operand. +which concatenates its left operand with the textual representation of its right operand. ### The `Tuple` classes @@ -385,14 +322,12 @@ class PartialFunction[-A, +B] extends Function1[A, B] { } ``` -The implicitly imported [`Predef`](#the-predef-object) object defines the name -`Function` as an alias of `Function1`. +The implicitly imported [`Predef`](#the-predef-object) object defines the name `Function` as an alias of `Function1`. ### Class `Array` -All operations on arrays desugar to the corresponding operations of the -underlying platform. Therefore, the following class definition is given for -informational purposes only: +All operations on arrays desugar to the corresponding operations of the underlying platform. +Therefore, the following class definition is given for informational purposes only: ```scala final class Array[T](_length: Int) @@ -404,20 +339,14 @@ extends java.io.Serializable with java.lang.Cloneable { } ``` -If ´T´ is not a type parameter or abstract type, the type `Array[T]` -is represented as the array type `|T|[]` in the -underlying host system, where `|T|` is the erasure of `T`. -If ´T´ is a type parameter or abstract type, a different representation might be -used (it is `Object` on the Java platform). +If ´T´ is not a type parameter or abstract type, the type `Array[T]` is represented as the array type `|T|[]` in the underlying host system, where `|T|` is the erasure of `T`. +If ´T´ is a type parameter or abstract type, a different representation might be used (it is `Object` on the Java platform). #### Operations -`length` returns the length of the array, `apply` means subscripting, -and `update` means element update. +`length` returns the length of the array, `apply` means subscripting, and `update` means element update. -Because of the syntactic sugar for `apply` and `update` operations, -we have the following correspondences between Scala and Java code for -operations on an array `xs`: +Because of the syntactic sugar for `apply` and `update` operations, we have the following correspondences between Scala and Java code for operations on an array `xs`: |_Scala_ |_Java_ | |------------------|------------| @@ -425,35 +354,22 @@ operations on an array `xs`: |`xs(i)` |`xs[i]` | |`xs(i) = e` |`xs[i] = e` | -Two implicit conversions exist in `Predef` that are frequently applied to arrays: -a conversion to `scala.collection.mutable.ArrayOps` and a conversion to -`scala.collection.mutable.ArraySeq` (a subtype of `scala.collection.Seq`). +Two implicit conversions exist in `Predef` that are frequently applied to arrays: a conversion to `scala.collection.mutable.ArrayOps` and a conversion to `scala.collection.mutable.ArraySeq` (a subtype of `scala.collection.Seq`). -Both types make many of the standard operations found in the Scala -collections API available. The conversion to `ArrayOps` is temporary, as all operations -defined on `ArrayOps` return a value of type `Array`, while the conversion to `ArraySeq` -is permanent as all operations return a value of type `ArraySeq`. +Both types make many of the standard operations found in the Scala collections API available. +The conversion to `ArrayOps` is temporary, as all operations defined on `ArrayOps` return a value of type `Array`, while the conversion to `ArraySeq` is permanent as all operations return a value of type `ArraySeq`. The conversion to `ArrayOps` takes priority over the conversion to `ArraySeq`. -Because of the tension between parametrized types in Scala and the ad-hoc -implementation of arrays in the host-languages, some subtle points -need to be taken into account when dealing with arrays. These are -explained in the following. +Because of the tension between parametrized types in Scala and the ad-hoc implementation of arrays in the host-languages, some subtle points need to be taken into account when dealing with arrays. +These are explained in the following. #### Variance -Unlike arrays in Java, arrays in Scala are _not_ -co-variant; That is, ´S <: T´ does not imply -`Array[´S´] ´<:´ Array[´T´]` in Scala. -However, it is possible to cast an array -of ´S´ to an array of ´T´ if such a cast is permitted in the host -environment. +Unlike arrays in Java, arrays in Scala are _not_ co-variant; That is, ´S <: T´ does not imply `Array[´S´] ´<:´ Array[´T´]` in Scala. +However, it is possible to cast an array of ´S´ to an array of ´T´ if such a cast is permitted in the host environment. -For instance `Array[String]` does not conform to -`Array[Object]`, even though `String` conforms to `Object`. -However, it is possible to cast an expression of type -`Array[String]` to `Array[Object]`, and this -cast will succeed without raising a `ClassCastException`. Example: +For instance `Array[String]` does not conform to `Array[Object]`, even though `String` conforms to `Object`. +However, it is possible to cast an expression of type `Array[String]` to `Array[Object]`, and this cast will succeed without raising a `ClassCastException`. Example: ```scala val xs = new Array[String](2) @@ -461,14 +377,9 @@ val xs = new Array[String](2) val ys: Array[Object] = xs.asInstanceOf[Array[Object]] // OK ``` -The instantiation of an array with a polymorphic element type ´T´ requires -information about type ´T´ at runtime. -This information is synthesized by adding a [context bound](07-implicits.html#context-bounds-and-view-bounds) -of `scala.reflect.ClassTag` to type ´T´. -An example is the -following implementation of method `mkArray`, which creates -an array of an arbitrary type ´T´, given a sequence of ´T´`s which -defines its elements: +The instantiation of an array with a polymorphic element type ´T´ requires information about type ´T´ at runtime. +This information is synthesized by adding a [context bound](07-implicits.html#context-bounds-and-view-bounds) of `scala.reflect.ClassTag` to type ´T´. +An example is the following implementation of method `mkArray`, which creates an array of an arbitrary type ´T´, given a sequence of ´T´`s which defines its elements: ```scala import reflect.ClassTag @@ -483,19 +394,14 @@ def mkArray[T : ClassTag](elems: Seq[T]): Array[T] = { } ``` -If type ´T´ is a type for which the host platform offers a specialized array -representation, this representation is used. +If type ´T´ is a type for which the host platform offers a specialized array representation, this representation is used. ###### Example -On the Java Virtual Machine, an invocation of `mkArray(List(1,2,3))` -will return a primitive array of `int`s, written as `int[]` in Java. +On the Java Virtual Machine, an invocation of `mkArray(List(1,2,3))` will return a primitive array of `int`s, written as `int[]` in Java. #### Companion object -`Array`'s companion object provides various factory methods for the -instantiation of single- and multi-dimensional arrays, an extractor method -[`unapplySeq`](08-pattern-matching.html#extractor-patterns) which enables pattern matching -over arrays and additional utility methods: +`Array`'s companion object provides various factory methods for the instantiation of single- and multi-dimensional arrays, an extractor method [`unapplySeq`](08-pattern-matching.html#extractor-patterns) which enables pattern matching over arrays and additional utility methods: ```scala package scala @@ -623,10 +529,8 @@ trait Node { ## The `Predef` Object -The `Predef` object defines standard functions and type aliases -for Scala programs. It is implicitly imported, as described in -[the chapter on name binding](02-identifiers-names-and-scopes.html), -so that all its defined members are available without qualification. +The `Predef` object defines standard functions and type aliases for Scala programs. +It is implicitly imported, as described in [the chapter on name binding](02-identifiers-names-and-scopes.html), so that all its defined members are available without qualification. Its definition for the JVM environment conforms to the following signature: ```scala @@ -728,89 +632,70 @@ object Predef { ### Predefined Implicit Definitions The `Predef` object also contains a number of implicit definitions, which are available by default (because `Predef` is implicitly imported). -Implicit definitions come in two priorities. High-priority implicits are defined in the `Predef` class itself whereas low priority implicits are defined in a class inherited by `Predef`. The rules of -static [overloading resolution](06-expressions.html#overloading-resolution) -stipulate that, all other things being equal, implicit resolution -prefers high-priority implicits over low-priority ones. +Implicit definitions come in two priorities. +High-priority implicits are defined in the `Predef` class itself whereas low priority implicits are defined in a class inherited by `Predef`. +The rules of static [overloading resolution](06-expressions.html#overloading-resolution) stipulate that, all other things being equal, implicit resolution prefers high-priority implicits over low-priority ones. The available low-priority implicits include definitions falling into the following categories. -1. For every primitive type, a wrapper that takes values of that type - to instances of a `runtime.Rich*` class. For instance, values of type `Int` - can be implicitly converted to instances of class `runtime.RichInt`. +1. For every primitive type, a wrapper that takes values of that type to instances of a `runtime.Rich*` class. +For instance, values of type `Int` can be implicitly converted to instances of class `runtime.RichInt`. -1. For every array type with elements of primitive type, a wrapper that - takes the arrays of that type to instances of a `ArraySeq` class. For instance, values of type `Array[Float]` can be implicitly converted to instances of class `ArraySeq[Float]`. - There are also generic array wrappers that take elements - of type `Array[T]` for arbitrary `T` to `ArraySeq`s. +1. For every array type with elements of primitive type, a wrapper that takes the arrays of that type to instances of a `ArraySeq` class. +For instance, values of type `Array[Float]` can be implicitly converted to instances of class `ArraySeq[Float]`. +There are also generic array wrappers that take elements of type `Array[T]` for arbitrary `T` to `ArraySeq`s. 1. An implicit conversion from `String` to `WrappedString`. The available high-priority implicits include definitions falling into the following categories. - * An implicit wrapper that adds `ensuring` methods - with the following overloaded variants to type `Any`. - - ```scala - def ensuring(cond: Boolean): A = { assert(cond); x } - def ensuring(cond: Boolean, msg: Any): A = { assert(cond, msg); x } - def ensuring(cond: A => Boolean): A = { assert(cond(x)); x } - def ensuring(cond: A => Boolean, msg: Any): A = { assert(cond(x), msg); x } - ``` - - * An implicit wrapper that adds a `->` method with the following implementation - to type `Any`. - - ```scala - def -> [B](y: B): (A, B) = (x, y) - ``` - - * For every array type with elements of primitive type, a wrapper that - takes the arrays of that type to instances of a `runtime.ArrayOps` - class. For instance, values of type `Array[Float]` can be implicitly - converted to instances of class `runtime.ArrayOps[Float]`. There are - also generic array wrappers that take elements of type `Array[T]` for - arbitrary `T` to `ArrayOps`s. - - * An implicit wrapper that adds `+` and `formatted` method with the following - implementations to type `Any`. - - ```scala - def +(other: String) = String.valueOf(self) + other - def formatted(fmtstr: String): String = fmtstr format self - ``` - - * Numeric primitive conversions that implement the transitive closure of the - following mappings: - - ``` - Byte -> Short - Short -> Int - Char -> Int - Int -> Long - Long -> Float - Float -> Double - ``` - - * Boxing and unboxing conversions between primitive types and their boxed - versions: - - ``` - Byte <-> java.lang.Byte - Short <-> java.lang.Short - Char <-> java.lang.Character - Int <-> java.lang.Integer - Long <-> java.lang.Long - Float <-> java.lang.Float - Double <-> java.lang.Double - Boolean <-> java.lang.Boolean - ``` - - * An implicit definition that generates instances of type `T <:< T`, for - any type `T`. Here, `<:<` is a class defined as follows. - - ```scala - sealed abstract class <:<[-From, +To] extends (From => To) - ``` - - Implicit parameters of `<:<` types are typically used to implement type constraints. +* An implicit wrapper that adds `ensuring` methods with the following overloaded variants to type `Any`. +```scala +def ensuring(cond: Boolean): A = { assert(cond); x } +def ensuring(cond: Boolean, msg: Any): A = { assert(cond, msg); x } +def ensuring(cond: A => Boolean): A = { assert(cond(x)); x } +def ensuring(cond: A => Boolean, msg: Any): A = { assert(cond(x), msg); x } +``` + +* An implicit wrapper that adds a `->` method with the following implementation to type `Any`. +```scala +def -> [B](y: B): (A, B) = (x, y) +``` + +* For every array type with elements of primitive type, a wrapper that takes the arrays of that type to instances of a `runtime.ArrayOps` class. +For instance, values of type `Array[Float]` can be implicitly converted to instances of class `runtime.ArrayOps[Float]`. +There are also generic array wrappers that take elements of type `Array[T]` for arbitrary `T` to `ArrayOps`s. + +* An implicit wrapper that adds `+` and `formatted` method with the following implementations to type `Any`. +```scala +def +(other: String) = String.valueOf(self) + other +def formatted(fmtstr: String): String = fmtstr format self +``` + +* Numeric primitive conversions that implement the transitive closure of the following mappings: +``` +Byte -> Short +Short -> Int +Char -> Int +Int -> Long +Long -> Float +Float -> Double +``` + +* Boxing and unboxing conversions between primitive types and their boxed versions: +``` +Byte <-> java.lang.Byte +Short <-> java.lang.Short +Char <-> java.lang.Character +Int <-> java.lang.Integer +Long <-> java.lang.Long +Float <-> java.lang.Float +Double <-> java.lang.Double +Boolean <-> java.lang.Boolean +``` + +* An implicit definition that generates instances of type `T <:< T`, for any type `T`. Here, `<:<` is a class defined as follows. +```scala +sealed abstract class <:<[-From, +To] extends (From => To) +``` +Implicit parameters of `<:<` types are typically used to implement type constraints. diff --git a/docs/_spec/13-syntax-summary.md b/docs/_spec/13-syntax-summary.md index e799a6f579b3..7c1d394bd4e1 100644 --- a/docs/_spec/13-syntax-summary.md +++ b/docs/_spec/13-syntax-summary.md @@ -88,8 +88,7 @@ semi ::= ‘;’ | nl {nl} ## Context-free Syntax -The context-free syntax of Scala is given by the following EBNF -grammar: +The context-free syntax of Scala is given by the following EBNF grammar: ```ebnf Literal ::= [‘-’] integerLiteral diff --git a/docs/_spec/README.md b/docs/_spec/README.md index e1c7e3497601..5aa9240c6158 100644 --- a/docs/_spec/README.md +++ b/docs/_spec/README.md @@ -39,6 +39,10 @@ and open http://0.0.0.0:4000/ to view the spec. Jekyll will rebuild as you edit - Use of the appropriate unicode characters instead of the latex modifiers for accents, etc. is necessary. For example, é instead of `\'e`. - MathJAX errors will appear within the rendered DOM as span elements with class `mtext` and style attribute `color: red` applied. It is possible to search for this combination in the development tools of the browser of your choice. In chrome, CTRL+F / CMD+F within the inspect element panel allows you to do this. +- This document follows the "one sentence <=> one line" convention, with the following exceptions below. +- - A multiline code bloc is part of the sentence +- - An enumeration of links is long enough + ### Macro replacements: - While MathJAX just support LaTeX style command definition, it is recommended to not use this as it will likely cause issues with preparing the document for PDF or ebook distribution. From 75d671fe392c406851924f062d679ee98cd2c372 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 16:54:18 +0200 Subject: [PATCH 107/657] Replace function by method wherever possible --- docs/_spec/01-lexical-syntax.md | 2 +- docs/_spec/03-types.md | 2 +- .../04-basic-declarations-and-definitions.md | 42 +++++++++---------- docs/_spec/05-classes-and-objects.md | 2 +- docs/_spec/06-expressions.md | 32 +++++++------- docs/_spec/08-pattern-matching.md | 2 +- docs/_spec/12-the-scala-standard-library.md | 2 +- 7 files changed, 42 insertions(+), 42 deletions(-) diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md index 032cf3703bb9..862e85a014be 100644 --- a/docs/_spec/01-lexical-syntax.md +++ b/docs/_spec/01-lexical-syntax.md @@ -234,7 +234,7 @@ A single new line token is accepted > (y: Int) = x + y > ``` > -> With an additional newline character, the same code is interpreted as an abstract function definition and a syntactically illegal statement: +> With an additional newline character, the same code is interpreted as an abstract method definition and a syntactically illegal statement: > > ```scala > def func(x: Int) diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index 664ddea49713..42157b176888 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -335,7 +335,7 @@ An argument type of the form ´\Rightarrow T´ represents a [call-by-name parame Function types associate to the right, e.g. ´S \Rightarrow T \Rightarrow U´ is the same as ´S \Rightarrow (T \Rightarrow U)´. -Function types are shorthands for class types that define `apply` functions. +Function types are shorthands for class types that define an `apply` method. Specifically, the ´n´-ary function type ´(T_1 , \ldots , T_n) \Rightarrow U´ is a shorthand for the class type `Function´_n´[´T_1´ , … , ´T_n´, ´U´]`. Such class types are defined in the Scala library for ´n´ between 0 and 22 as follows. diff --git a/docs/_spec/04-basic-declarations-and-definitions.md b/docs/_spec/04-basic-declarations-and-definitions.md index f9e55b15a59b..af98eef2a37d 100644 --- a/docs/_spec/04-basic-declarations-and-definitions.md +++ b/docs/_spec/04-basic-declarations-and-definitions.md @@ -183,7 +183,7 @@ VarDef ::= PatDef | ids ‘:’ Type ‘=’ ‘_’ ``` -A variable declaration `var ´x´: ´T´` is equivalent to the declarations of both a _getter function_ ´x´ *and* a _setter function_ `´x´_=`: +A variable declaration `var ´x´: ´T´` is equivalent to the declarations of both a _getter method_ ´x´ *and* a _setter method_ `´x´_=`: ```scala def ´x´: ´T´ @@ -215,9 +215,9 @@ The default value depends on the type ´T´ as follows: |`()` | `Unit` | |`null` | all other types | -When they occur as members of a template, both forms of variable definition also introduce a getter function ´x´ which returns the value currently assigned to the variable, as well as a setter function `´x´_=` which changes the value currently assigned to the variable. -The functions have the same signatures as for a variable declaration. -The template then has these getter and setter functions as members, whereas the original variable cannot be accessed directly as a template member. +When they occur as members of a template, both forms of variable definition also introduce a getter method ´x´ which returns the value currently assigned to the variable, as well as a setter method `´x´_=` which changes the value currently assigned to the variable. +The methods have the same signatures as for a variable declaration. +The template then has these getter and setter methods as members, whereas the original variable cannot be accessed directly as a template member. ###### Example @@ -287,7 +287,7 @@ A _type alias_ `type ´t´ = ´T´` defines ´t´ to be an alias name for the ty The left hand side of a type alias may have a type parameter clause, e.g. `type ´t´[´\mathit{tps}\,´] = ´T´`. The scope of a type parameter extends over the right hand side ´T´ and the type parameter clause ´\mathit{tps}´ itself. -The scope rules for [definitions](#basic-declarations-and-definitions) and [type parameters](#function-declarations-and-definitions) make it possible that a type name appears in its own bound or in its right-hand side. +The scope rules for [definitions](#basic-declarations-and-definitions) and [type parameters](#method-declarations-and-definitions) make it possible that a type name appears in its own bound or in its right-hand side. However, it is a static error if a type alias refers recursively to the defined type constructor itself. That is, the type ´T´ in a type alias `type ´t´[´\mathit{tps}\,´] = ´T´` may not refer directly or indirectly to the name ´t´. It is also an error if an abstract type is directly or indirectly its own upper or lower bound. @@ -346,7 +346,7 @@ VariantTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeParam TypeParam ::= (id | ‘_’) [TypeParamClause] [‘>:’ Type] [‘<:’ Type] [‘:’ Type] ``` -Type parameters appear in type definitions, class definitions, and function definitions. +Type parameters appear in type definitions, class definitions, and method definitions. In this section we consider only type parameter definitions with lower bounds `>: ´L´` and upper bounds `<: ´U´` whereas a discussion of context bounds `: ´U´` and view bounds `<% ´U´` is deferred to [here](07-implicits.html#context-bounds-and-view-bounds). The most general form of a proper type parameter is @@ -497,7 +497,7 @@ abstract class OutputChannel[-A] { With that annotation, we have that `OutputChannel[AnyRef]` conforms to `OutputChannel[String]`. That is, a channel on which one can write any object can substitute for a channel on which one can write only strings. -## Function Declarations and Definitions +## Method Declarations and Definitions ```ebnf Dcl ::= ‘def’ FunDcl @@ -515,23 +515,23 @@ ParamType ::= Type | Type ‘*’ ``` -A _function declaration_ has the form `def ´f\,\mathit{psig}´: ´T´`, where ´f´ is the function's name, ´\mathit{psig}´ is its parameter signature and ´T´ is its result type. -A _function definition_ `def ´f\,\mathit{psig}´: ´T´ = ´e´` also includes a _function body_ ´e´, i.e. an expression which defines the function's result. +A _method declaration_ has the form `def ´f\,\mathit{psig}´: ´T´`, where ´f´ is the method's name, ´\mathit{psig}´ is its parameter signature and ´T´ is its result type. +A _method definition_ `def ´f\,\mathit{psig}´: ´T´ = ´e´` also includes a _method body_ ´e´, i.e. an expression which defines the method's result. A parameter signature consists of an optional type parameter clause `[´\mathit{tps}\,´]`, followed by zero or more value parameter clauses `(´\mathit{ps}_1´)...(´\mathit{ps}_n´)`. Such a declaration or definition introduces a value with a (possibly polymorphic) method type whose parameter types and result type are as given. -The type of the function body is expected to [conform](06-expressions.html#expression-typing) to the function's declared result type, if one is given. -If the function definition is not recursive, the result type may be omitted, in which case it is determined from the packed type of the function body. +The type of the method body is expected to [conform](06-expressions.html#expression-typing) to the method's declared result type, if one is given. +If the method definition is not recursive, the result type may be omitted, in which case it is determined from the packed type of the method body. A _type parameter clause_ ´\mathit{tps}´ consists of one or more [type declarations](#type-declarations-and-type-aliases), which introduce type parameters, possibly with bounds. -The scope of a type parameter includes the whole signature, including any of the type parameter bounds as well as the function body, if it is present. +The scope of a type parameter includes the whole signature, including any of the type parameter bounds as well as the method body, if it is present. A _value parameter clause_ ´\mathit{ps}´ consists of zero or more formal parameter bindings such as `´x´: ´T´` or `´x: T = e´`, which bind value parameters and associate them with their types. ### Default Arguments Each value parameter declaration may optionally define a default argument. -The default argument expression ´e´ is type-checked with an expected type ´T'´ obtained by replacing all occurrences of the function's type parameters in ´T´ by the undefined type. +The default argument expression ´e´ is type-checked with an expected type ´T'´ obtained by replacing all occurrences of the method's type parameters in ´T´ by the undefined type. For every parameter ´p_{i,j}´ with a default argument a method named `´f\$´default´\$´n` is generated which computes the default argument expression. Here, ´n´ denotes the parameter's position in the method declaration. @@ -555,7 +555,7 @@ def compare´\$´default´\$´1[T]: Int = 0 def compare´\$´default´\$´2[T](a: T): T = a ``` -The scope of a formal value parameter name ´x´ comprises all subsequent parameter clauses, as well as the method return type and the function body, if they are given. +The scope of a formal value parameter name ´x´ comprises all subsequent parameter clauses, as well as the method return type and the method body, if they are given. Both type parameter names and value parameter names must be pairwise distinct. A default value which depends on earlier parameters uses the actual arguments if they are provided, not the default arguments. @@ -582,7 +582,7 @@ ParamType ::= ‘=>’ Type The type of a value parameter may be prefixed by `=>`, e.g. `´x´: => ´T´`. The type of such a parameter is then the parameterless method type `=> ´T´`. -This indicates that the corresponding argument is not evaluated at the point of function application, but instead is evaluated at each use within the function. +This indicates that the corresponding argument is not evaluated at the point of method application, but instead is evaluated at each use within the method. That is, the argument is evaluated using _call-by-name_. The by-name modifier is disallowed for parameters of classes that carry a `val` or `var` prefix, including parameters of case classes for which a `val` prefix is implicitly generated. @@ -654,11 +654,11 @@ FunDcl ::= FunSig FunDef ::= FunSig [nl] ‘{’ Block ‘}’ ``` -Special syntax exists for procedures, i.e. functions that return the `Unit` value `()`. -A _procedure declaration_ is a function declaration where the result type is omitted. +Special syntax exists for procedures, i.e. methods that return the `Unit` value `()`. +A _procedure declaration_ is a method declaration where the result type is omitted. The result type is then implicitly completed to the `Unit` type. E.g., `def ´f´(´\mathit{ps}´)` is equivalent to `def ´f´(´\mathit{ps}´): Unit`. -A _procedure definition_ is a function definition where the result type and the equals sign are omitted; its defining expression must be a block. +A _procedure definition_ is a method definition where the result type and the equals sign are omitted; its defining expression must be a block. E.g., `def ´f´(´\mathit{ps}´) {´\mathit{stats}´}` is equivalent to `def ´f´(´\mathit{ps}´): Unit = {´\mathit{stats}´}`. ###### Example @@ -686,8 +686,8 @@ object Terminal extends Writer { ### Method Return Type Inference -A class member definition ´m´ that overrides some other function ´m'´ in a base class of ´C´ may leave out the return type, even if it is recursive. -In this case, the return type ´R'´ of the overridden function ´m'´, seen as a member of ´C´, is taken as the return type of ´m´ for each recursive invocation of ´m´. +A class member definition ´m´ that overrides some other method ´m'´ in a base class of ´C´ may leave out the return type, even if it is recursive. +In this case, the return type ´R'´ of the overridden method ´m'´, seen as a member of ´C´, is taken as the return type of ´m´ for each recursive invocation of ´m´. That way, a type ´R´ for the right-hand side of ´m´ can be determined, which is then taken as the return type of ´m´. Note that ´R´ may be different from ´R'´, as long as ´R´ conforms to ´R'´. @@ -709,7 +709,7 @@ Here, it is OK to leave out the result type of `factorial` in `C`, even though t \label{sec:overloaded-defs} \todo{change} -An overloaded definition is a set of ´n > 1´ value or function +An overloaded definition is a set of ´n > 1´ value or method definitions in the same statement sequence that define the same name, binding it to types `´T_1 \commadots T_n´`, respectively. The individual definitions are called _alternatives_. Overloaded diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index d89fcfb115ff..622f9dc8ae59 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -603,7 +603,7 @@ The self constructor invocation must construct a generic instance of the class. I.e. if the class in question has name ´C´ and type parameters `[´\mathit{tps}\,´]`, then a self constructor invocation must generate an instance of `´C´[´\mathit{tps}\,´]`; it is not permitted to instantiate formal type parameters. The signature and the self constructor invocation of a constructor definition are type-checked and evaluated in the scope which is in effect at the point of the enclosing class definition, augmented by any type parameters of the enclosing class and by any [early definitions](#early-definitions) of the enclosing template. -The rest of the constructor expression is type-checked and evaluated as a function body in the current class. +The rest of the constructor expression is type-checked and evaluated as a method body in the current class. If there are auxiliary constructors of a class ´C´, they form together with ´C´'s primary [constructor](#class-definitions) an overloaded constructor definition. The usual rules for [overloading resolution](06-expressions.html#overloading-resolution) apply for constructor invocations of ´C´, including for the self constructor invocations in the constructor expressions themselves. diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index 597756550e6f..92697e64f0ac 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -190,7 +190,7 @@ Then we have: Note that the `superB` method returns different results depending on whether `B` is mixed in with class `Root` or `A`. -## Function Applications +## Method Applications ```ebnf SimpleExpr ::= SimpleExpr1 ArgumentExprs @@ -201,8 +201,8 @@ ArgumentExprs ::= ‘(’ [Exprs] ‘)’ Exprs ::= Expr {‘,’ Expr} ``` -An application `´f(e_1, ..., e_m)´` applies the function `´f´` to the argument expressions `´e_1, ..., e_m´`. -For this expression to be well-typed, the function must be *applicable* to its arguments, which is defined next by case analysis on ´f´'s type. +An application `´f(e_1, ..., e_m)´` applies the expression `´f´` to the argument expressions `´e_1, ..., e_m´`. +For the overal expression to be well-typed, ´f´ must be *applicable* to its arguments, which is defined next by case analysis on ´f´'s type. If ´f´ has a method type `(´p_1´:´T_1, ..., p_n´:´T_n´)´U´`, each argument expression ´e_i´ is typed with the corresponding parameter type ´T_i´ as expected type. Let ´S_i´ be the type of argument ´e_i´ ´(i = 1, ..., m)´. @@ -223,8 +223,8 @@ The value `´f´` is applicable to the given arguments if `´f´.apply` is appli The application `´f´(´e_1, ..., e_n´)` evaluates ´f´ and then each argument ´e_1, ..., e_n´ from left to right, except for arguments that correspond to a by-name parameter (see below). Each argument expression is converted to the type of its corresponding formal parameter. -After that, the application is rewritten to the function's right hand side, with actual arguments substituted for formal parameters. -The result of evaluating the rewritten right-hand side is finally converted to the function's declared result type, if one is given. +After that, the application is rewritten to the method's right hand side, with actual arguments substituted for formal parameters. +The result of evaluating the rewritten right-hand side is finally converted to the method's declared result type, if one is given. The case of a formal parameter with a parameterless method type `=> ´T´` is treated specially. In this case, the corresponding actual argument expression ´e´ is not evaluated before the application. @@ -232,7 +232,7 @@ Instead, every use of the formal parameter on the right-hand side of the rewrite In other words, the evaluation order for `=>`-parameters is _call-by-name_ whereas the evaluation order for normal parameters is _call-by-value_. Furthermore, it is required that ´e´'s [packed type](#expression-typing) conforms to the parameter type ´T´. The behavior of by-name parameters is preserved if the application is transformed into a block due to named or default arguments. -In this case, the local value for that parameter has the form `val ´y_i´ = () => ´e´` and the argument passed to the function is `´y_i´()`. +In this case, the local value for that parameter has the form `val ´y_i´ = () => ´e´` and the argument passed to the method is `´y_i´()`. The last argument in an application may be marked as a sequence argument, e.g. `´e´: _*`. Such an argument must correspond to a [repeated parameter](04-basic-declarations-and-definitions.html#repeated-parameters) of type `´S´*` and it must be the only argument matching this parameter (i.e. the number of formal parameters and actual arguments must be the same). @@ -243,7 +243,7 @@ When the application uses named arguments, the vararg parameter has to be specif If only a single argument is supplied, it may be supplied as a block expression and parentheses can be omitted, in the form `´f´ { block }`. This is valid when `f` has a single formal parameter or when all other formal parameters have default values. -A function application usually allocates a new frame on the program's run-time stack. +A method application usually allocates a new frame on the program's run-time stack. However, if a local method or a final method calls itself as its last action, the call is executed using the stack-frame of the caller. ###### Example @@ -304,7 +304,7 @@ The result of transforming ´f´ is a block of the form where every argument in ´(\mathit{args}\_1), ..., (\mathit{args}\_l)´ is a reference to one of the values ´x_1, ..., x_k´. To integrate the current application into the block, first a value definition using a fresh name ´y_i´ is created for every argument in ´e_1, ..., e_m´, which is initialised to ´e_i´ for positional arguments and to ´e'_i´ for named arguments of the form `´x_i=e'_i´`. -Then, for every parameter which is not specified by the argument list, a value definition using a fresh name ´z_i´ is created, which is initialized using the method computing the [default argument](04-basic-declarations-and-definitions.html#function-declarations-and-definitions) of this parameter. +Then, for every parameter which is not specified by the argument list, a value definition using a fresh name ´z_i´ is created, which is initialized using the method computing the [default argument](04-basic-declarations-and-definitions.html#method-declarations-and-definitions) of this parameter. Let ´\mathit{args}´ be a permutation of the generated names ´y_i´ and ´z_i´ such such that the position of each name matches the position of its corresponding parameter in the method type `(´p_1:T_1, ..., p_n:T_n´)´U´`. The final result of the transformation is a block of the form @@ -366,13 +366,13 @@ Note that a space is necessary between a method name and the trailing underscore SimpleExpr ::= SimpleExpr TypeArgs ``` -A _type application_ `´e´[´T_1, ..., T_n´]` instantiates a polymorphic value ´e´ of type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´S´` with argument types `´T_1, ..., T_n´`. +A _type application_ `´e´[´T_1, ..., T_n´]` instantiates a polymorphic method ´e´ of type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´S´` with argument types `´T_1, ..., T_n´`. Every argument type ´T_i´ must obey the corresponding bounds ´L_i´ and ´U_i´. That is, for each ´i = 1, ..., n´, we must have ´\sigma L_i <: T_i <: \sigma U_i´, where ´\sigma´ is the substitution ´[a_1 := T_1, ..., a_n := T_n]´. The type of the application is ´\sigma S´. -If the function part ´e´ is of some value type, the type application is taken to be equivalent to `´e´.apply[´T_1 , ...,´ T´_n´]`, i.e. the application of an `apply` method defined by ´e´. +If ´e´ is not a method, and is instead of some value type, the type application is taken to be equivalent to `´e´.apply[´T_1 , ...,´ T´_n´]`, i.e. the application of an `apply` method defined by ´e´. Type applications can be omitted if [local type inference](#local-type-inference) can infer best type parameters for a polymorphic method from the types of the actual method arguments and the expected result type. @@ -1146,12 +1146,12 @@ Otherwise, if ´e´ has method type ´()T´, it is implicitly applied to the emp ### Overloading Resolution If an identifier or selection ´e´ references several members of a class, the context of the reference is used to identify a unique member. -The way this is done depends on whether or not ´e´ is used as a function. +The way this is done depends on whether or not ´e´ is used as a method. Let ´\mathscr{A}´ be the set of members referenced by ´e´. Assume first that ´e´ appears as a function in an application, as in `´e´(´e_1´, ..., ´e_m´)`. -One first determines the set of functions that is potentially [applicable](#function-applications) based on the _shape_ of the arguments. +One first determines the set of methods that are potentially [applicable](#method-applications) based on the _shape_ of the arguments. The *shape* of an argument expression ´e´, written ´\mathit{shape}(e)´, is a type that is defined as follows: - For a function expression `(´p_1´: ´T_1, ..., p_n´: ´T_n´) => ´b´: (Any, ..., Any) => ´\mathit{shape}(b)´`, where `Any` occurs ´n´ times in the argument type. @@ -1159,7 +1159,7 @@ The *shape* of an argument expression ´e´, written ´\mathit{shape}(e)´, is - For a named argument `´n´ = ´e´`: ´\mathit{shape}(e)´. - For all other expressions: `Nothing`. -Let ´\mathscr{B}´ be the set of alternatives in ´\mathscr{A}´ that are [_applicable_](#function-applications) to expressions ´(e_1, ..., e_n)´ of types ´(\mathit{shape}(e_1), ..., \mathit{shape}(e_n))´. +Let ´\mathscr{B}´ be the set of alternatives in ´\mathscr{A}´ that are [_applicable_](#method-applications) to expressions ´(e_1, ..., e_n)´ of types ´(\mathit{shape}(e_1), ..., \mathit{shape}(e_n))´. If there is precisely one alternative in ´\mathscr{B}´, that alternative is chosen. Otherwise, let ´S_1, ..., S_m´ be the list of types obtained by typing each argument as follows. @@ -1197,7 +1197,7 @@ question: given so the method is not more specific than the value. --> -- A parameterized method ´m´ of type `(´p_1:T_1, ..., p_n:T_n´)´U´` is _as specific as_ some other member ´m'´ of type ´S´ if ´m'´ is [applicable](#function-applications) to arguments `(´p_1, ..., p_n´)` of types ´T_1, ..., T_n´. +- A parameterized method ´m´ of type `(´p_1:T_1, ..., p_n:T_n´)´U´` is _as specific as_ some other member ´m'´ of type ´S´ if ´m'´ is [applicable](#method-applications) to arguments `(´p_1, ..., p_n´)` of types ´T_1, ..., T_n´. - A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is as specific as some other member of type ´S´ if ´T´ is as specific as ´S´ under the assumption that for ´i = 1, ..., n´ each ´a_i´ is an abstract type name bounded from below by ´L_i´ and from above by ´U_i´. - A member of any other type is always as specific as a parameterized method or a polymorphic method. - Given two members of types ´T´ and ´U´ which are neither parameterized nor polymorphic method types, the member of type ´T´ is as specific as the member of type ´U´ if the existential dual of ´T´ conforms to the existential dual of ´U´. @@ -1220,12 +1220,12 @@ An alternative ´A´ is _more specific_ than an alternative ´B´ if the relativ It is an error if there is no alternative in ´\mathscr{CC}´ which is more specific than all other alternatives in ´\mathscr{CC}´. -Assume next that ´e´ appears as a function in a type application, as in `´e´[´\mathit{targs}\,´]`. +Assume next that ´e´ appears as a method in a type application, as in `´e´[´\mathit{targs}\,´]`. Then all alternatives in ´\mathscr{A}´ which take the same number of type parameters as there are type arguments in ´\mathit{targs}´ are chosen. It is an error if no such alternative exists. If there are several such alternatives, overloading resolution is applied again to the whole expression `´e´[´\mathit{targs}\,´]`. -Assume finally that ´e´ does not appear as a function in either an application or a type application. +Assume finally that ´e´ does not appear as a method in either an application or a type application. If an expected type is given, let ´\mathscr{B}´ be the set of those alternatives in ´\mathscr{A}´ which are [compatible](03-types.html#compatibility) to it. Otherwise, let ´\mathscr{B}´ be the same as ´\mathscr{A}´. In this last case we choose the most specific alternative among all alternatives in ´\mathscr{B}´. diff --git a/docs/_spec/08-pattern-matching.md b/docs/_spec/08-pattern-matching.md index 45d777f584a0..97fb73d58b06 100644 --- a/docs/_spec/08-pattern-matching.md +++ b/docs/_spec/08-pattern-matching.md @@ -474,7 +474,7 @@ The expected type of the pattern `y: Number` is `Term[B]`. The type `Number` does not conform to `Term[B]`; hence Case 2 of the rules above applies. This means that `B` is treated as another type variable for which subtype constraints are inferred. In our case the applicable constraint is `Number <: Term[B]`, which entails `B = Int`. Hence, `B` is treated in the case clause as an abstract type with lower and upper bound `Int`. -Therefore, the right hand side of the case clause, `y.n`, of type `Int`, is found to conform to the function's declared result type, `Number`. +Therefore, the right hand side of the case clause, `y.n`, of type `Int`, is found to conform to the method's declared result type, `Number`. ## Pattern Matching Expressions diff --git a/docs/_spec/12-the-scala-standard-library.md b/docs/_spec/12-the-scala-standard-library.md index 086d396ba4dd..401735286a51 100644 --- a/docs/_spec/12-the-scala-standard-library.md +++ b/docs/_spec/12-the-scala-standard-library.md @@ -529,7 +529,7 @@ trait Node { ## The `Predef` Object -The `Predef` object defines standard functions and type aliases for Scala programs. +The `Predef` object defines standard methods and type aliases for Scala programs. It is implicitly imported, as described in [the chapter on name binding](02-identifiers-names-and-scopes.html), so that all its defined members are available without qualification. Its definition for the JVM environment conforms to the following signature: From ac6fcafdcd35deddaf5e11319c172c5641904838 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 3 Feb 2023 15:11:31 +0100 Subject: [PATCH 108/657] Update readme --- docs/_spec/README.md | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/_spec/README.md b/docs/_spec/README.md index 5aa9240c6158..b9eba413f8a2 100644 --- a/docs/_spec/README.md +++ b/docs/_spec/README.md @@ -1,4 +1,6 @@ -# Scala Language Reference +# WIP Scala 3 Language Specification + +**This is still a work in progress, and should *not* be regarded as a source of truth.** First of all, the language specification is meant to be correct, precise and clear. @@ -16,9 +18,6 @@ We aim to track the configuration GitHub Pages uses but differences may arise as ## Building - -Travis CI builds the spec automatically after every merged pull release and publishes to https://www.scala-lang.org/files/archive/spec/2.13/. - To preview locally, run the following commands in the docs/_spec subfolder: @@ -40,8 +39,16 @@ and open http://0.0.0.0:4000/ to view the spec. Jekyll will rebuild as you edit - MathJAX errors will appear within the rendered DOM as span elements with class `mtext` and style attribute `color: red` applied. It is possible to search for this combination in the development tools of the browser of your choice. In chrome, CTRL+F / CMD+F within the inspect element panel allows you to do this. - This document follows the "one sentence <=> one line" convention, with the following exceptions below. -- - A multiline code bloc is part of the sentence -- - An enumeration of links is long enough + - A multiline code block is part of the sentence + - An enumeration of links is long enough + +- Whenever doing an enumeration of the kind "a, ..., z", follow the following conventions: + - It should always be "separator whitespace period period period separator whitespace", for example `, ..., ` or `,\n...,\n` for multiline. + - If in a code block, only the elements (a and z above) should be in math mode (between forward ticks) + - If in a math expression, the whole thing should be in a single math mode + - Look at the [Tuple Types section](docs/_spec/03-types.html#tuple-types) for an example of the different cases above. + +- Try to use "Note" blocks to point out logical conclusions that are not obvious, for examples, look at the [Tuple Types section](docs/_spec/03-types.html#tuple-types). ### Macro replacements: @@ -51,7 +58,7 @@ and open http://0.0.0.0:4000/ to view the spec. Jekyll will rebuild as you edit - As MathJAX has no support for slanted font (latex command \sl), so in all instances this should be replaced with \mathit{} - The macro \U{ABCD} used for unicode character references can be replaced with \\uABCD. - The macro \URange{ABCD}{DCBA} used for unicode character ranges can be replaced with \\uABCD-\\uDBCA. -- The macro \commadots can be replaced with ` , … , `. +- The macro \commadots can be replaced with ` , … , ` (But should not, see above). - There is no adequate replacement for `\textsc{...}` (small caps) in pandoc markdown. While unicode contains a number of small capital letters, it is notably missing Q and X as these glyphs are intended for phonetic spelling, therefore these cannot be reliably used. For now, the best option is to use underscore emphasis and capitalise the text manually, `_LIKE THIS_`. ### Unicode Character replacements From ec261b64f50b77d69d1ad4c57b05aeb9e4691c7d Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 3 Feb 2023 15:36:31 +0100 Subject: [PATCH 109/657] Remove travis key --- docs/_spec/id_dsa_travis.enc | 68 ------------------------------------ 1 file changed, 68 deletions(-) delete mode 100644 docs/_spec/id_dsa_travis.enc diff --git a/docs/_spec/id_dsa_travis.enc b/docs/_spec/id_dsa_travis.enc deleted file mode 100644 index 6709463580af..000000000000 --- a/docs/_spec/id_dsa_travis.enc +++ /dev/null @@ -1,68 +0,0 @@ -U2FsdGVkX19ePRmShLaiBw8T+ZZjbrD7zejYuQmDFA3U6/CSCjOzJLrQSBViWwH5 -/0BvyYdva00SW9g+soQfXShHlnJUz89ZpQj2Z82ipnebtcgy20jnlsNUdo0FG2aG -tAD3OUNxY+IsCeM7tvym955x7TGjDreygZfMUcVibJNZfk3MKPu1uF7xBD800hQE -1eW21bE2bUZeIMPY3t7ZIIqAH+RbYOir0O/XKoxhdTVgpXDE3ntaGIvLr/rleIyT -nsE5UN5XNP/ONj7hsK3kSSoDHujQ5TxvhF60IGJyXksJEBtM1ZirMG20/SKPuT9C -5ROkA3lOMNFkYiSiQiy4c6uU0ynSdkZ22xiJX6d+qvyhybZsBJhSo4ksE5XbwOjX -0QJ6pro5IT+dq/KQadzlGv/27+trc3Dvf5lnxlYZ0vZDx81/dwFUI0VVLF4CBIo5 -4KBH/b/2lOAkVB9sNpJZoutMh9c4ay6h0rAJC7BzXFxMZSKvDhJmjEUzVDGTgOny -cv6Tpabf/pC+KtqlxQoVq4JTfcGB/TPt7gKE87E4fIUPcBZ36A6NH2slbzNCBuSQ -4h5t2C7e/WPPCFVL5Q+0usLdUaMUoaKeKpDK/LecbOUKcdYfYhoSlgV23ApllsES -YLk9Ldl7sbUx9pVT/suI61CGs/3AVMjKq/l5wemM5T9Y7LYYK1TirEvRL2yZy9Eq -OnCWPA/2j9u13O4ZahHJ+JPp/eQXjPlt++IRk0mF5Ua1mKHWJIFr10SXKy9W/2n8 -b8BVnAaFTdv99vgRTjb0Ic5fYivEzvas/yxv7rA5d/LQ5oLNJrhzOnkQvzFzjGI6 -0N6vBV+1BDFQsnz0vBR7gzV+KhQenVIbyFQsxpp4pzP1N1/QZ/qujD6IiAG3H/DG -kLJc7UO3EfdQ6yRvGYVFsZ4GBBAjtD0y+lUIG7q6y57FvHKKvN+4Y7Yy5IUlfpUE -Y4hu5k4oAY9w0EkyOZsSGLMtwZhaLUX/jWgTjXoe8+pp234DIaSBWCsVqj5PHTnZ -6v1fdmpM+rXS4mFpJnegmmv0WG8l4Fk8QfUExxn5VgXkoAPFhYWDFNiFJw4ZRsBX -2KsnUyi4X0UYjAhVya6JVDMD1l42RskZFXKynTaXSXT+fS1zUZlcpXVy9qdOng7Z -UZrMixTt3kFcw2WDULpHqpbmqbbSXqoErZOSquKzAJw0AO81otsSFiCvb7q05Uw6 -Y6mWmPrIYDZJ78CraA1HPtDvmRW0jRes8kQ6BUhHeVZuS8bGvUSzwEwnzUtaF+Gz -XESJ2wKWVRHqyzbY48UHNllYQfReXV1xQ5t6mQeAVYa+sRRBFSXW4QFwIAxjuB2x -0+yBCzoP0DL8hqtrlYOf4ycQvb0xXq+CajTFzO1lD3tzonq9cM8BsAHXaTYyIYd1 -GtaMxhXfEDKAwaB1t9LHfvV31BGvDv+4XVNAc8ru/wVAxbNptVfstkBj1W5Ns7to -W8AmPKASAFddbkbPCYiVVNQnsa+RC9YPu87QQJcD+BiszPGz2LG0L5EOAiis5uFL -e6S3Lvbx2yomW5CaFOOd36zqB6DJmMDsWluHNzrX8BPUeSEcHfqMrM5WV4HhtTsU -c7Rs5fY0uA/J8VBVGUHLBodW4jnRJAVlCXsncRgj50cATwRnhQpCeRAjMsB7NSsF -Fo1wyMz9k/w69efqJKwX3R29zP3bCTTt36FdQ+uB37L+Fgek6FHUpjL8M0dGB4Tc -5y+YO9eUmzs7Lkl6AHEf4rM14yzL/ETJWP+IBjPg9Np3y6auiKKZ5thXSWmCUD05 -HBoBsX1Fk88NpfDzp2mTzFWDm2XWNnBzcSZn8jSLcSj6KAB3j8myun1u5Ah5M3gd -vgm23GoohOODwzhaZzQbFA8J7aVTXQkzshmqtfjLpg4QtpXThgSTZ7pij/UW4pKU -fMEQAFi7uu2mrkQK+V6H/USCL63bpcALSskXrXZkGMOxJZfA1ND6GCKWQPZeVi1h -O3mgbVsRhRZ8F1rGySwP/Z9D0IWQRBotuVWh4rHqoYzafpO1QjVRm/yfVOSbr9yB -ObEFLIv6c+Eu4I8FbM839qLUiufv+8tUsHzVjN+zP14KwDaiVMC3Y56YQp4wqR2B -IAufYraqoP+q8wmFiux9hPDy857sgyXqdPIQy+p0yNuUJl8ZlQYzCgPhNx4pE1P5 -YOoNJ9AsaN0CMI82M6A8thjPLZfFZ+6Nt8jdBipmMe0APq3wfb9NPWlVx3sh0/Bp -cF3y3xQRgRBk8Twq3Imol2cFCsYu8cQNyPxKCQG/NHKVUffXiUoFsBCvg8oGYU0s -mew25XAx9iZ7+/JC0dmeMQ2xOF9dKPnIhcM5rVt8WSFX4IxTawpUAQlN4N6rHFfx -/w2WHInL34zpBDTQqKUWC+AdxVMc9Is8X1Zpv+GoBv3LEHt8GNKRFG6HmTW6sz+v -0aHbvT2jU1iWqDf9icL29MRT0nXuzoZN0Nf69RBjvnTh35gE8r7y5URaBVI0mZkU -ZL5Fohc2mLmzR7Te8B6/eAdov8nkeLPg5CDkq1T7O/R8hpiHGncfkaqbZTv0oUdN -1Hu9Kt12aakem647KnfdhsWifzMv3nY6uT082iXbI9uXvh1WLMp1HZkTeUvARAan -i/VgiO/0+BBTv/XywpJphsy4UfOJ5cTbg2FWQ8f/DsJMqlbsBQeqD+G9j9b7W2Eg -f3XdvLoWhwR0uLCeHyAA+wfzogltXRxavX8c9wmzbkl8X1fYN6aiPJRr45lenvqS -4n2PAj2qX23n8+sI9iH1Af026Nrb/Kvbo9f/gfaQj2Z2WXiGIT0/RGH4Mz3V/mvD -sNKvSVzQ5VEkvxcMtXmkC7AJBYOKSyv0Vp/2ySzltxkghvBrrbq/RX9Cr2iJBZJN -RrqfefkT8A7vOI+YSjNRTIrRHTc/UfX+nZldzCPfeh3lU2eKUkappZHsGXda++uN -K7mMrXNoy4yCd5YNTCeLiQorklGxzeCCtoa8C+gDSbJY7HtjkvwbeaXUi7CpNPa3 -0GBPe5bQcK+vsynVgHnGU8qH4VOE7dgDWMjUi5+IdGC26zcsM6VvMArfk93Ny3xX -5AS61/4oMKBAedxVQejMD/xUVdjf6x0U+6gKGIlZFyiNl4kPtY+o0Ok7BkFItsDn -sC9dKRlwrvQlI6uNE3+Rk6R5rQLX1EW4UBnOL7YOOWLypiviB0DKas8FTL6RzWfl -TeZRwDS7nYWXYHSBvexEfDnEbv4Xnncz0gQ42ixmTXWVNNGcS8mNLR8GKpQGKEX7 -t6Bub1GZd8EsdVDkG1EUU3qwk6fx4PgGfqxZ+MgrZOlXhYbHmJTE83IeuYfBbAb3 -2MxbOhYmENigWNRf5S9vRkMr254xDJ1eIAAE3FHqeW1fEPbrHy8M1AS1DKlEoNMI -yW2lcOP0HAuib4sLXTqa8d00h7qiClyy3NCtPwKyUganSzSIKOMO7G+Bbf6gJfhN -VBr58/nj8aUZzKCdJO5U1Hou6/fUPnTltyURrfbe/B0RpMCCoUNcwpfT0VltOEDa -4pDD3Z9lnejSmCKplbWLvEWVPi4muNXg9E08cTnolqQIx0zWTMMZkhmzq3z3hKh9 -F1uLWaZd/dzyIxkHVTujKfyEaOmFH+MDzquHoJFaXtlK2220ARSlTgEBUHfICesA -dtXDw/ipuUCy5GAloUWZDJGz8DwCWBwsl/pN+oXq0SK0kZXcjCn04l/LVikAJjUK -fcAlg3SAkwXW17pocvOfxCF6cBJBcNYi74V5n5GSW0entbx4J3ki4UpEI0OQFGEJ -9alenvjUqJGHRGLjMdhv0YjNX15Ww/eAaBFlm19z7Uf02EuTDx4RuxyODGn/oYUa -NXB0obcO2t9ZLj1KrAgY4mseerdY3jJeh2fk6g2Unbo+RDMtB1fMcyaP2ApCxlZg -GVRYULd8shdCKQTg/5eUcNvVpE66m1EyfreE9XZBLwf35O7Bb1t1Aj56gWHg2raS -gLsdecV+7dDSMm71QNNhLreo1iQ6uKKRM5KATHCbvSzeYSTwGNOzXHYBjEC48RpR -nGn8qNT1s7Ddl6W8/kABN0L3i4dNgAIE10AuJuaJGukr0Wiv/aWookD/lGgB6SlS -EJOqZks1YQC/7gLgYIiYnL1iphHonLclqh+GHCqEONPfql7XwonawtNnPYvGVz20 -XynW1kKiF05CPWsolLhgOj8F4eVeTFEG5qPfELZeK0ADxIkbpWOXnYUWXLn59gby -sdijsfJtmWh5aaESy5iEfBTedGaX60+AntTwoN0ncXuseDorwEo3DrUuObjCi5wL -vhxedV446Do4PEEinUV499CGrMlc+lB2UEn5lJ2Fi1uhakbvhhTLL3zgmhaNlr0u From 9db5c9e66d05a6f6d0a75aa7ca1cd94914ec1d57 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 6 Feb 2023 15:26:17 +0100 Subject: [PATCH 110/657] install correct sass-embedded --- docs/_spec/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/_spec/Dockerfile b/docs/_spec/Dockerfile index a08c5d0e7f98..1fc28081c59f 100644 --- a/docs/_spec/Dockerfile +++ b/docs/_spec/Dockerfile @@ -5,7 +5,9 @@ RUN apt-get install -y curl \ && apt-get install -y nodejs \ && curl -L https://www.npmjs.com/install.sh | sh -RUN gem install bundler:1.17.2 jekyll +RUN gem update --system +RUN gem install sass-embedded -v 1.58.0 +RUN gem install bundler:1.17.2 jekyll WORKDIR /srv/jekyll From 1f5a99013fb172348f0bc0b4dcc77db940e79aea Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 6 Feb 2023 16:33:58 +0100 Subject: [PATCH 111/657] Propagate implicit search errors from implicit macros Fixes partially #16835 --- .../dotty/tools/dotc/transform/Splicer.scala | 10 ++++++++- .../dotty/tools/dotc/typer/Implicits.scala | 16 +++++++++++--- tests/neg-macros/i16835.check | 6 ++++++ tests/neg-macros/i16835/Macro_1.scala | 21 +++++++++++++++++++ tests/neg-macros/i16835/Test_2.scala | 1 + 5 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 tests/neg-macros/i16835.check create mode 100644 tests/neg-macros/i16835/Macro_1.scala create mode 100644 tests/neg-macros/i16835/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index b936afb73dc8..3d3cd1f41c84 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -119,7 +119,7 @@ object Splicer { sym.exists && !sym.is(Package) && sym.owner.ownersIterator.exists(x => x == expansionOwner || // symbol was generated within this macro expansion - x.is(Macro, butNot = Method) && x.name == nme.MACROkw // symbol was generated within another macro expansion + isMacroOwner(x) // symbol was generated within another macro expansion ) && !locals.contains(sym) // symbol is not in current scope }.traverse(tree) @@ -222,6 +222,14 @@ object Splicer { checkIfValidStaticCall(tree)(using Set.empty) } + /** Is this the dummy owner of a macro expansion */ + def isMacroOwner(sym: Symbol)(using Context): Boolean = + sym.is(Macro, butNot = Method) && sym.name == nme.MACROkw + + /** Is this the dummy owner of a macro expansion */ + def inMacroExpansion(using Context) = + ctx.owner.ownersIterator.exists(isMacroOwner) + /** Tree interpreter that evaluates the tree. * Interpreter is assumed to start at quotation level -1. */ diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 20859b8c9d25..7d185d335218 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -31,6 +31,7 @@ import Feature.migrateTo3 import config.Printers.{implicits, implicitsDetailed} import collection.mutable import reporting._ +import transform.Splicer import annotation.tailrec import scala.annotation.internal.sharable @@ -567,6 +568,12 @@ object Implicits: def msg(using Context) = em"Failed to synthesize an instance of type ${clarify(expectedType)}:${formatReasons}" + class MacroErrorsFailure(errors: List[Diagnostic.Error], + val expectedType: Type, + val argument: Tree) extends SearchFailureType { + def msg(using Context): Message = + em"${errors.map(_.msg).mkString("\n")}" + } end Implicits import Implicits._ @@ -1157,19 +1164,22 @@ trait Implicits: if ctx.reporter.hasErrors || !cand.ref.symbol.isAccessibleFrom(cand.ref.prefix) then - ctx.reporter.removeBufferedMessages - adapted.tpe match { + val res = adapted.tpe match { case _: SearchFailureType => SearchFailure(adapted) case error: PreviousErrorType if !adapted.symbol.isAccessibleFrom(cand.ref.prefix) => SearchFailure(adapted.withType(new NestedFailure(error.msg, pt))) - case _ => + case tpe => // Special case for `$conforms` and `<:<.refl`. Showing them to the users brings // no value, so we instead report a `NoMatchingImplicitsFailure` if (adapted.symbol == defn.Predef_conforms || adapted.symbol == defn.SubType_refl) NoMatchingImplicitsFailure + else if Splicer.inMacroExpansion && tpe <:< pt then + SearchFailure(adapted.withType(new MacroErrorsFailure(ctx.reporter.allErrors.reverse, pt, argument))) else SearchFailure(adapted.withType(new MismatchedImplicit(ref, pt, argument))) } + ctx.reporter.removeBufferedMessages + res else SearchSuccess(adapted, ref, cand.level, cand.isExtension)(ctx.typerState, ctx.gadt) } diff --git a/tests/neg-macros/i16835.check b/tests/neg-macros/i16835.check new file mode 100644 index 000000000000..fb02f3c7f13f --- /dev/null +++ b/tests/neg-macros/i16835.check @@ -0,0 +1,6 @@ + +-- Error: tests/neg-macros/i16835/Test_2.scala:1:17 -------------------------------------------------------------------- +1 |def test: Unit = foo // error + | ^^^ + | my error + | my second error diff --git a/tests/neg-macros/i16835/Macro_1.scala b/tests/neg-macros/i16835/Macro_1.scala new file mode 100644 index 000000000000..ddee5dbecb4e --- /dev/null +++ b/tests/neg-macros/i16835/Macro_1.scala @@ -0,0 +1,21 @@ +import scala.quoted.* + +class Bar + +inline def foo: Unit = ${ fooExpr } + +def fooExpr(using Quotes): Expr[Unit] = + import quotes.reflect.* + Implicits.search(TypeRepr.of[Bar]) match + case res: ImplicitSearchSuccess => '{} + case failure: ImplicitSearchFailure => + report.errorAndAbort(failure.explanation) + + +inline given bar: Bar = ${ barExpr } + +def barExpr(using Quotes): Expr[Bar] = + import quotes.reflect.* + report.error(s"my error") + report.error(s"my second error") + '{ new Bar } diff --git a/tests/neg-macros/i16835/Test_2.scala b/tests/neg-macros/i16835/Test_2.scala new file mode 100644 index 000000000000..0dc2d39d6c3d --- /dev/null +++ b/tests/neg-macros/i16835/Test_2.scala @@ -0,0 +1 @@ +def test: Unit = foo // error From 790af81c1d1f8905a2f0707fd14bb7f8708b3d6d Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Mon, 6 Feb 2023 23:06:41 +0100 Subject: [PATCH 112/657] Simplify and document box adaptation --- .../dotty/tools/dotc/cc/CheckCaptures.scala | 63 ++++++++----------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 077d345d792d..77363a165f64 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -656,7 +656,7 @@ class CheckCaptures extends Recheck, SymTransformer: expected /** Adapt `actual` type to `expected` type by inserting boxing and unboxing conversions - * + * * @param alwaysConst always make capture set variables constant after adaptation */ def adaptBoxed(actual: Type, expected: Type, pos: SrcPos, alwaysConst: Boolean = false)(using Context): Type = @@ -721,61 +721,52 @@ class CheckCaptures extends Recheck, SymTransformer: val arrow = if covariant then "~~>" else "<~~" i"adapting $actual $arrow $expected" - /** Destruct a capturing type `tp` to a tuple (cs, tp0, boxed), - * where `tp0` is not a capturing type. - * - * If `tp` is a nested capturing type, the return tuple always represents - * the innermost capturing type. The outer capture annotations can be - * reconstructed with the returned function. - */ - def destructCapturingType(tp: Type, reconstruct: Type => Type = x => x): ((Type, CaptureSet, Boolean), Type => Type) = - tp.dealias match - case tp @ CapturingType(parent, cs) => - if parent.dealias.isCapturingType then - destructCapturingType(parent, res => reconstruct(tp.derivedCapturingType(res, cs))) - else - ((parent, cs, tp.isBoxed), reconstruct) - case actual => - val res = if tp.isFromJavaObject then tp else actual - ((res, CaptureSet(), false), reconstruct) - def adapt(actual: Type, expected: Type, covariant: Boolean): Type = trace(adaptInfo(actual, expected, covariant), recheckr, show = true) { if expected.isInstanceOf[WildcardType] then actual else - val ((parent, cs, actualIsBoxed), recon) = destructCapturingType(actual) - - val needsAdaptation = actualIsBoxed != expected.isBoxedCapturing - val insertBox = needsAdaptation && covariant != actualIsBoxed - - val (parent1, cs1) = parent match { + // Decompose the actual type into the inner shape type, the capture set and the box status + val styp = if actual.isFromJavaObject then actual else actual.stripCapturing + val cs = actual.captureSet + val boxed = actual.isBoxedCapturing + + // A box/unbox should be inserted, if the actual box status mismatches with the expectation + val needsAdaptation = boxed != expected.isBoxedCapturing + // Whether to insert a box or an unbox? + val insertBox = needsAdaptation && covariant != boxed + + // Adapt the inner shape type: get the adapted shape type, and the capture set leaked during adaptation + val (styp1, leaked) = styp match { case actual @ AppliedType(tycon, args) if defn.isNonRefinedFunction(actual) => - val (parent1, leaked) = adaptFun(parent, args.init, args.last, expected, covariant, insertBox, + adaptFun(actual, args.init, args.last, expected, covariant, insertBox, (aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1)) - (parent1, leaked ++ cs) case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(actual) => // TODO Find a way to combine handling of generic and dependent function types (here and elsewhere) - val (parent1, leaked) = adaptFun(parent, rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox, + adaptFun(actual, rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox, (aargs1, ares1) => rinfo.derivedLambdaType(paramInfos = aargs1, resType = ares1) .toFunctionType(isJava = false, alwaysDependent = true)) - (parent1, leaked ++ cs) case actual: MethodType => - val (parent1, leaked) = adaptFun(parent, actual.paramInfos, actual.resType, expected, covariant, insertBox, + adaptFun(actual, actual.paramInfos, actual.resType, expected, covariant, insertBox, (aargs1, ares1) => actual.derivedLambdaType(paramInfos = aargs1, resType = ares1)) - (parent1, leaked ++ cs) case actual @ RefinedType(p, nme, rinfo: PolyType) if defn.isFunctionOrPolyType(actual) => - val (parent1, leaked) = adaptTypeFun(parent, rinfo.resType, expected, covariant, insertBox, + adaptTypeFun(actual, rinfo.resType, expected, covariant, insertBox, ares1 => val rinfo1 = rinfo.derivedLambdaType(rinfo.paramNames, rinfo.paramInfos, ares1) val actual1 = actual.derivedRefinedType(p, nme, rinfo1) actual1 ) - (parent1, leaked ++ cs) case _ => - (parent, cs) + (styp, CaptureSet()) } + // Capture set of the term after adaptation + val cs1 = cs ++ leaked + + // Compute the adapted type + def adaptedType(resultBoxed: Boolean) = + styp1.capturing(if alwaysConst then CaptureSet(cs1.elems) else cs1).forceBoxStatus(resultBoxed) + if needsAdaptation then val criticalSet = // the set which is not allowed to have `*` if covariant then cs1 // can't box with `*` @@ -797,9 +788,9 @@ class CheckCaptures extends Recheck, SymTransformer: } if !insertBox then // unboxing markFree(criticalSet, pos) - recon(CapturingType(parent1, if alwaysConst then CaptureSet(cs1.elems) else cs1, !actualIsBoxed)) + adaptedType(!boxed) else - recon(CapturingType(parent1, if alwaysConst then CaptureSet(cs1.elems) else cs1, actualIsBoxed)) + adaptedType(boxed) } var actualw = actual.widenDealias From 31bf9578954aa07e32fc4a5029ab0a734b10bc64 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Tue, 7 Feb 2023 11:57:24 +0100 Subject: [PATCH 113/657] docs: update CONTRIBUTING.md to point to contributing guide This is a follow-up of https://github.com/scala/docs.scala-lang/pull/2692. In that PR I went ahead and migrated all of the information contained in this `CONTRIBUTING.md` to the actual guide. Now we just point there to ensure everything is in one place. --- CONTRIBUTING.md | 63 ++++--------------------------------------------- 1 file changed, 4 insertions(+), 59 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3267d1f02700..48206fd67b3c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,60 +1,5 @@ -# Dotty Developer Guidelines +# Contributing to Dotty -These guidelines are meant to be a living document that should be changed and adapted as needed. We encourage changes that make it easier to achieve our goals in an efficient way. - -## General Workflow - -This is the process for committing code to the Scala project. There are of course exceptions to these rules, for example minor changes to comments and documentation, fixing a broken build etc. - -1. Make sure you have signed the [Scala CLA](https://www.lightbend.com/contribute/cla/scala), if not, sign it. -2. Before starting to work on a feature or a fix, it's good practice to ensure that: - 1. There is a ticket for your work in the project's [issue tracker](https://github.com/lampepfl/dotty/issues); - 2. The ticket has been discussed and prioritized by the team. -3. You should always perform your work in its own Git branch. The branch should be given a descriptive name that explains its intent. Some teams also like adding the ticket number and/or the [GitHub](http://github.com) user ID to the branch name, these details is up to each of the individual teams. (See below for more details on branch naming.) -4. When the feature or fix is completed you should open a [Pull Request](https://help.github.com/articles/using-pull-requests) on GitHub. -5. The Pull Request should be reviewed by other maintainers (as many as feasible/practical). Note that a reviewer can also be an outside contributor—members of Typesafe or VirtusLab and independent contributors are encouraged to participate in the review process. It is not a closed process. Please try to avoid conflict of interest—the spirit of the review process is to evenly distribute the understanding of our code base across its maintainers as well as to load balance quality assurance. Assigning a review to a "sure win" reviewer is not a good long-term solution. -6. After the review, you should resolve issues brought up by the reviewers as needed (pushing a new commit to address reviewers' comments), iterating until the reviewers give their thumbs up, the "LGTM" (acronym for "Looks Good To Me"). -7. Once the code has passed review the Pull Request can be merged into the distribution. - -## Pull Request Requirements - -In order for a Pull Request to be considered, it has to meet these requirements: - -1. Live up to the current code standard: - - Not violate [DRY](https://www.oreilly.com/library/view/97-things-every/9780596809515/ch30.html). - - [Boy Scout Rule](https://www.oreilly.com/library/view/97-things-every/9780596809515/ch08.html) should be applied. -2. Tests are of paramount importance. -3. The code must be well documented in the project's standard documentation format (see the ‘Documentation’ section below). - -If *all* of these requirements are not met then the code should **not** be merged into the distribution, and need not even be reviewed. - -## Documentation - -All contributed code should come accompanied by documentation. Pull requests containing undocumented code will not be accepted. Both user-facing Scaladoc comments, as well as committer-facing internal documentation (i.e. essential design decisions that other maintainers should know about should be placed inline with line comments `//`) should be accompanying all contributed code where possible. - - -## Work In Progress - -It is ok to work on a public feature branch in the GitHub repository. Something that can sometimes be useful for early feedback etc. If so, then it is preferable to name the branch accordingly. This can be done by either prefixing the name with ``wip-`` as in ‘Work In Progress’, or use hierarchical names like ``wip/..``, ``feature/..`` or ``topic/..``. Either way is fine as long as it is clear that it is work in progress and not ready for merge. This work can temporarily have a lower standard. However, to be merged into master it will have to go through the regular process outlined above, with Pull Request, review etc.. - -Also, to facilitate both well-formed commits and working together, the ``wip`` and ``feature``/``topic`` identifiers also have special meaning. Any branch labeled with ``wip`` is considered “git-unstable” and may be rebased and have its history rewritten. Any branch with ``feature``/``topic`` in the name is considered “stable” enough for others to depend on when a group is working on a feature. - -## Creating Commits And Writing Commit Messages - -Follow these guidelines when creating public commits and writing commit messages. - -1. If your work spans multiple local commits (for example; if you do safe point commits while working in a feature branch or work in a branch for long time doing merges/rebases etc.) then please do not commit it all but rewrite the history by squashing the commits into one large commit which is accompanied by a detailed commit message for (as discussed in the following sections). For more info, see the article: [Git Workflow](http://sandofsky.com/blog/git-workflow.html). Additionally, every commit should be able to be used in isolation—that is, each commit must build and pass all tests. -2. The first line should be a descriptive sentence about what the commit is doing. It should be possible to fully understand what the commit does by just reading this single line. It is **not ok** to only list the ticket number, type "minor fix" or similar. If the commit has a corresponding ticket, include a reference to the ticket number, prefixed with "Closes #", at the beginning of the first line followed by the title of the ticket, assuming that it aptly and concisely summarizes the commit in a single line. If the commit is a small fix, then you are done. If not, go to 3. -3. Following the single line description (ideally no more than 70 characters long) should be a blank line followed by an enumerated list with the details of the commit. -4. Add keywords for your commit (depending on the degree of automation we reach, the list may change over time): - * ``Review by @githubuser`` - will notify the reviewer via GitHub. Everyone is encouraged to give feedback, however. (Remember that @-mentions will result in notifications also when pushing to a WIP branch, so please only include this in your commit message when you're ready for your pull request to be reviewed. Alternatively, you may request a review in the pull request's description.) - * ``Fix/Fixing/Fixes/Close/Closing/Refs #ticket`` - if you want to mark the ticket as fixed in the issue tracker (Assembla understands this). - * ``backport to _branch name_`` - if the fix needs to be cherry-picked to another branch (like 2.9.x, 2.10.x, etc) - -Example: - - Closes #2 Fixes the build - - - Details 1 - - Details 2 - - Details 3 +Firstly, thanks for being willing to contribute to Dotty! Head on over the +[Scala 3 Contributing +Guide](https://docs.scala-lang.org/scala3/guides/contribution/contribution-intro.html), which should have all the info you're looking for. From 03e3bd437d35a0a65ec175711d2e52e6bc4d5481 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 13:53:21 +0200 Subject: [PATCH 114/657] Apply class-shadowing.md --- docs/_spec/05-classes-and-objects.md | 2 +- .../dropped-features/class-shadowing.md | 2 -- .../dropped-features/class-shadowing-spec.md | 26 ------------------- 3 files changed, 1 insertion(+), 29 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/class-shadowing.md (94%) delete mode 100644 docs/_spec/TODOreference/dropped-features/class-shadowing-spec.md diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index 622f9dc8ae59..c40bd49cae16 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -241,7 +241,7 @@ It inherits member `f` from trait `C` and member `g` from trait `B`. A member ´M´ of class ´C´ that [matches](#class-members) a non-private member ´M'´ of a base class of ´C´ is said to _override_ that member. In this case the binding of the overriding member ´M´ must [subsume](03-types.html#conformance) the binding of the overridden member ´M'´. Furthermore, the following restrictions on modifiers apply to ´M´ and ´M'´: - +- ´M'´ must not be a class. - ´M'´ must not be labeled `final`. - ´M´ must not be [`private`](#modifiers). - If ´M´ is labeled `private[´C´]` for some enclosing class or package ´C´, then ´M'´ must be labeled `private[´C'´]` for some class or package ´C'´ where ´C'´ equals ´C´ or ´C'´ is contained in ´C´. diff --git a/docs/_spec/TODOreference/dropped-features/class-shadowing.md b/docs/_spec/APPLIEDreference/dropped-features/class-shadowing.md similarity index 94% rename from docs/_spec/TODOreference/dropped-features/class-shadowing.md rename to docs/_spec/APPLIEDreference/dropped-features/class-shadowing.md index 8bfdb1eb196c..a27b53db7cce 100644 --- a/docs/_spec/TODOreference/dropped-features/class-shadowing.md +++ b/docs/_spec/APPLIEDreference/dropped-features/class-shadowing.md @@ -29,5 +29,3 @@ The issue is that the two `Ops` classes _look_ like one overrides the other, but classes in Scala 2 cannot be overridden. To keep things clean (and its internal operations consistent) the Scala 3 compiler forces you to rename the inner classes so that their names are different. - -[More details](./class-shadowing-spec.md) diff --git a/docs/_spec/TODOreference/dropped-features/class-shadowing-spec.md b/docs/_spec/TODOreference/dropped-features/class-shadowing-spec.md deleted file mode 100644 index 6c81087408c6..000000000000 --- a/docs/_spec/TODOreference/dropped-features/class-shadowing-spec.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -layout: doc-page -title: "Dropped: Class Shadowing - More Details" -nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/class-shadowing-spec.html ---- - -Spec diff: in section [5.1.4 Overriding](https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html#Overriding), add *M' must not be a class*. - -> Why do we want to make this change to the language? - -Class shadowing is irregular compared to other types of overrides. Indeed, inner classes are not actually overridden but simply shadowed. - - -> How much existing code is going to be affected? - -From all the code compiled so far with Scala 3 the only instance of this I could find is in the stdlib. Looking at [this commit](https://github.com/lampepfl/scala/commit/68f13bf39979b631ed211ec1751934306ceb5d6c#diff-7aa508b70e055b47c823764e3e5646b8) it seems like the usage of class shadowing was accidental. - - -> How exactly is existing code going to be affected? - -Code that relies on overridden inner classes will stop compiling. - - -> Is this change going to be migratable automatically? - -No. From 63fe1c089f798a8806f15ae971118c1fa25f2e33 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 8 Feb 2023 10:06:24 +0100 Subject: [PATCH 115/657] Add regression tests Closes #16842 --- tests/neg/i16842.scala | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tests/neg/i16842.scala diff --git a/tests/neg/i16842.scala b/tests/neg/i16842.scala new file mode 100644 index 000000000000..1e7e5cc14339 --- /dev/null +++ b/tests/neg/i16842.scala @@ -0,0 +1,25 @@ +sealed trait Expr1 +sealed trait Literal extends Expr1 + +case class ArrayLiter(elems: List[Expr1]) extends Literal + +sealed trait SemanticType { + type T // the type with which a literal of this semanticType is represented +} +case object SemanticInt extends SemanticType { + type T = Int +} + +case class SemanticArray[U <: SemanticType](dim: Int) extends SemanticType { + type T = List[U] +} + +sealed trait Expr2[+T] +class Liter[T <: SemanticType](val ty: T, val value: ty.T) extends Expr2[T] + +def typecheckArrayLiter( + a: ArrayLiter +): Liter[SemanticArray[SemanticType]] = { + val x: List[Expr2[SemanticInt.type]] = List() + Liter(SemanticArray[SemanticInt.type], x) // error // error +} From 5fb27667a5843c388670f1f7688b37007aa94610 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 8 Feb 2023 10:59:39 +0100 Subject: [PATCH 116/657] Print owner of bind symbol with -Yprint-debug-owners --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 6f258fdddbe9..8ffb99f073fb 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -571,7 +571,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (lo eq hi) && alias.isEmpty then optText(lo)(" = " ~ _) else optText(lo)(" >: " ~ _) ~ optText(hi)(" <: " ~ _) ~ optText(alias)(" = " ~ _) case bind @ Bind(name, body) => - keywordText("given ").provided(tree.symbol.isOneOf(GivenOrImplicit) && !homogenizedView) ~ // Used for scala.quoted.Type in quote patterns (not pickled) + toTextOwner(bind) ~ keywordText("given ").provided(tree.symbol.isOneOf(GivenOrImplicit) && !homogenizedView) ~ // Used for scala.quoted.Type in quote patterns (not pickled) changePrec(InfixPrec) { nameIdText(bind) ~ " @ " ~ toText(body) } case Alternative(trees) => changePrec(OrPrec) { toText(trees, " | ") } From 36926c8a6256b4baf883251499e11a6466fd49ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pa=C5=82ka?= Date: Wed, 8 Feb 2023 16:39:33 +0100 Subject: [PATCH 117/657] Add --should-fail flag to the bisect script --- project/scripts/bisect.scala | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/project/scripts/bisect.scala b/project/scripts/bisect.scala index e60e632a7feb..2e554a885c79 100755 --- a/project/scripts/bisect.scala +++ b/project/scripts/bisect.scala @@ -31,6 +31,7 @@ val usageMessage = """ |The optional may be any combination of: |* --dry-run | Don't try to bisect - just make sure the validation command works correctly + | |* --releases | Bisect only releases from the given range (defaults to all releases). | The range format is ..., where both and are optional, e.g. @@ -38,8 +39,12 @@ val usageMessage = """ | * 3.2.1-RC1-bin-20220620-de3a82c-NIGHTLY.. | * ..3.3.0-RC1-bin-20221124-e25362d-NIGHTLY | The ranges are treated as inclusive. + | |* --bootstrapped - | Publish locally and test a bootstrapped compiler rather than a nonboostrapped one + | Publish locally and test a bootstrapped compiler rather than a nonboostrapped one. + | + |* --should-fail + | Expect the validation command to fail rather that succeed. This can be used e.g. to find out when some illegal code started to compile. | |Warning: The bisect script should not be run multiple times in parallel because of a potential race condition while publishing artifacts locally. @@ -54,7 +59,7 @@ val usageMessage = """ val validationScript = scriptOptions.validationCommand.validationScript val releases = Releases.fromRange(scriptOptions.releasesRange) - val releaseBisect = ReleaseBisect(validationScript, releases) + val releaseBisect = ReleaseBisect(validationScript, shouldFail = scriptOptions.shouldFail, releases) releaseBisect.verifyEdgeReleases() @@ -64,18 +69,19 @@ val usageMessage = """ println(s"First bad release: ${firstBadRelease.version}") println("\nFinished bisecting releases\n") - val commitBisect = CommitBisect(validationScript, bootstrapped = scriptOptions.bootstrapped, lastGoodRelease.hash, firstBadRelease.hash) + val commitBisect = CommitBisect(validationScript, shouldFail = scriptOptions.shouldFail, bootstrapped = scriptOptions.bootstrapped, lastGoodRelease.hash, firstBadRelease.hash) commitBisect.bisect() -case class ScriptOptions(validationCommand: ValidationCommand, dryRun: Boolean, bootstrapped: Boolean, releasesRange: ReleasesRange) +case class ScriptOptions(validationCommand: ValidationCommand, dryRun: Boolean, bootstrapped: Boolean, releasesRange: ReleasesRange, shouldFail: Boolean) object ScriptOptions: def fromArgs(args: Seq[String]) = val defaultOptions = ScriptOptions( validationCommand = null, dryRun = false, bootstrapped = false, - ReleasesRange(first = None, last = None) + ReleasesRange(first = None, last = None), + shouldFail = false ) parseArgs(args, defaultOptions) @@ -86,6 +92,7 @@ object ScriptOptions: case "--releases" :: argsRest => val range = ReleasesRange.tryParse(argsRest.head).get parseArgs(argsRest.tail, options.copy(releasesRange = range)) + case "--should-fail" :: argsRest => parseArgs(argsRest, options.copy(shouldFail = true)) case _ => val command = ValidationCommand.fromArgs(args) options.copy(validationCommand = command) @@ -182,7 +189,7 @@ case class Release(version: String): override def toString: String = version -class ReleaseBisect(validationScript: File, allReleases: Vector[Release]): +class ReleaseBisect(validationScript: File, shouldFail: Boolean, allReleases: Vector[Release]): assert(allReleases.length > 1, "Need at least 2 releases to bisect") private val isGoodReleaseCache = collection.mutable.Map.empty[Release, Boolean] @@ -217,21 +224,22 @@ class ReleaseBisect(validationScript: File, allReleases: Vector[Release]): isGoodReleaseCache.getOrElseUpdate(release, { println(s"Testing ${release.version}") val result = Seq(validationScript.getAbsolutePath, release.version).! - val isGood = result == 0 + val isGood = if(shouldFail) result != 0 else result == 0 // invert the process status if failure was expected println(s"Test result: ${release.version} is a ${if isGood then "good" else "bad"} release\n") isGood }) -class CommitBisect(validationScript: File, bootstrapped: Boolean, lastGoodHash: String, fistBadHash: String): +class CommitBisect(validationScript: File, shouldFail: Boolean, bootstrapped: Boolean, lastGoodHash: String, fistBadHash: String): def bisect(): Unit = println(s"Starting bisecting commits $lastGoodHash..$fistBadHash\n") val scala3CompilerProject = if bootstrapped then "scala3-compiler-bootstrapped" else "scala3-compiler" val scala3Project = if bootstrapped then "scala3-bootstrapped" else "scala3" + val validationCommandStatusModifier = if shouldFail then "! " else "" // invert the process status if failure was expected val bisectRunScript = s""" |scalaVersion=$$(sbt "print ${scala3CompilerProject}/version" | tail -n1) |rm -r out |sbt "clean; ${scala3Project}/publishLocal" - |${validationScript.getAbsolutePath} "$$scalaVersion" + |${validationCommandStatusModifier}${validationScript.getAbsolutePath} "$$scalaVersion" """.stripMargin "git bisect start".! s"git bisect bad $fistBadHash".! From 07dcca1b73800aa3623dce92323c494aad98b055 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Fri, 27 Jan 2023 09:52:06 +0100 Subject: [PATCH 118/657] Fix crashes of path dependent types in spliced Type.of Previously it was assumed that the type in Type.of could be captured as a whole, which meant that path dependent types for which a separate @SplicedType hole definitions were included in a block, would end up with missing references. Now when find a block in Type.of, we try to analise all parts of the type separately, adding additional hole definitions to the block as necessary. For types that can be captured as a whole (those which did not have a block generated previously, meaning they do not include any @SplicedType hole definitions), old method is used. --- .../dotty/tools/dotc/transform/Splicing.scala | 39 ++++++++++++++++--- tests/pos-macros/i16615.scala | 16 ++++++++ 2 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 tests/pos-macros/i16615.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index ad3f0322130d..799dbc6c09c9 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -242,9 +242,14 @@ class Splicing extends MacroTransform: else args.mapConserve(arg => transformLevel0QuoteContent(arg)(using quoteContext)) } cpy.Apply(tree)(cpy.Select(sel)(cpy.Apply(app)(fn, newArgs), nme.apply), quotesArgs) - case Apply(TypeApply(_, List(tpt)), List(quotes)) + case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => - ref(capturedType(tpt))(using ctx.withSource(tree.source)).withSpan(tree.span) + tpt match + case block: Block => + val newBlock = capturedBlockPartTypes(block) + Apply(TypeApply(typeof, List(newBlock)), List(quotes)).withSpan(tree.span) + case _ => + ref(capturedType(tpt))(using ctx.withSource(tree.source)).withSpan(tree.span) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) case _ => @@ -335,17 +340,39 @@ class Splicing extends MacroTransform: val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2 ref(bindingSym) - private def capturedType(tree: Tree)(using Context): Symbol = - val tpe = tree.tpe.widenTermRefExpr - def newBinding = newSymbol( + private def newQuotedTypeClassBinding(tpe: Type)(using Context) = + newSymbol( spliceOwner, UniqueName.fresh(nme.Type).toTermName, Param, defn.QuotedTypeClass.typeRef.appliedTo(tpe), ) - val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newBinding))._2 + + private def capturedType(tree: Tree)(using Context): Symbol = + val tpe = tree.tpe.widenTermRefExpr + val bindingSym = refBindingMap + .getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tpe)))._2 bindingSym + private def capturedBlockPartTypes(block: Block)(using Context): Tree = + val old = healedTypes + healedTypes = PCPCheckAndHeal.QuoteTypeTags(block.span) + val capturePartTypes = new TypeMap { + def apply(tp: Type) = tp match { + case typeRef @ TypeRef(prefix, _) if isCaptured(prefix.typeSymbol) || isCaptured(prefix.termSymbol) => + val termRef = refBindingMap + .getOrElseUpdate(typeRef.symbol, (TypeTree(typeRef), newQuotedTypeClassBinding(typeRef)))._2.termRef + val tagRef = healedTypes.nn.getTagRef(termRef) + tagRef + case _ => + mapOver(tp) + } + } + val captured = capturePartTypes(block.tpe.widenTermRefExpr) + val newHealedTypes = healedTypes.nn.getTypeTags + healedTypes = old + Block(newHealedTypes ::: block.stats, TypeTree(captured)) + private def getTagRefFor(tree: Tree)(using Context): Tree = val capturedTypeSym = capturedType(tree) TypeTree(healedTypes.nn.getTagRef(capturedTypeSym.termRef)) diff --git a/tests/pos-macros/i16615.scala b/tests/pos-macros/i16615.scala new file mode 100644 index 000000000000..510994f20423 --- /dev/null +++ b/tests/pos-macros/i16615.scala @@ -0,0 +1,16 @@ +import scala.quoted.* + +trait Api: + type Reader[E] + +def bugImpl[T: Type, Q[_]: Type](using Quotes) = + '{ + val p: Api = ??? + ${ + Type.of[p.Reader[T]] + Type.of[Q[p.Reader[T]]] + Type.of[p.Reader[Q[p.Reader[T]]]] + Type.of[p.Reader[Q[T]]] + Expr(1) + } + } \ No newline at end of file From 90cdb6957a78b14d838a5ec49b707d9b3504f695 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Wed, 8 Feb 2023 18:06:07 +0100 Subject: [PATCH 119/657] Make holes more consistent and address review comments --- .../dotty/tools/dotc/transform/Splicing.scala | 27 ++++++++++++------- tests/pos-macros/i16615.scala | 5 +++- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 799dbc6c09c9..393fe46b8438 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -244,12 +244,14 @@ class Splicing extends MacroTransform: cpy.Apply(tree)(cpy.Select(sel)(cpy.Apply(app)(fn, newArgs), nme.apply), quotesArgs) case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => - tpt match - case block: Block => - val newBlock = capturedBlockPartTypes(block) - Apply(TypeApply(typeof, List(newBlock)), List(quotes)).withSpan(tree.span) + val newContent = capturedPartTypes(tpt) + newContent match + case block: Block => + inContext(ctx.withSource(tree.source)) { + Apply(TypeApply(typeof, List(newContent)), List(quotes)).withSpan(tree.span) + } case _ => - ref(capturedType(tpt))(using ctx.withSource(tree.source)).withSpan(tree.span) + ref(capturedType(newContent))(using ctx.withSource(tree.source)).withSpan(tree.span) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) case _ => @@ -354,9 +356,9 @@ class Splicing extends MacroTransform: .getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tpe)))._2 bindingSym - private def capturedBlockPartTypes(block: Block)(using Context): Tree = + private def capturedPartTypes(tpt: Tree)(using Context): Tree = val old = healedTypes - healedTypes = PCPCheckAndHeal.QuoteTypeTags(block.span) + healedTypes = PCPCheckAndHeal.QuoteTypeTags(tpt.span) val capturePartTypes = new TypeMap { def apply(tp: Type) = tp match { case typeRef @ TypeRef(prefix, _) if isCaptured(prefix.typeSymbol) || isCaptured(prefix.termSymbol) => @@ -368,10 +370,17 @@ class Splicing extends MacroTransform: mapOver(tp) } } - val captured = capturePartTypes(block.tpe.widenTermRefExpr) + val captured = capturePartTypes(tpt.tpe.widenTermRefExpr) val newHealedTypes = healedTypes.nn.getTypeTags healedTypes = old - Block(newHealedTypes ::: block.stats, TypeTree(captured)) + tpt match + case block: Block => + cpy.Block(block)(newHealedTypes ::: block.stats, TypeTree(captured)) + case _ => + if newHealedTypes.nonEmpty then + cpy.Block(tpt)(newHealedTypes, TypeTree(captured)) + else + tpt private def getTagRefFor(tree: Tree)(using Context): Tree = val capturedTypeSym = capturedType(tree) diff --git a/tests/pos-macros/i16615.scala b/tests/pos-macros/i16615.scala index 510994f20423..3cc2d271fa87 100644 --- a/tests/pos-macros/i16615.scala +++ b/tests/pos-macros/i16615.scala @@ -10,7 +10,10 @@ def bugImpl[T: Type, Q[_]: Type](using Quotes) = Type.of[p.Reader[T]] Type.of[Q[p.Reader[T]]] Type.of[p.Reader[Q[p.Reader[T]]]] + Type.of[List[p.Reader[T]]] + Type.of[p.Reader[List[p.Reader[T]]]] + Type.of[p.Reader[List[T]]] Type.of[p.Reader[Q[T]]] Expr(1) } - } \ No newline at end of file + } From 7f6a2f752d9059ca3d681e54076100e597a8e4ea Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Thu, 2 Feb 2023 17:56:12 +0100 Subject: [PATCH 120/657] improvement: Small fixes to allow using Metals with scaladoc with sbt This would require just setting `bspEnabled := true`, but not for sjsJUnitTests which seem to cause inifinite loop of compilations. I also need to work a bit on Bloop to also make it work. This still needs https://github.com/scalameta/metals/pull/4938 --- project/Build.scala | 2 ++ project/build.sbt | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 90f2dc3fe076..097876a63f92 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -360,6 +360,7 @@ object Build { // Settings used when compiling dotty with a non-bootstrapped dotty lazy val commonBootstrappedSettings = commonDottySettings ++ NoBloopExport.settings ++ Seq( + // To enable support of scaladoc and language-server projects you need to change this to true and use sbt as your build server bspEnabled := false, (Compile / unmanagedSourceDirectories) += baseDirectory.value / "src-bootstrapped", @@ -1130,6 +1131,7 @@ object Build { enablePlugins(DottyJSPlugin). dependsOn(`scala3-library-bootstrappedJS`). settings( + bspEnabled := false, scalacOptions --= Seq("-Xfatal-warnings", "-deprecation"), // Required to run Scala.js tests. diff --git a/project/build.sbt b/project/build.sbt index e19492c42022..188dfa5c6702 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -1,7 +1,4 @@ // Used by VersionUtil to get gitHash and commitDate libraryDependencies += "org.eclipse.jgit" % "org.eclipse.jgit" % "4.11.0.201803080745-r" - -Compile / unmanagedSourceDirectories += - baseDirectory.value / "../language-server/src/dotty/tools/languageserver/config" libraryDependencies += Dependencies.`jackson-databind` From 09ac6c27c7e65d1b23525b5f5c6a229278bd26ef Mon Sep 17 00:00:00 2001 From: Tomasz Godzik Date: Wed, 8 Feb 2023 18:59:22 +0100 Subject: [PATCH 121/657] bugfix: Make sure symbol exists before calling owner --- .../src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala index 84bc1e77a1ef..91614aaccad2 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala @@ -190,7 +190,7 @@ class ExtractSemanticDB extends Phase: registerUseGuarded(None, privateWithin, spanOfSymbol(privateWithin, tree.span, tree.source), tree.source) else if !excludeSymbol(tree.symbol) then registerSymbol(tree.symbol, symbolKinds(tree)) - case tree: Template if tree.symbol.owner.is(Invisible) => + case tree: Template if tree.symbol != NoSymbol && tree.symbol.owner.is(Invisible) => // do nothing // exclude the symbols and synthetics generated by @main annotation // (main class generated by @main has `Invisible` flag, see `MainProxies.scala`). @@ -201,7 +201,7 @@ class ExtractSemanticDB extends Phase: val selfSpan = tree.self.span if selfSpan.exists && selfSpan.hasLength then traverse(tree.self) - if tree.symbol.owner.isEnumClass then + if tree.symbol != NoSymbol && tree.symbol.owner.isEnumClass then tree.body.foreachUntilImport(traverse).foreach(traverse) // the first import statement else tree.body.foreach(traverse) From e0f56c8ec98fd18ed2f3f5822022c18ebcd50254 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 8 Feb 2023 14:31:34 +0100 Subject: [PATCH 122/657] Add regression test Closes #16008 --- tests/pos-macros/i16008/Macro_1.scala | 24 ++++++++++++++++++++++++ tests/pos-macros/i16008/Test_2.scala | 1 + 2 files changed, 25 insertions(+) create mode 100644 tests/pos-macros/i16008/Macro_1.scala create mode 100644 tests/pos-macros/i16008/Test_2.scala diff --git a/tests/pos-macros/i16008/Macro_1.scala b/tests/pos-macros/i16008/Macro_1.scala new file mode 100644 index 000000000000..b1cc1e6b2b9d --- /dev/null +++ b/tests/pos-macros/i16008/Macro_1.scala @@ -0,0 +1,24 @@ +import scala.quoted.* + +enum MyEnum0: + case Marked + case Marked2(i: Int) + +trait MyMarker + +enum MyEnum(val value: String): + case Marked extends MyEnum("marked") with MyMarker + case Marked2(i: Int) extends MyEnum("marked") with MyMarker + +inline def enumMacro: Unit = ${ enumMacroExpr } + +private def enumMacroExpr(using Quotes): Expr[Unit] = + import quotes.reflect.* + assert(TypeRepr.of[MyEnum0].typeSymbol.flags.is(Flags.Enum)) + assert(TypeRepr.of[MyEnum0.Marked.type].termSymbol.flags.is(Flags.Enum)) + assert(TypeRepr.of[MyEnum0.Marked2].typeSymbol.flags.is(Flags.Enum)) + assert(TypeRepr.of[MyEnum].typeSymbol.flags.is(Flags.Enum)) + assert(TypeRepr.of[MyEnum.Marked.type].termSymbol.flags.is(Flags.Enum)) + assert(TypeRepr.of[MyEnum.Marked2].typeSymbol.flags.is(Flags.Enum)) + + '{} diff --git a/tests/pos-macros/i16008/Test_2.scala b/tests/pos-macros/i16008/Test_2.scala new file mode 100644 index 000000000000..43631e59e4b2 --- /dev/null +++ b/tests/pos-macros/i16008/Test_2.scala @@ -0,0 +1 @@ +def test = enumMacro From 94a6c674e017b7b861db84edeb9c8b3bcfb4b31f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 9 Feb 2023 11:24:33 +0100 Subject: [PATCH 123/657] Always load REPL classes in macros including the output directory Fixes #15104 --- .../dotty/tools/dotc/quoted/Interpreter.scala | 19 +++++++++---------- compiler/test-resources/repl-macros/i15104a | 7 +++++++ compiler/test-resources/repl-macros/i15104b | 5 +++++ compiler/test-resources/repl-macros/i15104c | 7 +++++++ compiler/test-resources/repl-macros/i5551 | 3 +-- .../test/dotty/tools/repl/ScriptedTests.scala | 4 ++++ 6 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 compiler/test-resources/repl-macros/i15104a create mode 100644 compiler/test-resources/repl-macros/i15104b create mode 100644 compiler/test-resources/repl-macros/i15104c diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index 38cecb7953b8..7c8262ecbf00 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -32,10 +32,15 @@ import dotty.tools.dotc.reporting.Message import dotty.tools.repl.AbstractFileClassLoader /** Tree interpreter for metaprogramming constructs */ -class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context): +class Interpreter(pos: SrcPos, classLoader0: ClassLoader)(using Context): import Interpreter._ import tpd._ + val classLoader = + if ctx.owner.topLevelClass.name.startsWith(str.REPL_SESSION_LINE) then + new AbstractFileClassLoader(ctx.settings.outputDir.value, classLoader0) + else classLoader0 + /** Local variable environment */ type Env = Map[Symbol, Object] def emptyEnv: Env = Map.empty @@ -157,18 +162,12 @@ class Interpreter(pos: SrcPos, classLoader: ClassLoader)(using Context): args.toSeq private def interpretedStaticMethodCall(moduleClass: Symbol, fn: Symbol, args: List[Object]): Object = { - val (inst, clazz) = - try - if (moduleClass.name.startsWith(str.REPL_SESSION_LINE)) - (null, loadReplLineClass(moduleClass)) - else { - val inst = loadModule(moduleClass) - (inst, inst.getClass) - } + val inst = + try loadModule(moduleClass) catch case MissingClassDefinedInCurrentRun(sym) => suspendOnMissing(sym, pos) - + val clazz = inst.getClass val name = fn.name.asTermName val method = getMethod(clazz, name, paramsSig(fn)) stopIfRuntimeException(method.invoke(inst, args: _*), method) diff --git a/compiler/test-resources/repl-macros/i15104a b/compiler/test-resources/repl-macros/i15104a new file mode 100644 index 000000000000..92e82928b509 --- /dev/null +++ b/compiler/test-resources/repl-macros/i15104a @@ -0,0 +1,7 @@ +scala> import scala.quoted._ +scala> object Foo { def macroImpl(using Quotes) = Expr(1) } +// defined object Foo +scala> inline def foo = ${ Foo.macroImpl } +def foo: Int +scala> foo +val res0: Int = 1 diff --git a/compiler/test-resources/repl-macros/i15104b b/compiler/test-resources/repl-macros/i15104b new file mode 100644 index 000000000000..ebbdb2402076 --- /dev/null +++ b/compiler/test-resources/repl-macros/i15104b @@ -0,0 +1,5 @@ +scala> import scala.quoted._ +scala> object Foo { def macroImpl(using Quotes) = Expr(1); inline def foo = ${ Foo.macroImpl } } +// defined object Foo +scala> Foo.foo +val res0: Int = 1 diff --git a/compiler/test-resources/repl-macros/i15104c b/compiler/test-resources/repl-macros/i15104c new file mode 100644 index 000000000000..482b9487c9d9 --- /dev/null +++ b/compiler/test-resources/repl-macros/i15104c @@ -0,0 +1,7 @@ +scala> import scala.quoted._ +scala> def macroImpl(using Quotes) = Expr(1) +def macroImpl(using x$1: quoted.Quotes): quoted.Expr[Int] +scala> inline def foo = ${ macroImpl } +def foo: Int +scala> foo +val res0: Int = 1 diff --git a/compiler/test-resources/repl-macros/i5551 b/compiler/test-resources/repl-macros/i5551 index fb039ed19dd6..d71251e9a824 100644 --- a/compiler/test-resources/repl-macros/i5551 +++ b/compiler/test-resources/repl-macros/i5551 @@ -1,8 +1,7 @@ scala> import scala.quoted._ scala> def assertImpl(expr: Expr[Boolean])(using q: Quotes) = '{ if !($expr) then throw new AssertionError("failed assertion")} def assertImpl - (expr: quoted.Expr[Boolean]) - (using q: quoted.Quotes): quoted.Expr[Unit] + (expr: quoted.Expr[Boolean])(using q: quoted.Quotes): quoted.Expr[Unit] scala> inline def assert(expr: => Boolean): Unit = ${ assertImpl('{expr}) } def assert(expr: => Boolean): Unit diff --git a/compiler/test/dotty/tools/repl/ScriptedTests.scala b/compiler/test/dotty/tools/repl/ScriptedTests.scala index 5c3a32cd40f8..dc809228e86b 100644 --- a/compiler/test/dotty/tools/repl/ScriptedTests.scala +++ b/compiler/test/dotty/tools/repl/ScriptedTests.scala @@ -3,12 +3,16 @@ package tools package repl import org.junit.Test +import org.junit.experimental.categories.Category /** Runs all tests contained in `compiler/test-resources/repl/` */ class ScriptedTests extends ReplTest { @Test def replTests = scripts("/repl").foreach(testFile) + @Category(Array(classOf[BootstrappedOnlyTests])) + @Test def replMacrosTests = scripts("/repl-macros").foreach(testFile) + @Test def typePrinterTests = scripts("/type-printer").foreach(testFile) } From 116a9602cb32fde45329fedc2eaa6a987152e98f Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 9 Feb 2023 08:35:45 -0800 Subject: [PATCH 124/657] Fix comment test number --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6f85efb0fc8a..dddf7523420a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1335,7 +1335,7 @@ class Namer { typer: Typer => * * The idea is that this simulates the hypothetical case where export forwarders * are not generated and we treat an export instead more like an import where we - * expand the use site reference. Test cases in {neg,pos}/i14699.scala. + * expand the use site reference. Test cases in {neg,pos}/i14966.scala. * * @pre Forwarders with the same name are consecutive in `forwarders`. */ From f77069a74c4df65c7f69a6476aeb6b52fa8acb48 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 10 Feb 2023 15:36:06 +0100 Subject: [PATCH 125/657] ClassfileParser: Avoid cycle when accessing companion in inner class lookup Previously, the call to `info` on the module val could lead to a cycle since the module val might be in the process of being completed. This commit fixes this by only using the module class which is all we need to lookup members. Fixes #15288. Fixes #14059. Co-Authored-By: Tom Grigg --- .../tools/dotc/core/classfile/ClassfileParser.scala | 8 ++++---- sbt-test/java-compat/i15288/QueryRequest.java | 9 +++++++++ sbt-test/java-compat/i15288/Test.scala | 2 ++ sbt-test/java-compat/i15288/build.sbt | 1 + sbt-test/java-compat/i15288/test | 5 +++++ 5 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 sbt-test/java-compat/i15288/QueryRequest.java create mode 100644 sbt-test/java-compat/i15288/Test.scala create mode 100644 sbt-test/java-compat/i15288/build.sbt create mode 100644 sbt-test/java-compat/i15288/test diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 7702e6a93446..eeeb3767bd34 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -1086,10 +1086,10 @@ class ClassfileParser( if (sym == classRoot.symbol) staticScope.lookup(name) else { - var module = sym.companionModule - if (!module.exists && sym.isAbsent()) - module = sym.scalacLinkedClass - module.info.member(name).symbol + var moduleClass = sym.registeredCompanion + if (!moduleClass.exists && sym.isAbsent()) + moduleClass = sym.scalacLinkedClass + moduleClass.info.member(name).symbol } else if (sym == classRoot.symbol) instanceScope.lookup(name) diff --git a/sbt-test/java-compat/i15288/QueryRequest.java b/sbt-test/java-compat/i15288/QueryRequest.java new file mode 100644 index 000000000000..e43487e09449 --- /dev/null +++ b/sbt-test/java-compat/i15288/QueryRequest.java @@ -0,0 +1,9 @@ +interface CopyableBuilder {} +interface ToCopyableBuilder {} + +public class QueryRequest implements ToCopyableBuilder { + public static Builder builder() { throw new UnsupportedOperationException(); } + public interface Builder extends CopyableBuilder { + void build(); + } +} diff --git a/sbt-test/java-compat/i15288/Test.scala b/sbt-test/java-compat/i15288/Test.scala new file mode 100644 index 000000000000..e03617ac4c33 --- /dev/null +++ b/sbt-test/java-compat/i15288/Test.scala @@ -0,0 +1,2 @@ +class Test: + def makeQuery = QueryRequest.builder().build() diff --git a/sbt-test/java-compat/i15288/build.sbt b/sbt-test/java-compat/i15288/build.sbt new file mode 100644 index 000000000000..63e314982c41 --- /dev/null +++ b/sbt-test/java-compat/i15288/build.sbt @@ -0,0 +1 @@ +scalaVersion := sys.props("plugin.scalaVersion") diff --git a/sbt-test/java-compat/i15288/test b/sbt-test/java-compat/i15288/test new file mode 100644 index 000000000000..ad1a8a5987ee --- /dev/null +++ b/sbt-test/java-compat/i15288/test @@ -0,0 +1,5 @@ +## This could just be a pos test checked by FromTastyTests, but +## ParallelTesting#compileTastyInDir does not support test with multiple files +## currently. +> compile +> doc From d6ee51e3d4880737c3a9596bd3773ca5013db4f8 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 11 Feb 2023 13:34:25 +0100 Subject: [PATCH 126/657] Harden tpd.Apply/TypeApply in case of errors Harden tpd.Apply/TypeApply in case of errors to accept non-sensical terms as functions. Fixes #16861 --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 6 ++++++ tests/neg/i16861.scala | 2 ++ tests/neg/i16861a.scala | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 tests/neg/i16861.scala create mode 100644 tests/neg/i16861a.scala diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 1a202ece1e66..aea8cf297ab8 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -47,12 +47,18 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Apply(expr, args) case _: RefTree | _: GenericApply | _: Inlined | _: Hole => ta.assignType(untpd.Apply(fn, args), fn, args) + case _ => + assert(ctx.reporter.errorsReported) + ta.assignType(untpd.Apply(fn, args), fn, args) def TypeApply(fn: Tree, args: List[Tree])(using Context): TypeApply = fn match case Block(Nil, expr) => TypeApply(expr, args) case _: RefTree | _: GenericApply => ta.assignType(untpd.TypeApply(fn, args), fn, args) + case _ => + assert(ctx.reporter.errorsReported) + ta.assignType(untpd.TypeApply(fn, args), fn, args) def Literal(const: Constant)(using Context): Literal = ta.assignType(untpd.Literal(const)) diff --git a/tests/neg/i16861.scala b/tests/neg/i16861.scala new file mode 100644 index 000000000000..50c56974d027 --- /dev/null +++ b/tests/neg/i16861.scala @@ -0,0 +1,2 @@ +given foo[T]: Any = summon[bar] // error +def bar: Nothing = ??? \ No newline at end of file diff --git a/tests/neg/i16861a.scala b/tests/neg/i16861a.scala new file mode 100644 index 000000000000..b93f884f5e56 --- /dev/null +++ b/tests/neg/i16861a.scala @@ -0,0 +1,4 @@ +import scala.quoted.* +trait Foo +object Foo: + inline given foo[T <: Foo]: T = summon[Type.of[T]] // error From 2678e67c2acc6eeee5e0a252a2c19a78bedcd83a Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 11 Feb 2023 15:37:04 +0100 Subject: [PATCH 127/657] Add missing criterion to subtype check Fixes #16850 --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 1 + tests/neg/i16850.check | 10 ++++++++++ tests/neg/i16850.scala | 10 ++++++++++ 3 files changed, 21 insertions(+) create mode 100644 tests/neg/i16850.check create mode 100644 tests/neg/i16850.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 2a0072590550..9e8d18765352 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -309,6 +309,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling thirdTryNamed(tp2) else ( (tp1.name eq tp2.name) + && !sym1.is(Private) && tp2.isPrefixDependentMemberRef && isSubPrefix(tp1.prefix, tp2.prefix) && tp1.signature == tp2.signature diff --git a/tests/neg/i16850.check b/tests/neg/i16850.check new file mode 100644 index 000000000000..6c9c7f7e0eac --- /dev/null +++ b/tests/neg/i16850.check @@ -0,0 +1,10 @@ +-- [E007] Type Mismatch Error: tests/neg/i16850.scala:7:33 ------------------------------------------------------------- +7 | def add(elm: Y): Unit = list = elm :: list // error + | ^^^ + | Found: (elm : Y) + | Required: Class.this.Y² + | + | where: Y is a type in class Class + | Y² is a type in trait Trait + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i16850.scala b/tests/neg/i16850.scala new file mode 100644 index 000000000000..e7904fcd44e7 --- /dev/null +++ b/tests/neg/i16850.scala @@ -0,0 +1,10 @@ + +trait Trait : + type Y + var list: List[Y] = Nil + +class Class[Y] extends Trait : + def add(elm: Y): Unit = list = elm :: list // error + +object Object extends Class[Int] : + add(42) From ad675afe873b64ce178ba82d254d64254330a90f Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 11 Feb 2023 19:30:13 +0100 Subject: [PATCH 128/657] Generate toString only for synthetic companions of case classes Don't generate a toString method if the companion is explicitly given. This matches Scala 2's behavior. Fixes #16879 --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 6 +++++- tests/run/i16879.scala | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/run/i16879.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6f85efb0fc8a..cab5dc273f4b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -541,7 +541,11 @@ class Namer { typer: Typer => res = cpy.TypeDef(modCls)( rhs = cpy.Template(modTempl)( derived = if (fromTempl.derived.nonEmpty) fromTempl.derived else modTempl.derived, - body = fromTempl.body ++ modTempl.body)) + body = fromTempl.body.filter { + case stat: DefDef => stat.name != nme.toString_ + // toString should only be generated if explicit companion is missing + case _ => true + } ++ modTempl.body)) if (fromTempl.derived.nonEmpty) { if (modTempl.derived.nonEmpty) report.error(em"a class and its companion cannot both have `derives` clauses", mdef.srcPos) diff --git a/tests/run/i16879.scala b/tests/run/i16879.scala new file mode 100644 index 000000000000..c01b8cb1ed89 --- /dev/null +++ b/tests/run/i16879.scala @@ -0,0 +1,16 @@ +trait Companion: + final override def toString: String = "Companion" + +case class Example(value: Int) +object Example extends Companion + +case class C() +object C: + override def toString = "CC" + +case class D() + +@main def Test = + assert(Example.toString == "Companion") + assert(C.toString == "CC") + assert(D.toString == "D") From 4e8e201be4227ec1b13a28d50505ba71f2979454 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 11 Feb 2023 19:48:39 +0100 Subject: [PATCH 129/657] PolyFunction desugar: Don't copy type flags to parameter Fixes #16871 Signed-off-by: odersky --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 2 +- tests/pos/i16871.scala | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i16871.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 2cfd292fd6dd..85dd5e6665c6 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1732,7 +1732,7 @@ object desugar { val applyVParams = vargs.zipWithIndex.map { case (p: ValDef, _) => p.withAddedFlags(mods.flags) - case (p, n) => makeSyntheticParameter(n + 1, p).withAddedFlags(mods.flags) + case (p, n) => makeSyntheticParameter(n + 1, p).withAddedFlags(mods.flags.toTermFlags) } RefinedTypeTree(polyFunctionTpt, List( DefDef(nme.apply, applyTParams :: applyVParams :: Nil, res, EmptyTree).withFlags(Synthetic) diff --git a/tests/pos/i16871.scala b/tests/pos/i16871.scala new file mode 100644 index 000000000000..3251a7135346 --- /dev/null +++ b/tests/pos/i16871.scala @@ -0,0 +1,3 @@ +import scala.language.experimental.captureChecking + +val f: [X] => Int => Int = [X] => (x: Int) => x \ No newline at end of file From afd499bfe1c5cf279a4635a72b98b5ddae2d65fd Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 11 Feb 2023 20:48:30 +0100 Subject: [PATCH 130/657] Update semanticdb expect file --- tests/semanticdb/metac.expect | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 32e19e1f9c46..c78d30e00863 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -467,7 +467,7 @@ Schema => SemanticDB v4 Uri => Classes.scala Text => empty Language => Scala -Symbols => 109 entries +Symbols => 108 entries Occurrences => 114 entries Synthetics => 2 entries @@ -504,10 +504,9 @@ classes/C4#copy$default$1(). => method copy$default$1 => Int @uncheckedVariance classes/C4#copy(). => method copy (param x: Int): C4 classes/C4#copy().(x) => param x: Int classes/C4#x. => val method x Int -classes/C4. => final object C4 extends Object { self: C4.type => +4 decls } +classes/C4. => final object C4 extends Object { self: C4.type => +3 decls } classes/C4.apply(). => method apply (param x: Int): C4 classes/C4.apply().(x) => param x: Int -classes/C4.toString(). => method toString => String <: scala/Any#toString(). classes/C4.unapply(). => method unapply (param x$1: C4): C4 classes/C4.unapply().(x$1) => param x$1: C4 classes/C6# => case class C6 extends Object with Product with Serializable { self: C6 => +5 decls } @@ -4559,7 +4558,7 @@ Schema => SemanticDB v4 Uri => semanticdb-Types.scala Text => empty Language => Scala -Symbols => 144 entries +Symbols => 143 entries Occurrences => 228 entries Synthetics => 1 entries @@ -4585,10 +4584,9 @@ types/Foo#copy$default$1(). => method copy$default$1 => "abc" @uncheckedVariance types/Foo#copy(). => method copy (param s: "abc"): Foo types/Foo#copy().(s) => param s: "abc" types/Foo#s. => val method s "abc" -types/Foo. => final object Foo extends Object { self: Foo.type => +6 decls } +types/Foo. => final object Foo extends Object { self: Foo.type => +5 decls } types/Foo.apply(). => method apply (param s: "abc"): Foo types/Foo.apply().(s) => param s: "abc" -types/Foo.toString(). => method toString => String <: scala/Any#toString(). types/Foo.unapply(). => method unapply (param x$1: Foo): Foo types/Foo.unapply().(x$1) => param x$1: Foo types/Foo.x. => val method x "abc" @deprecated From bdd56dd7073cc87293f96fee23a980140276cf3b Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 12 Feb 2023 10:11:49 +0100 Subject: [PATCH 131/657] Move test to cc directory This avoids a pickler test failure, since under pickling we don't see language imports in source when we print the trees for comparisons. --- tests/{pos => pos-custom-args/captures}/i16871.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{pos => pos-custom-args/captures}/i16871.scala (100%) diff --git a/tests/pos/i16871.scala b/tests/pos-custom-args/captures/i16871.scala similarity index 100% rename from tests/pos/i16871.scala rename to tests/pos-custom-args/captures/i16871.scala From ae05f998bf118ca72d9e39857092227456c5da13 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 12 Feb 2023 11:49:21 +0100 Subject: [PATCH 132/657] Don't eta expand unary varargs methods A unary varargs method sits between nullary methods that sometimes get a () argument inferred (i.e. for methods coming from Java) and other methods that can be eta expanded. The safest strategy for them is to do neither, and expect either an explicit expected function type, or an explicit argument. That's also what Scala 2 does. Fixes #16820 Reclassifies #14567 to be a neg test (with the error message suggested in the original issue for #14567) --- .../src/dotty/tools/dotc/typer/Typer.scala | 5 ++++- tests/neg/i16820.check | 22 +++++++++++++++++++ tests/neg/i16820.scala | 13 +++++++++++ tests/pos/i14367.scala | 2 +- tests/pos/i14567.scala | 2 -- 5 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 tests/neg/i16820.check create mode 100644 tests/neg/i16820.scala delete mode 100644 tests/pos/i14567.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index a87d6dd7e703..82928894ebca 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3922,7 +3922,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else defn.functionArity(ptNorm) else val nparams = wtp.paramInfos.length - if nparams > 0 || pt.eq(AnyFunctionProto) then nparams + if nparams > 1 + || nparams == 1 && !wtp.isVarArgsMethod + || pt.eq(AnyFunctionProto) + then nparams else -1 // no eta expansion in this case adaptNoArgsUnappliedMethod(wtp, funExpected, arity) case _ => diff --git a/tests/neg/i16820.check b/tests/neg/i16820.check new file mode 100644 index 000000000000..5f2e82bbf2ef --- /dev/null +++ b/tests/neg/i16820.check @@ -0,0 +1,22 @@ +-- Error: tests/neg/i16820.scala:5:11 ---------------------------------------------------------------------------------- +5 | val x1 = f // error + | ^ + | missing arguments for method f in object Test +-- [E100] Syntax Error: tests/neg/i16820.scala:6:11 -------------------------------------------------------------------- +6 | val x2 = g // error + | ^ + | method g in object Test must be called with () argument + | + | longer explanation available when compiling with `-explain` +-- Error: tests/neg/i16820.scala:8:14 ---------------------------------------------------------------------------------- +8 | val x3 = "".formatted // error + | ^^^^^^^^^^^^ + | missing arguments for method formatted in class String +-- Error: tests/neg/i16820.scala:9:40 ---------------------------------------------------------------------------------- +9 | val x4 = java.nio.file.Paths.get(".").toRealPath // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | missing arguments for method toRealPath in trait Path +-- Error: tests/neg/i16820.scala:13:14 --------------------------------------------------------------------------------- +13 |def test = Foo(3) // error + | ^^^^^^ + | missing arguments for method apply in object Foo diff --git a/tests/neg/i16820.scala b/tests/neg/i16820.scala new file mode 100644 index 000000000000..7a93f3a5d355 --- /dev/null +++ b/tests/neg/i16820.scala @@ -0,0 +1,13 @@ +object Test: + def f(xs: Int*) = xs.sum + def g() = 1 + + val x1 = f // error + val x2 = g // error + + val x3 = "".formatted // error + val x4 = java.nio.file.Paths.get(".").toRealPath // error + +// #14567 +case class Foo(x: Int)(xs: String*) +def test = Foo(3) // error diff --git a/tests/pos/i14367.scala b/tests/pos/i14367.scala index d74f0aa8373e..3319e705b741 100644 --- a/tests/pos/i14367.scala +++ b/tests/pos/i14367.scala @@ -1,5 +1,5 @@ def m(i: Int*) = i.sum -val f1 = m +val f1: Seq[Int] => Int = m val f2 = i => m(i*) def n(i: Seq[Int]) = i.sum diff --git a/tests/pos/i14567.scala b/tests/pos/i14567.scala deleted file mode 100644 index fe47279c07b1..000000000000 --- a/tests/pos/i14567.scala +++ /dev/null @@ -1,2 +0,0 @@ -case class Foo(x: Int)(xs: String*) -def test = Foo(3) From 4b8a31fcc092b912caa6fc2a4ee55ebc4a4ac2d6 Mon Sep 17 00:00:00 2001 From: "s.bazarsadaev" Date: Sun, 6 Nov 2022 20:49:37 +0300 Subject: [PATCH 133/657] Closes #14340 - add dynamic access to value classes field --- .../src/dotty/tools/dotc/typer/Dynamic.scala | 32 +++++++++++++------ tests/run/i14340.check | 4 +++ tests/run/i14340.scala | 20 ++++++++++++ 3 files changed, 46 insertions(+), 10 deletions(-) create mode 100644 tests/run/i14340.check create mode 100644 tests/run/i14340.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 1630ce31e4c6..5de9ca6ab341 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -2,20 +2,22 @@ package dotty.tools package dotc package typer -import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.ast.Trees.* import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.core.Constants.Constant -import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Names.{Name, TermName} -import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.StdNames.* +import dotty.tools.dotc.core.Types.* +import dotty.tools.dotc.core.Decorators.* import dotty.tools.dotc.core.TypeErasure -import util.Spans._ -import core.Symbols._ -import ErrorReporting._ -import reporting._ +import util.Spans.* +import core.Symbols.* +import ErrorReporting.* +import dotty.tools.dotc.transform.ValueClasses +import dotty.tools.dotc.transform.TypeUtils.isPrimitiveValueType +import reporting.* object Dynamic { private def isDynamicMethod(name: Name): Boolean = @@ -215,6 +217,12 @@ trait Dynamic { errorTree(tree, em"Structural access not allowed on method $name because it $reason") fun.tpe.widen match { + case tpe: ValueType if ValueClasses.isDerivedValueClass(tpe.classSymbol) => + val underlying = ValueClasses.valueClassUnbox(tpe.classSymbol.asClass).info.resultType.asSeenFrom(tpe, tpe.classSymbol) + val wrapped = structuralCall(nme.selectDynamic, Nil) + val resultTree = if wrapped.tpe.isExactlyAny then New(tpe, wrapped.cast(underlying) :: Nil) else wrapped + resultTree.cast(tpe) + case tpe: ValueType => structuralCall(nme.selectDynamic, Nil).cast(tpe) @@ -236,7 +244,11 @@ trait Dynamic { fail(i"has a parameter type with an unstable erasure") :: Nil else TypeErasure.erasure(tpe).asInstanceOf[MethodType].paramInfos.map(clsOf(_)) - structuralCall(nme.applyDynamic, classOfs).cast(tpe.finalResultType) + val finalTpe = tpe.finalResultType + if ValueClasses.isDerivedValueClass(finalTpe.classSymbol) then + New(finalTpe, structuralCall(nme.applyDynamic, classOfs).cast(finalTpe) + .cast(ValueClasses.valueClassUnbox(finalTpe.classSymbol.asClass).info.resultType.asSeenFrom(finalTpe, finalTpe.classSymbol)) :: Nil) + else structuralCall(nme.applyDynamic, classOfs).cast(finalTpe) } // (@allanrenucci) I think everything below is dead code diff --git a/tests/run/i14340.check b/tests/run/i14340.check new file mode 100644 index 000000000000..db372c9502e9 --- /dev/null +++ b/tests/run/i14340.check @@ -0,0 +1,4 @@ +1 +2 +3 +qux \ No newline at end of file diff --git a/tests/run/i14340.scala b/tests/run/i14340.scala new file mode 100644 index 000000000000..e9d8733ccfa5 --- /dev/null +++ b/tests/run/i14340.scala @@ -0,0 +1,20 @@ +class Foo(val value: Int) extends AnyVal +class Bar[A](val value: A) extends AnyVal + +class Container1 extends reflect.Selectable + +class Container2 extends Selectable: + def selectDynamic(name: String) = Bar(name) + +val cont1 = new Container1: + def foo = new Foo(1) + val bar = new Bar(Foo(2)) + def fooFromInt(i: Int) = new Foo(i) + +val cont2 = (new Container2).asInstanceOf[Container2 { def qux: Bar[String] }] + +@main def Test: Unit = + println(cont1.foo.value) + println(cont1.bar.value.value) + println(cont1.fooFromInt(3).value) + println(cont2.qux.value) \ No newline at end of file From d6ae0cbdfa5e19886cadcec3c8ee02d944a36c8f Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 12 Feb 2023 13:01:30 +0100 Subject: [PATCH 134/657] Allow @implicitNotFound messages as explanations A problem of the @implicitNotFOund mechanism so far was that the user defined message replaced the compiler-generated one, which might lose valuable information. This commit adds an alternative where an @implicitNotFound message that starts with `...` is taken as an explanation (without the ...) enabled under -explain. The compiler-generated message is then kept as the explicit error message. We apply the mechanism for an @implicitNotFound message for `boundary.Label`. This now produces messages like this one: ``` -- [E172] Type Error: tests/neg-custom-args/explain/labelNotFound.scala:2:30 ------------------------------------------- 2 | scala.util.boundary.break(1) // error | ^ |No given instance of type scala.util.boundary.Label[Int] was found for parameter label of method break in object boundary |--------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | A Label is generated from an enclosing `scala.util.boundary` call. | Maybe that boundary is missing? --------------------------------------------------------------------------------------------------------------------- ``` --- .../dotty/tools/dotc/reporting/messages.scala | 193 ++++++++++-------- .../dotty/tools/dotc/CompilationTests.scala | 5 +- library/src/scala/util/boundary.scala | 2 + .../explain/hidden-type-errors.check | 23 +++ .../hidden-type-errors/Macro.scala | 0 .../hidden-type-errors/Test.scala | 0 .../{ => explain}/i11637.check | 4 +- .../{ => explain}/i11637.scala | 0 .../{ => explain}/i15575.check | 4 +- .../{ => explain}/i15575.scala | 0 .../{ => explain}/i16601a.check | 2 +- .../{ => explain}/i16601a.scala | 0 .../explain/labelNotFound.check | 10 + .../explain/labelNotFound.scala | 4 + .../neg-custom-args/hidden-type-errors.check | 28 --- 15 files changed, 148 insertions(+), 127 deletions(-) create mode 100644 tests/neg-custom-args/explain/hidden-type-errors.check rename tests/neg-custom-args/{ => explain}/hidden-type-errors/Macro.scala (100%) rename tests/neg-custom-args/{ => explain}/hidden-type-errors/Test.scala (100%) rename tests/neg-custom-args/{ => explain}/i11637.check (92%) rename tests/neg-custom-args/{ => explain}/i11637.scala (100%) rename tests/neg-custom-args/{ => explain}/i15575.check (87%) rename tests/neg-custom-args/{ => explain}/i15575.scala (100%) rename tests/neg-custom-args/{ => explain}/i16601a.check (89%) rename tests/neg-custom-args/{ => explain}/i16601a.scala (100%) create mode 100644 tests/neg-custom-args/explain/labelNotFound.check create mode 100644 tests/neg-custom-args/explain/labelNotFound.scala delete mode 100644 tests/neg-custom-args/hidden-type-errors.check diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index f41d34b8c17c..fb9eaed094e8 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2562,6 +2562,106 @@ class MissingImplicitArgument( case ambi: AmbiguousImplicits => withoutDisambiguation() case _ => + /** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing + * all occurrences of `${X}` where `X` is in `paramNames` with the + * corresponding shown type in `args`. + */ + def userDefinedErrorString(raw: String, paramNames: List[String], args: List[Type])(using Context): String = + def translate(name: String): Option[String] = + val idx = paramNames.indexOf(name) + if (idx >= 0) Some(i"${args(idx)}") else None + """\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match + case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn + ) + + /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}" + * @param sym Symbol of the annotated type or of the method whose parameter was annotated + * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int + */ + def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type)(using Context): String = + val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym) + userDefinedErrorString( + rawMsg, + paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString), + args = substitutableTypesSymbols.map(_.typeRef).map(substituteType) + ) + + /** Extract a user defined error message from a symbol `sym` + * with an annotation matching the given class symbol `cls`. + */ + def userDefinedMsg(sym: Symbol, cls: Symbol)(using Context) = + for + ann <- sym.getAnnotation(cls) + msg <- ann.argumentConstantString(0) + yield msg + + def userDefinedImplicitNotFoundTypeMessageFor(sym: Symbol)(using Context): Option[String] = + for + rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot) + if Feature.migrateTo3 || sym != defn.Function1 + // Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore + yield + val substituteType = (_: Type).asSeenFrom(pt, sym) + formatAnnotationMessage(rawMsg, sym, substituteType) + + /** Extracting the message from a method parameter, e.g. in + * + * trait Foo + * + * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ??? + */ + def userDefinedImplicitNotFoundParamMessage(using Context): Option[String] = + paramSymWithMethodCallTree.flatMap: (sym, applTree) => + userDefinedMsg(sym, defn.ImplicitNotFoundAnnot).map: rawMsg => + val fn = tpd.funPart(applTree) + val targs = tpd.typeArgss(applTree).flatten + val methodOwner = fn.symbol.owner + val methodOwnerType = tpd.qualifier(fn).tpe + val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType) + val methodTypeArgs = targs.map(_.tpe) + val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs) + formatAnnotationMessage(rawMsg, sym.owner, substituteType) + + def userDefinedImplicitNotFoundTypeMessage(using Context): Option[String] = + def recur(tp: Type): Option[String] = tp match + case tp: TypeRef => + val sym = tp.symbol + userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info)) + case tp: ClassInfo => + tp.baseClasses.iterator + .map(userDefinedImplicitNotFoundTypeMessageFor) + .find(_.isDefined).flatten + case tp: TypeProxy => + recur(tp.superType) + case tp: AndType => + recur(tp.tp1).orElse(recur(tp.tp2)) + case _ => + None + recur(pt) + + /** The implicitNotFound annotation on the parameter, or else on the type. + * implicitNotFound message strings starting with `...` are intended for + * additional explanations, not the message proper. The leading `...` is + * dropped in this case. + * @param explain The message is used for an additional explanation, not + * the message proper. + */ + def userDefinedImplicitNotFoundMessage(explain: Boolean)(using Context): Option[String] = + def filter(msg: Option[String]) = msg match + case Some(str) => + if str.startsWith("...") then + if explain then Some(str.drop(3)) else None + else if explain then None + else msg + case None => None + filter(userDefinedImplicitNotFoundParamMessage) + .orElse(filter(userDefinedImplicitNotFoundTypeMessage)) + + object AmbiguousImplicitMsg { + def unapply(search: SearchSuccess): Option[String] = + userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot) + } + def msg(using Context): String = def formatMsg(shortForm: String)(headline: String = shortForm) = arg match @@ -2585,29 +2685,6 @@ class MissingImplicitArgument( |But ${tpe.explanation}.""" case _ => headline - /** Format `raw` implicitNotFound or implicitAmbiguous argument, replacing - * all occurrences of `${X}` where `X` is in `paramNames` with the - * corresponding shown type in `args`. - */ - def userDefinedErrorString(raw: String, paramNames: List[String], args: List[Type]): String = { - def translate(name: String): Option[String] = { - val idx = paramNames.indexOf(name) - if (idx >= 0) Some(i"${args(idx)}") else None - } - - """\$\{\s*([^}\s]+)\s*\}""".r.replaceAllIn(raw, (_: Regex.Match) match { - case Regex.Groups(v) => quoteReplacement(translate(v).getOrElse("")).nn - }) - } - - /** Extract a user defined error message from a symbol `sym` - * with an annotation matching the given class symbol `cls`. - */ - def userDefinedMsg(sym: Symbol, cls: Symbol) = for { - ann <- sym.getAnnotation(cls) - msg <- ann.argumentConstantString(0) - } yield msg - def location(preposition: String) = if (where.isEmpty) "" else s" $preposition $where" def defaultAmbiguousImplicitMsg(ambi: AmbiguousImplicits) = @@ -2644,39 +2721,6 @@ class MissingImplicitArgument( userDefinedErrorString(raw, params, args) } - /** @param rawMsg Message template with variables, e.g. "Variable A is ${A}" - * @param sym Symbol of the annotated type or of the method whose parameter was annotated - * @param substituteType Function substituting specific types for abstract types associated with variables, e.g A -> Int - */ - def formatAnnotationMessage(rawMsg: String, sym: Symbol, substituteType: Type => Type): String = { - val substitutableTypesSymbols = substitutableTypeSymbolsInScope(sym) - - userDefinedErrorString( - rawMsg, - paramNames = substitutableTypesSymbols.map(_.name.unexpandedName.toString), - args = substitutableTypesSymbols.map(_.typeRef).map(substituteType) - ) - } - - /** Extracting the message from a method parameter, e.g. in - * - * trait Foo - * - * def foo(implicit @annotation.implicitNotFound("Foo is missing") foo: Foo): Any = ??? - */ - def userDefinedImplicitNotFoundParamMessage: Option[String] = paramSymWithMethodCallTree.flatMap { (sym, applTree) => - userDefinedMsg(sym, defn.ImplicitNotFoundAnnot).map { rawMsg => - val fn = tpd.funPart(applTree) - val targs = tpd.typeArgss(applTree).flatten - val methodOwner = fn.symbol.owner - val methodOwnerType = tpd.qualifier(fn).tpe - val methodTypeParams = fn.symbol.paramSymss.flatten.filter(_.isType) - val methodTypeArgs = targs.map(_.tpe) - val substituteType = (_: Type).asSeenFrom(methodOwnerType, methodOwner).subst(methodTypeParams, methodTypeArgs) - formatAnnotationMessage(rawMsg, sym.owner, substituteType) - } - } - /** Extracting the message from a type, e.g. in * * @annotation.implicitNotFound("Foo is missing") @@ -2684,37 +2728,6 @@ class MissingImplicitArgument( * * def foo(implicit foo: Foo): Any = ??? */ - def userDefinedImplicitNotFoundTypeMessage: Option[String] = - def recur(tp: Type): Option[String] = tp match - case tp: TypeRef => - val sym = tp.symbol - userDefinedImplicitNotFoundTypeMessageFor(sym).orElse(recur(tp.info)) - case tp: ClassInfo => - tp.baseClasses.iterator - .map(userDefinedImplicitNotFoundTypeMessageFor) - .find(_.isDefined).flatten - case tp: TypeProxy => - recur(tp.superType) - case tp: AndType => - recur(tp.tp1).orElse(recur(tp.tp2)) - case _ => - None - recur(pt) - - def userDefinedImplicitNotFoundTypeMessageFor(sym: Symbol): Option[String] = - for - rawMsg <- userDefinedMsg(sym, defn.ImplicitNotFoundAnnot) - if Feature.migrateTo3 || sym != defn.Function1 - // Don't inherit "No implicit view available..." message if subtypes of Function1 are not treated as implicit conversions anymore - yield - val substituteType = (_: Type).asSeenFrom(pt, sym) - formatAnnotationMessage(rawMsg, sym, substituteType) - - object AmbiguousImplicitMsg { - def unapply(search: SearchSuccess): Option[String] = - userDefinedMsg(search.ref.symbol, defn.ImplicitAmbiguousAnnot) - } - arg.tpe match case ambi: AmbiguousImplicits => (ambi.alt1, ambi.alt2) match @@ -2728,8 +2741,7 @@ class MissingImplicitArgument( i"""No implicit search was attempted${location("for")} |since the expected type $target is not specific enough""" case _ => - val shortMessage = userDefinedImplicitNotFoundParamMessage - .orElse(userDefinedImplicitNotFoundTypeMessage) + val shortMessage = userDefinedImplicitNotFoundMessage(explain = false) .getOrElse(defaultImplicitNotFoundMessage) formatMsg(shortMessage)() end msg @@ -2758,7 +2770,8 @@ class MissingImplicitArgument( .orElse(noChainConversionsNote(ignoredConvertibleImplicits)) .getOrElse(ctx.typer.importSuggestionAddendum(pt)) - def explain(using Context) = "" + def explain(using Context) = userDefinedImplicitNotFoundMessage(explain = true) + .getOrElse("") end MissingImplicitArgument class CannotBeAccessed(tpe: NamedType, superAccess: Boolean)(using Context) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index dee65e404da9..ed531aa404c2 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -143,6 +143,7 @@ class CompilationTests { compileFilesInDir("tests/neg-custom-args/feature", defaultOptions.and("-Xfatal-warnings", "-feature")), compileFilesInDir("tests/neg-custom-args/no-experimental", defaultOptions.and("-Yno-experimental")), compileFilesInDir("tests/neg-custom-args/captures", defaultOptions.and("-language:experimental.captureChecking")), + compileFilesInDir("tests/neg-custom-args/explain", defaultOptions.and("-explain")), compileFile("tests/neg-custom-args/avoid-warn-deprecation.scala", defaultOptions.and("-Xfatal-warnings", "-feature")), compileFile("tests/neg-custom-args/i3246.scala", scala2CompatMode), compileFile("tests/neg-custom-args/overrideClass.scala", scala2CompatMode), @@ -155,9 +156,6 @@ class CompilationTests { compileFile("tests/neg-custom-args/i1754.scala", allowDeepSubtypes), compileFile("tests/neg-custom-args/i12650.scala", allowDeepSubtypes), compileFile("tests/neg-custom-args/i9517.scala", defaultOptions.and("-Xprint-types")), - compileFile("tests/neg-custom-args/i11637.scala", defaultOptions.and("-explain")), - compileFile("tests/neg-custom-args/i15575.scala", defaultOptions.and("-explain")), - compileFile("tests/neg-custom-args/i16601a.scala", defaultOptions.and("-explain")), compileFile("tests/neg-custom-args/interop-polytypes.scala", allowDeepSubtypes.and("-Yexplicit-nulls")), compileFile("tests/neg-custom-args/conditionalWarnings.scala", allowDeepSubtypes.and("-deprecation").and("-Xfatal-warnings")), compileFilesInDir("tests/neg-custom-args/isInstanceOf", allowDeepSubtypes and "-Xfatal-warnings"), @@ -182,7 +180,6 @@ class CompilationTests { compileFile("tests/neg-custom-args/matchable.scala", defaultOptions.and("-Xfatal-warnings", "-source", "future")), compileFile("tests/neg-custom-args/i7314.scala", defaultOptions.and("-Xfatal-warnings", "-source", "future")), compileFile("tests/neg-custom-args/capt-wf.scala", defaultOptions.and("-language:experimental.captureChecking", "-Xfatal-warnings")), - compileDir("tests/neg-custom-args/hidden-type-errors", defaultOptions.and("-explain")), compileFile("tests/neg-custom-args/i13026.scala", defaultOptions.and("-print-lines")), compileFile("tests/neg-custom-args/i13838.scala", defaultOptions.and("-Ximplicit-search-limit", "1000")), compileFile("tests/neg-custom-args/jdk-9-app.scala", defaultOptions.and("-release:8")), diff --git a/library/src/scala/util/boundary.scala b/library/src/scala/util/boundary.scala index 3c6c6982c7ee..ea21446d9682 100644 --- a/library/src/scala/util/boundary.scala +++ b/library/src/scala/util/boundary.scala @@ -1,4 +1,5 @@ package scala.util +import scala.annotation.implicitNotFound /** A boundary that can be exited by `break` calls. * `boundary` and `break` represent a unified and superior alternative for the @@ -34,6 +35,7 @@ object boundary: /** Labels are targets indicating which boundary will be exited by a `break`. */ + @implicitNotFound("...A Label is generated from an enclosing `scala.util.boundary` call.\nMaybe that boundary is missing?") final class Label[-T] /** Abort current computation and instead return `value` as the value of diff --git a/tests/neg-custom-args/explain/hidden-type-errors.check b/tests/neg-custom-args/explain/hidden-type-errors.check new file mode 100644 index 000000000000..551d1d7b16ba --- /dev/null +++ b/tests/neg-custom-args/explain/hidden-type-errors.check @@ -0,0 +1,23 @@ +-- [E007] Type Mismatch Error: tests/neg-custom-args/explain/hidden-type-errors/Test.scala:6:24 ------------------------ +6 | val x = X.doSomething("XXX") // error + | ^^^^^^^^^^^^^^^^^^^^ + | Found: String + | Required: Int + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | + | Tree: t12717.A.bar("XXX") + | I tried to show that + | String + | conforms to + | Int + | but the comparison trace ended with `false`: + | + | ==> String <: Int + | ==> String <: Int + | <== String <: Int = false + | <== String <: Int = false + | + | The tests were made under the empty constraint + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg-custom-args/hidden-type-errors/Macro.scala b/tests/neg-custom-args/explain/hidden-type-errors/Macro.scala similarity index 100% rename from tests/neg-custom-args/hidden-type-errors/Macro.scala rename to tests/neg-custom-args/explain/hidden-type-errors/Macro.scala diff --git a/tests/neg-custom-args/hidden-type-errors/Test.scala b/tests/neg-custom-args/explain/hidden-type-errors/Test.scala similarity index 100% rename from tests/neg-custom-args/hidden-type-errors/Test.scala rename to tests/neg-custom-args/explain/hidden-type-errors/Test.scala diff --git a/tests/neg-custom-args/i11637.check b/tests/neg-custom-args/explain/i11637.check similarity index 92% rename from tests/neg-custom-args/i11637.check rename to tests/neg-custom-args/explain/i11637.check index 0664a05f4f86..82424396a43b 100644 --- a/tests/neg-custom-args/i11637.check +++ b/tests/neg-custom-args/explain/i11637.check @@ -1,4 +1,4 @@ --- [E057] Type Mismatch Error: tests/neg-custom-args/i11637.scala:11:33 ------------------------------------------------ +-- [E057] Type Mismatch Error: tests/neg-custom-args/explain/i11637.scala:11:33 ---------------------------------------- 11 | var h = new HKT3_1[FunctorImpl](); // error // error | ^ | Type argument test2.FunctorImpl does not conform to upper bound [Generic2[T <: String] <: Set[T]] =>> Any @@ -26,7 +26,7 @@ | | The tests were made under the empty constraint -------------------------------------------------------------------------------------------------------------------- --- [E057] Type Mismatch Error: tests/neg-custom-args/i11637.scala:11:21 ------------------------------------------------ +-- [E057] Type Mismatch Error: tests/neg-custom-args/explain/i11637.scala:11:21 ---------------------------------------- 11 | var h = new HKT3_1[FunctorImpl](); // error // error | ^ | Type argument test2.FunctorImpl does not conform to upper bound [Generic2[T <: String] <: Set[T]] =>> Any diff --git a/tests/neg-custom-args/i11637.scala b/tests/neg-custom-args/explain/i11637.scala similarity index 100% rename from tests/neg-custom-args/i11637.scala rename to tests/neg-custom-args/explain/i11637.scala diff --git a/tests/neg-custom-args/i15575.check b/tests/neg-custom-args/explain/i15575.check similarity index 87% rename from tests/neg-custom-args/i15575.check rename to tests/neg-custom-args/explain/i15575.check index f69111efeb96..e254e0a5e22e 100644 --- a/tests/neg-custom-args/i15575.check +++ b/tests/neg-custom-args/explain/i15575.check @@ -1,4 +1,4 @@ --- [E057] Type Mismatch Error: tests/neg-custom-args/i15575.scala:3:27 ------------------------------------------------- +-- [E057] Type Mismatch Error: tests/neg-custom-args/explain/i15575.scala:3:27 ----------------------------------------- 3 | def bar[T]: Unit = foo[T & Any] // error | ^ | Type argument T & Any does not conform to lower bound Any @@ -18,7 +18,7 @@ | | The tests were made under the empty constraint --------------------------------------------------------------------------------------------------------------------- --- [E057] Type Mismatch Error: tests/neg-custom-args/i15575.scala:7:14 ------------------------------------------------- +-- [E057] Type Mismatch Error: tests/neg-custom-args/explain/i15575.scala:7:14 ----------------------------------------- 7 | val _ = foo[String] // error | ^ | Type argument String does not conform to lower bound CharSequence diff --git a/tests/neg-custom-args/i15575.scala b/tests/neg-custom-args/explain/i15575.scala similarity index 100% rename from tests/neg-custom-args/i15575.scala rename to tests/neg-custom-args/explain/i15575.scala diff --git a/tests/neg-custom-args/i16601a.check b/tests/neg-custom-args/explain/i16601a.check similarity index 89% rename from tests/neg-custom-args/i16601a.check rename to tests/neg-custom-args/explain/i16601a.check index 604f71993ada..63be0d2cd2b2 100644 --- a/tests/neg-custom-args/i16601a.check +++ b/tests/neg-custom-args/explain/i16601a.check @@ -1,4 +1,4 @@ --- [E042] Type Error: tests/neg-custom-args/i16601a.scala:1:27 --------------------------------------------------------- +-- [E042] Type Error: tests/neg-custom-args/explain/i16601a.scala:1:27 ------------------------------------------------- 1 |@main def Test: Unit = new concurrent.ExecutionContext // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ExecutionContext is a trait; it cannot be instantiated diff --git a/tests/neg-custom-args/i16601a.scala b/tests/neg-custom-args/explain/i16601a.scala similarity index 100% rename from tests/neg-custom-args/i16601a.scala rename to tests/neg-custom-args/explain/i16601a.scala diff --git a/tests/neg-custom-args/explain/labelNotFound.check b/tests/neg-custom-args/explain/labelNotFound.check new file mode 100644 index 000000000000..594a838aeeed --- /dev/null +++ b/tests/neg-custom-args/explain/labelNotFound.check @@ -0,0 +1,10 @@ +-- [E172] Type Error: tests/neg-custom-args/explain/labelNotFound.scala:2:30 ------------------------------------------- +2 | scala.util.boundary.break(1) // error + | ^ + |No given instance of type scala.util.boundary.Label[Int] was found for parameter label of method break in object boundary + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | A Label is generated from an enclosing `scala.util.boundary` call. + | Maybe that boundary is missing? + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg-custom-args/explain/labelNotFound.scala b/tests/neg-custom-args/explain/labelNotFound.scala new file mode 100644 index 000000000000..8eeaed90f331 --- /dev/null +++ b/tests/neg-custom-args/explain/labelNotFound.scala @@ -0,0 +1,4 @@ +object Test: + scala.util.boundary.break(1) // error + + diff --git a/tests/neg-custom-args/hidden-type-errors.check b/tests/neg-custom-args/hidden-type-errors.check deleted file mode 100644 index a373e409af2f..000000000000 --- a/tests/neg-custom-args/hidden-type-errors.check +++ /dev/null @@ -1,28 +0,0 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/hidden-type-errors/Test.scala:6:24 -------------------------------- -6 | val x = X.doSomething("XXX") // error - | ^^^^^^^^^^^^^^^^^^^^ - | Found: String - | Required: Int - | This location contains code that was inlined from Test.scala:6 - -Explanation -=========== - -Tree: t12717.A.bar("XXX") - -I tried to show that - String -conforms to - Int -but the comparison trace ended with `false`: - - ==> String <: Int - ==> String <: Int (recurring) - ==> String <: Int (recurring) - <== String <: Int (recurring) = false - <== String <: Int (recurring) = false - <== String <: Int = false - -The tests were made under the empty constraint - -1 error found From 14517886c7acbad688bb14b05fe98078d8c80edf Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 12 Feb 2023 18:51:46 +0100 Subject: [PATCH 135/657] Try to be more subtle when inferring type parameters of class parents The previous scheme was basically like this: 1. If the parent type is an application, infer type parameters from the term parameters during completion of the class. 2. If the parent type is an Ident or Select and it appears in an anonymous class, infer it from the expected type. It could happen in the second case that we infer Nothing since an expected type was missing, but if the parent type had been an application, we would have inferred something else from implicit arguments to the parent constructor. This is a case handled by Scala 2, but not yet by Dotty. To deal with this we have to perform a complicated dance: - Try step (2) above, but back out if the inferred parent type has Nothing as a type argument. - During completion, if the inferred parent has missing type arguments, convert the parent type to an application with () arguments and try that instead. I normally would have thought this is too much sophistry but there are valid use cases that Scala 2 supports and it would be good if we get to parity for these. --- .../dotty/tools/dotc/typer/Inferencing.scala | 9 ++--- .../src/dotty/tools/dotc/typer/Namer.scala | 36 +++++++++++++------ .../src/dotty/tools/dotc/typer/Typer.scala | 13 +++---- tests/neg/i1643.scala | 4 +-- tests/neg/i4820.scala | 2 -- tests/neg/i4820b.scala | 5 --- tests/neg/i4820c.scala | 2 -- tests/pos/i16778.scala | 22 ++++++++++++ tests/pos/i4820.scala | 2 ++ tests/pos/i4820b.scala | 5 +++ 10 files changed, 66 insertions(+), 34 deletions(-) delete mode 100644 tests/neg/i4820.scala delete mode 100644 tests/neg/i4820b.scala delete mode 100644 tests/neg/i4820c.scala create mode 100644 tests/pos/i16778.scala create mode 100644 tests/pos/i4820.scala create mode 100644 tests/pos/i4820b.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index af4174423cbc..c31477d0270e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -307,16 +307,17 @@ object Inferencing { } /** If `tree` has a type lambda type, infer its type parameters by comparing with expected type `pt` */ - def inferTypeParams(tree: Tree, pt: Type)(using Context): Tree = tree.tpe match { + def inferTypeParams(tree: Tree, pt: Type)(using Context): Tree = tree.tpe match case tl: TypeLambda => val (tl1, tvars) = constrained(tl, tree) var tree1 = AppliedTypeTree(tree.withType(tl1), tvars) tree1.tpe <:< pt - fullyDefinedType(tree1.tpe, "template parent", tree.srcPos) - tree1 + if isFullyDefined(tree1.tpe, force = ForceDegree.failBottom) then + tree1 + else + EmptyTree case _ => tree - } def isSkolemFree(tp: Type)(using Context): Boolean = !tp.existsPart(_.isInstanceOf[SkolemType]) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6f85efb0fc8a..af6673086339 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -1453,27 +1453,41 @@ class Namer { typer: Typer => * only if parent type contains uninstantiated type parameters. */ def parentType(parent: untpd.Tree)(using Context): Type = - if (parent.isType) - typedAheadType(parent, AnyTypeConstructorProto).tpe - else { - val (core, targs) = stripApply(parent) match { + + def typedParentApplication(parent: untpd.Tree): Type = + val (core, targs) = stripApply(parent) match case TypeApply(core, targs) => (core, targs) case core => (core, Nil) - } - core match { + core match case Select(New(tpt), nme.CONSTRUCTOR) => val targs1 = targs map (typedAheadType(_)) val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes if (ptype.typeParams.isEmpty) ptype - else { + else if (denot.is(ModuleClass) && denot.sourceModule.isOneOf(GivenOrImplicit)) missingType(denot.symbol, "parent ")(using creationContext) fullyDefinedType(typedAheadExpr(parent).tpe, "class parent", parent.srcPos) - } case _ => UnspecifiedErrorType.assertingErrorsReported - } - } + + def typedParentType(tree: untpd.Tree): tpd.Tree = + val parentTpt = typer.typedType(parent, AnyTypeConstructorProto) + val ptpe = parentTpt.tpe + if ptpe.typeParams.nonEmpty + && ptpe.underlyingClassRef(refinementOK = false).exists + then + // Try to infer type parameters from a synthetic application. + // This might yield new info if implicit parameters are resolved. + // A test case is i16778.scala. + val app = untpd.Apply(untpd.Select(untpd.New(parentTpt), nme.CONSTRUCTOR), Nil) + typedParentApplication(app) + app.getAttachment(TypedAhead).getOrElse(parentTpt) + else + parentTpt + + if parent.isType then typedAhead(parent, typedParentType).tpe + else typedParentApplication(parent) + end parentType /** Check parent type tree `parent` for the following well-formedness conditions: * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) @@ -1607,7 +1621,7 @@ class Namer { typer: Typer => case Some(ttree) => ttree case none => val ttree = typed(tree) - xtree.putAttachment(TypedAhead, ttree) + if !ttree.isEmpty then xtree.putAttachment(TypedAhead, ttree) ttree } } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index a87d6dd7e703..5752409c51c1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -843,14 +843,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer isSkolemFree(pt) && isEligible(pt.underlyingClassRef(refinementOK = false))) templ1 = cpy.Template(templ)(parents = untpd.TypeTree(pt) :: Nil) - templ1.parents foreach { - case parent: RefTree => - typedAhead(parent, tree => inferTypeParams(typedType(tree), pt)) - case _ => - } - val x = tpnme.ANON_CLASS - val clsDef = TypeDef(x, templ1).withFlags(Final | Synthetic) - typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt) + for case parent: RefTree <- templ1.parents do + typedAhead(parent, tree => inferTypeParams(typedType(tree), pt)) + val anon = tpnme.ANON_CLASS + val clsDef = TypeDef(anon, templ1).withFlags(Final | Synthetic) + typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(anon), Nil)), pt) case _ => var tpt1 = typedType(tree.tpt) val tsym = tpt1.tpe.underlyingClassRef(refinementOK = false).typeSymbol diff --git a/tests/neg/i1643.scala b/tests/neg/i1643.scala index a10422de6eab..1745539d73f5 100644 --- a/tests/neg/i1643.scala +++ b/tests/neg/i1643.scala @@ -1,4 +1,4 @@ -trait T extends Array { // error // error +trait T extends Array { // error def t1(as: String*): Array[String] = { varargs1(as*) } // error def t2(as: String*): Array[String] = { super.varargs1(as*) } // error } @@ -7,7 +7,7 @@ class C extends Base_1 { // error def c2(as: String*): Array[String] = { super.varargs1(as*) } // error } object Test extends App { - val t = new T {} // error + val t = new T {} println(t.t1("a", "b").mkString(",")) println(t.t2("a", "b").mkString(",")) val c = new C {} diff --git a/tests/neg/i4820.scala b/tests/neg/i4820.scala deleted file mode 100644 index e19183b17b14..000000000000 --- a/tests/neg/i4820.scala +++ /dev/null @@ -1,2 +0,0 @@ -class Foo[A] -class Bar[A] extends Foo // error diff --git a/tests/neg/i4820b.scala b/tests/neg/i4820b.scala deleted file mode 100644 index 4a7b3da3fb1b..000000000000 --- a/tests/neg/i4820b.scala +++ /dev/null @@ -1,5 +0,0 @@ -trait SetOps[A, +C <: SetOps[A, C]] { - def concat(that: Iterable[A]): C = ??? -} - -class Set1[A] extends SetOps // error: should be SetOps[A, Set1[A]] diff --git a/tests/neg/i4820c.scala b/tests/neg/i4820c.scala deleted file mode 100644 index 6956b23363b5..000000000000 --- a/tests/neg/i4820c.scala +++ /dev/null @@ -1,2 +0,0 @@ -trait Foo[A] -class Bar[A] extends Foo // error \ No newline at end of file diff --git a/tests/pos/i16778.scala b/tests/pos/i16778.scala new file mode 100644 index 000000000000..426f3c86c0bd --- /dev/null +++ b/tests/pos/i16778.scala @@ -0,0 +1,22 @@ +final abstract class ForcedRecompilationToken[T] + +object ForcedRecompilationToken { + implicit def materialize: ForcedRecompilationToken["x"] = null.asInstanceOf[ForcedRecompilationToken["x"]] +} + +class PluginDef[T](implicit val recompilationToken: ForcedRecompilationToken[T]) + +object X { + val no = { + final class anon extends PluginDef {} // was: missing type parameters + new anon + } + + val bad = new PluginDef {} // was: No given instance + val good = new PluginDef() {} // ok +} + +object DependingPlugin { + class NestedDoublePlugin extends PluginDef + object NestedDoublePlugin extends PluginDef +} diff --git a/tests/pos/i4820.scala b/tests/pos/i4820.scala new file mode 100644 index 000000000000..8d368d150a00 --- /dev/null +++ b/tests/pos/i4820.scala @@ -0,0 +1,2 @@ +class Foo[A] +class Bar[A] extends Foo // was error, now expanded to Foo[Nothing] diff --git a/tests/pos/i4820b.scala b/tests/pos/i4820b.scala new file mode 100644 index 000000000000..a1c7d54f0c76 --- /dev/null +++ b/tests/pos/i4820b.scala @@ -0,0 +1,5 @@ +trait SetOps[A, +C <: SetOps[A, C]] { + def concat(that: Iterable[A]): C = ??? +} + +class Set1[A] extends SetOps // ideally should be SetOps[A, Set1[A]], but SetOps[Nothing, Nothin] is inferred From 79bad18adb6aad1c4cff1f105c63d24ade73fa4f Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 12 Feb 2023 19:37:51 +0100 Subject: [PATCH 136/657] Fix repl test --- compiler/test-resources/repl/i7644 | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/compiler/test-resources/repl/i7644 b/compiler/test-resources/repl/i7644 index 8ceaf8b00804..786823073470 100644 --- a/compiler/test-resources/repl/i7644 +++ b/compiler/test-resources/repl/i7644 @@ -5,11 +5,7 @@ scala> class T extends CanEqual | Cannot extend sealed trait CanEqual in a different source file | | longer explanation available when compiling with `-explain` --- [E056] Syntax Error: -------------------------------------------------------- -1 | class T extends CanEqual - | ^^^^^^^^ - | Missing type parameter for CanEqual -2 errors found +1 error found scala> class T extends CanEqual -- [E112] Syntax Error: -------------------------------------------------------- 1 | class T extends CanEqual @@ -17,8 +13,5 @@ scala> class T extends CanEqual | Cannot extend sealed trait CanEqual in a different source file | | longer explanation available when compiling with `-explain` --- [E056] Syntax Error: -------------------------------------------------------- -1 | class T extends CanEqual - | ^^^^^^^^ - | Missing type parameter for CanEqual -2 errors found +1 error found + From 2d641ecc48120faf03c9ad0dbc73c637fc8dad5a Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 3 Feb 2023 13:55:56 +0000 Subject: [PATCH 137/657] Patmat: Use less type variables in prefix inference In code like: class Outer: sealed trait Foo case class Bar() extends Foo def mat(foo: Foo) = foo match case Bar() => When in the course of decomposing the scrutinee's type, which is `Outer.this.Foo`, we're trying to instantiate subclass `Outer.this.Bar`, the `Outer.this` is fixed - it needn't be inferred, via type variables and type bounds. Cutting down on type variables, particularly when GADT symbols are also present, can really speed up the operation, including making code that used to hang forever compile speedily. --- .../src/dotty/tools/dotc/core/TypeOps.scala | 53 +++++++++++++------ tests/patmat/i12408.check | 2 +- tests/pos/i16785.scala | 11 ++++ 3 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 tests/pos/i16785.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index d9da11c561e8..c91412988e82 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -2,7 +2,7 @@ package dotty.tools package dotc package core -import Contexts._, Types._, Symbols._, Names._, Flags._ +import Contexts._, Types._, Symbols._, Names._, NameKinds.*, Flags._ import SymDenotations._ import util.Spans._ import util.Stats @@ -839,24 +839,51 @@ object TypeOps: } } - // Prefix inference, replace `p.C.this.Child` with `X.Child` where `X <: p.C` - // Note: we need to strip ThisType in `p` recursively. + /** Gather GADT symbols and `ThisType`s found in `tp2`, ie. the scrutinee. */ + object TraverseTp2 extends TypeTraverser: + val thisTypes = util.HashSet[ThisType]() + val gadtSyms = new mutable.ListBuffer[Symbol] + + def traverse(tp: Type) = { + val tpd = tp.dealias + if tpd ne tp then traverse(tpd) + else tp match + case tp: ThisType if !tp.tref.symbol.isStaticOwner && !thisTypes.contains(tp) => + thisTypes += tp + traverseChildren(tp.tref) + case tp: TypeRef if tp.symbol.isAbstractOrParamType => + gadtSyms += tp.symbol + traverseChildren(tp) + case _ => + traverseChildren(tp) + } + TraverseTp2.traverse(tp2) + val thisTypes = TraverseTp2.thisTypes + val gadtSyms = TraverseTp2.gadtSyms.toList + + // Prefix inference, given `p.C.this.Child`: + // 1. return it as is, if `C.this` is found in `tp`, i.e. the scrutinee; or + // 2. replace it with `X.Child` where `X <: p.C`, stripping ThisType in `p` recursively. // - // See tests/patmat/i3938.scala + // See tests/patmat/i3938.scala, tests/pos/i15029.more.scala, tests/pos/i16785.scala class InferPrefixMap extends TypeMap { var prefixTVar: Type | Null = null def apply(tp: Type): Type = tp match { - case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner => + case tp @ ThisType(tref) if !tref.symbol.isStaticOwner => val symbol = tref.symbol - if (symbol.is(Module)) + if thisTypes.contains(tp) then + prefixTVar = tp // e.g. tests/pos/i16785.scala, keep Outer.this + prefixTVar.uncheckedNN + else if symbol.is(Module) then TermRef(this(tref.prefix), symbol.sourceModule) else if (prefixTVar != null) this(tref) else { prefixTVar = WildcardType // prevent recursive call from assigning it - val tvars = tref.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } + // e.g. tests/pos/i15029.more.scala, create a TypeVar for `Instances`' B, so we can disregard `Ints` + val tvars = tref.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds, DepParamName.fresh(tparam.paramName)) } val tref2 = this(tref.applyIfParameterized(tvars)) - prefixTVar = newTypeVar(TypeBounds.upper(tref2)) + prefixTVar = newTypeVar(TypeBounds.upper(tref2), DepParamName.fresh(tref.name)) prefixTVar.uncheckedNN } case tp => mapOver(tp) @@ -864,15 +891,11 @@ object TypeOps: } val inferThisMap = new InferPrefixMap - val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } + val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds, DepParamName.fresh(tparam.paramName)) } val protoTp1 = inferThisMap.apply(tp1).appliedTo(tvars) - val getAbstractSymbols = new TypeAccumulator[List[Symbol]]: - def apply(xs: List[Symbol], tp: Type) = tp.dealias match - case tp: TypeRef if tp.symbol.exists && !tp.symbol.isClass => foldOver(tp.symbol :: xs, tp) - case tp => foldOver(xs, tp) - val syms2 = getAbstractSymbols(Nil, tp2).reverse - if syms2.nonEmpty then ctx.gadtState.addToConstraint(syms2) + if gadtSyms.nonEmpty then + ctx.gadtState.addToConstraint(gadtSyms) // If parent contains a reference to an abstract type, then we should // refine subtype checking to eliminate abstract types according to diff --git a/tests/patmat/i12408.check b/tests/patmat/i12408.check index ada7b8c21fa8..60acc2cba84e 100644 --- a/tests/patmat/i12408.check +++ b/tests/patmat/i12408.check @@ -1,2 +1,2 @@ -13: Pattern Match Exhaustivity: X[] & (X.this : X[T]).A(_), X[] & (X.this : X[T]).C(_) +13: Pattern Match Exhaustivity: A(_), C(_) 21: Pattern Match diff --git a/tests/pos/i16785.scala b/tests/pos/i16785.scala new file mode 100644 index 000000000000..1cfabf5a4312 --- /dev/null +++ b/tests/pos/i16785.scala @@ -0,0 +1,11 @@ +class VarImpl[Lbl, A] + +class Outer[|*|[_, _], Lbl1]: + type Var[A1] = VarImpl[Lbl1, A1] + + sealed trait Foo[G] + case class Bar[T, U]() + extends Foo[Var[T] |*| Var[U]] + + def go[X](scr: Foo[Var[X]]): Unit = scr match // was: compile hang + case Bar() => () From ea87c0895c258419218938bf4ac9d811dc051cae Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 13 Feb 2023 11:29:12 +0100 Subject: [PATCH 138/657] Tweak withDenot for overloaded denotations When creating a NamedType with a given overloaded denotation, make sure that the type has a Name as designator. This prevents accidentally overwriting a more precise symbolic TermRef that refers to one specific alternative of the denotation. This might be enough to fix #16884. It would be great if we could maintain the general invariant that NamedTypes with overloaded denotations always have names as designators. But that looks very hard when we take into account that we need to update named types to new runs. A type might have a single denotation in one round and an overloaded one in the next. --- .../src/dotty/tools/dotc/core/Types.scala | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d2fc225ff19d..0908574b0308 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2644,8 +2644,12 @@ object Types { } /** A reference like this one, but with the given symbol, if it exists */ - final def withSym(sym: Symbol)(using Context): ThisType = - if ((designator ne sym) && sym.exists) NamedType(prefix, sym).asInstanceOf[ThisType] + private def withSym(sym: Symbol)(using Context): ThisType = + if designator ne sym then NamedType(prefix, sym).asInstanceOf[ThisType] + else this + + private def withName(name: Name)(using Context): ThisType = + if designator ne name then NamedType(prefix, name).asInstanceOf[ThisType] else this /** A reference like this one, but with the given denotation, if it exists. @@ -2656,6 +2660,8 @@ object Types { * does not have a currently known denotation. * 3. The current designator is a name and the new symbolic named type * has the same info as the current info + * Returns a new named type with a name as designator if the denotation is + * overloaded and the name is different from the current designator. * Otherwise the current denotation is overwritten with the given one. * * Note: (2) and (3) are a "lock in mechanism" where a reference with a name as @@ -2669,13 +2675,16 @@ object Types { */ final def withDenot(denot: Denotation)(using Context): ThisType = if denot.exists then - val adapted = withSym(denot.symbol) + val adapted = + if denot.symbol.exists then withSym(denot.symbol) + else if denot.isOverloaded then withName(denot.name) + else this val result = - if (adapted.eq(this) + if adapted.eq(this) || designator.isInstanceOf[Symbol] || !adapted.denotationIsCurrent - || adapted.info.eq(denot.info)) - adapted + || adapted.info.eq(denot.info) + then adapted else this val lastDenot = result.lastDenotation denot match From 00d3079f63bc226967294f148b4609e036739aba Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 9 Feb 2023 10:18:19 +0100 Subject: [PATCH 139/657] Add regression test Fixes #15475 --- tests/pos-macros/i15475.scala | 13 +++++++++++++ tests/pos-macros/i15475a/Macro_1.scala | 17 +++++++++++++++++ tests/pos-macros/i15475a/Test_2.scala | 15 +++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 tests/pos-macros/i15475.scala create mode 100644 tests/pos-macros/i15475a/Macro_1.scala create mode 100644 tests/pos-macros/i15475a/Test_2.scala diff --git a/tests/pos-macros/i15475.scala b/tests/pos-macros/i15475.scala new file mode 100644 index 000000000000..20993cd46d15 --- /dev/null +++ b/tests/pos-macros/i15475.scala @@ -0,0 +1,13 @@ +def test = + transform { + val a: Seq[Generic[?]] = ??? + a.foreach { to => + to.mthd() + } + } + +transparent inline def transform[T](expr: T): T = ??? + +trait Generic[+T] { + def mthd(): Generic[T] = ??? +} diff --git a/tests/pos-macros/i15475a/Macro_1.scala b/tests/pos-macros/i15475a/Macro_1.scala new file mode 100644 index 000000000000..b1bd676e7e17 --- /dev/null +++ b/tests/pos-macros/i15475a/Macro_1.scala @@ -0,0 +1,17 @@ +package x + +import scala.quoted.* + + +transparent inline def xtransform[T](inline expr:T) = ${ + X.transform('expr) +} + +object X { + + def transform[T:Type](x: Expr[T])(using Quotes):Expr[T] = { + import quotes.reflect.* + x + } + +} diff --git a/tests/pos-macros/i15475a/Test_2.scala b/tests/pos-macros/i15475a/Test_2.scala new file mode 100644 index 000000000000..7757a14950de --- /dev/null +++ b/tests/pos-macros/i15475a/Test_2.scala @@ -0,0 +1,15 @@ +package x + +def hello = { + xtransform { + val a: Seq[Generic[?]] = null + a + .foreach { to => + to.mthd() + } + } +} + +trait Generic[+T] { + def mthd(): Generic[T] = this +} From 5c69746d771463924d5a7c0d10393f817865a238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Mon, 13 Feb 2023 17:00:03 +0100 Subject: [PATCH 140/657] Run CI on merge queue --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 46a4e6064d07..27314d188df7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,6 +15,7 @@ on: ## - or neither of them. ## But it's important to not have only one or the other. pull_request: + merge_group: schedule: - cron: '0 3 * * *' # Every day at 3 AM workflow_dispatch: From 46e82dd4bb32faa65c5d85c1f10b91eaca9256eb Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 13 Feb 2023 17:37:56 +0100 Subject: [PATCH 141/657] Refine infoDependsOnPrefix Make infoDependsOnPrefix return true even if the prefix is the ThisType of the enclosing class, in case that class has a selftype which refines the binding of an abstract type. This allows to implement withDenot without overwriting a NamedType's state. --- .../src/dotty/tools/dotc/core/Types.scala | 69 ++++++++++--------- .../dotty/tools/dotc/reporting/messages.scala | 2 +- .../test/dotc/pos-test-pickling.blacklist | 1 + 3 files changed, 39 insertions(+), 33 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 0908574b0308..5c16ef05f48f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2501,10 +2501,36 @@ object Types { /** A reference with the initial symbol in `symd` has an info that * might depend on the given prefix. + * Note: If T is an abstract type in trait or class C, its info depends + * even on C.this if class C has a self type that refines the info of T. + * Currently, "refines" means an actual refinement type that constrains the + * name `T`. We should try to extend that also to other classes that introduce + * a new bining for `T`. Furthermore, we should also treat term members + * in this way. */ private def infoDependsOnPrefix(symd: SymDenotation, prefix: Type)(using Context): Boolean = + + def refines(tp: Type, name: Name): Boolean = tp match + case AndType(tp1, tp2) => refines(tp1, name) || refines(tp2, name) + case RefinedType(parent, rname, _) => rname == name || refines(parent, name) + case tp: RecType => refines(tp.parent, name) + case _ => false + + def givenSelfTypeOrCompleter(cls: Symbol) = cls.infoOrCompleter match + case cinfo: ClassInfo => + cinfo.selfInfo match + case sym: Symbol => sym.infoOrCompleter + case tpe: Type => tpe + case _ => NoType + symd.maybeOwner.membersNeedAsSeenFrom(prefix) && !symd.is(NonMember) - || prefix.isInstanceOf[Types.ThisType] && symd.is(Opaque) // see pos/i11277.scala for a test where this matters + || prefix.match + case prefix: Types.ThisType => + symd.isAbstractType + && prefix.sameThis(symd.maybeOwner.thisType) + && refines(givenSelfTypeOrCompleter(prefix.cls), symd.name) + case _ => false + end infoDependsOnPrefix /** Is this a reference to a class or object member with an info that might depend * on the prefix? @@ -2653,25 +2679,11 @@ object Types { else this /** A reference like this one, but with the given denotation, if it exists. - * Returns a new named type with the denotation's symbol if that symbol exists, and - * one of the following alternatives applies: - * 1. The current designator is a symbol and the symbols differ, or - * 2. The current designator is a name and the new symbolic named type - * does not have a currently known denotation. - * 3. The current designator is a name and the new symbolic named type - * has the same info as the current info - * Returns a new named type with a name as designator if the denotation is - * overloaded and the name is different from the current designator. - * Otherwise the current denotation is overwritten with the given one. - * - * Note: (2) and (3) are a "lock in mechanism" where a reference with a name as - * designator can turn into a symbolic reference. - * - * Note: This is a subtle dance to keep the balance between going to symbolic - * references as much as we can (since otherwise we'd risk getting cycles) - * and to still not lose any type info in the denotation (since symbolic - * references often recompute their info directly from the symbol's info). - * A test case is neg/opaque-self-encoding.scala. + * Returns a new named type with the denotation's symbol as designator + * if that symbol exists and it is different from the current designator. + * Returns a new named type with the denotations's name as designator + * if the denotation is overloaded and its name is different from the + * current designator. */ final def withDenot(denot: Denotation)(using Context): ThisType = if denot.exists then @@ -2679,14 +2691,7 @@ object Types { if denot.symbol.exists then withSym(denot.symbol) else if denot.isOverloaded then withName(denot.name) else this - val result = - if adapted.eq(this) - || designator.isInstanceOf[Symbol] - || !adapted.denotationIsCurrent - || adapted.info.eq(denot.info) - then adapted - else this - val lastDenot = result.lastDenotation + val lastDenot = adapted.lastDenotation denot match case denot: SymDenotation if denot.validFor.firstPhaseId < ctx.phase.id @@ -2696,15 +2701,15 @@ object Types { // In this case the new SymDenotation might be valid for all phases, which means // we would not recompute the denotation when travelling to an earlier phase, maybe // in the next run. We fix that problem by creating a UniqueRefDenotation instead. - core.println(i"overwrite ${result.toString} / ${result.lastDenotation}, ${result.lastDenotation.getClass} with $denot at ${ctx.phaseId}") - result.setDenot( + core.println(i"overwrite ${adapted.toString} / ${adapted.lastDenotation}, ${adapted.lastDenotation.getClass} with $denot at ${ctx.phaseId}") + adapted.setDenot( UniqueRefDenotation( denot.symbol, denot.info, Period(ctx.runId, ctx.phaseId, denot.validFor.lastPhaseId), this.prefix)) case _ => - result.setDenot(denot) - result.asInstanceOf[ThisType] + adapted.setDenot(denot) + adapted.asInstanceOf[ThisType] else // don't assign NoDenotation, we might need to recover later. Test case is pos/avoid.scala. this diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index f41d34b8c17c..d7920e1f1b36 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -245,7 +245,7 @@ extends NotFoundMsg(MissingIdentID) { } } -class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) +class TypeMismatch(found: Type, expected: Type, inTree: Option[untpd.Tree], addenda: => String*)(using Context) extends TypeMismatchMsg(found, expected)(TypeMismatchID): def msg(using Context) = diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index cdbad2160f2a..5f7221ee0ce9 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -19,6 +19,7 @@ i12299a.scala i13871.scala i15181.scala i15922.scala +t5031_2.scala # Tree is huge and blows stack for printing Text i7034.scala From c830ad26733a633a6d58b93e9bd66ff7b67b65ed Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 13 Feb 2023 17:51:53 +0100 Subject: [PATCH 142/657] Also consider bindings in classes when computing "refines" --- compiler/src/dotty/tools/dotc/core/Types.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 5c16ef05f48f..dc5165e7ad7c 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2503,17 +2503,17 @@ object Types { * might depend on the given prefix. * Note: If T is an abstract type in trait or class C, its info depends * even on C.this if class C has a self type that refines the info of T. - * Currently, "refines" means an actual refinement type that constrains the - * name `T`. We should try to extend that also to other classes that introduce - * a new bining for `T`. Furthermore, we should also treat term members - * in this way. + * We should also treat term members in this way. */ private def infoDependsOnPrefix(symd: SymDenotation, prefix: Type)(using Context): Boolean = def refines(tp: Type, name: Name): Boolean = tp match case AndType(tp1, tp2) => refines(tp1, name) || refines(tp2, name) case RefinedType(parent, rname, _) => rname == name || refines(parent, name) - case tp: RecType => refines(tp.parent, name) + case tp: ClassInfo => + val other = tp.cls.infoOrCompleter.nonPrivateMember(name) + other.exists && other.symbol != symd.symbol + case tp: TypeProxy => refines(tp.underlying, name) case _ => false def givenSelfTypeOrCompleter(cls: Symbol) = cls.infoOrCompleter match From 5a8e53e06376a60fb55ada66aab9e23a971c5cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Mon, 13 Feb 2023 19:13:12 +0100 Subject: [PATCH 143/657] Run CI on merge queue --- .github/workflows/ci.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 27314d188df7..b70cc3bb9e70 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -98,6 +98,7 @@ jobs: - ${{ github.workspace }}/../../cache/general:/root/.cache if: "github.event_name == 'schedule' && github.repository == 'lampepfl/dotty' || github.event_name == 'push' + || github.event_name == 'merge_group' || ( github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') @@ -143,6 +144,7 @@ jobs: github.event_name == 'push' && github.ref != 'refs/heads/main' ) + || github.event_name == 'merge_group' || ( github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') @@ -219,6 +221,7 @@ jobs: - ${{ github.workspace }}/../../cache/general:/root/.cache if: "github.event_name == 'schedule' && github.repository == 'lampepfl/dotty' || github.event_name == 'push' + || github.event_name == 'merge_group' || ( github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') @@ -259,6 +262,7 @@ jobs: - ${{ github.workspace }}/../../cache/general:/root/.cache if: "github.event_name == 'schedule' && github.repository == 'lampepfl/dotty' || github.event_name == 'push' + || github.event_name == 'merge_group' || ( github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') @@ -307,6 +311,7 @@ jobs: - ${{ github.workspace }}/../../cache/general:/root/.cache if: "github.event_name == 'schedule' && github.repository == 'lampepfl/dotty' || github.event_name == 'push' + || github.event_name == 'merge_group' || ( github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') @@ -355,6 +360,7 @@ jobs: - ${{ github.workspace }}/../../cache/general:/root/.cache if: "github.event_name == 'schedule' && github.repository == 'lampepfl/dotty' || github.event_name == 'push' + || github.event_name == 'merge_group' || ( github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') From 8d65f197de88e46bcfe7627c06e5d06792decb8e Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 13 Feb 2023 19:25:25 +0100 Subject: [PATCH 144/657] Also consider non-final term members with self type refinements Also consider non-final term members with self type refinements for infoDependsOnPrefix --- .../src/dotty/tools/dotc/core/Types.scala | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index dc5165e7ad7c..5ef6dbd39335 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2501,20 +2501,24 @@ object Types { /** A reference with the initial symbol in `symd` has an info that * might depend on the given prefix. - * Note: If T is an abstract type in trait or class C, its info depends - * even on C.this if class C has a self type that refines the info of T. - * We should also treat term members in this way. + * Note: If M is an abstract type or non-final term member in trait or class C, + * its info depends even on C.this if class C has a self type that refines + * the info of M. */ private def infoDependsOnPrefix(symd: SymDenotation, prefix: Type)(using Context): Boolean = def refines(tp: Type, name: Name): Boolean = tp match - case AndType(tp1, tp2) => refines(tp1, name) || refines(tp2, name) - case RefinedType(parent, rname, _) => rname == name || refines(parent, name) + case AndType(tp1, tp2) => + refines(tp1, name) || refines(tp2, name) + case RefinedType(parent, rname, _) => + rname == name || refines(parent, name) case tp: ClassInfo => - val other = tp.cls.infoOrCompleter.nonPrivateMember(name) - other.exists && other.symbol != symd.symbol - case tp: TypeProxy => refines(tp.underlying, name) - case _ => false + val otherd = tp.cls.nonPrivateMembersNamed(name) + otherd.exists && !otherd.containsSym(symd.symbol) + case tp: TypeProxy => + refines(tp.underlying, name) + case _ => + false def givenSelfTypeOrCompleter(cls: Symbol) = cls.infoOrCompleter match case cinfo: ClassInfo => @@ -2526,7 +2530,10 @@ object Types { symd.maybeOwner.membersNeedAsSeenFrom(prefix) && !symd.is(NonMember) || prefix.match case prefix: Types.ThisType => - symd.isAbstractType + (symd.isAbstractType + || symd.isTerm + && !symd.flagsUNSAFE.isOneOf(Module | Final | Param) + && !symd.maybeOwner.isEffectivelyFinal) && prefix.sameThis(symd.maybeOwner.thisType) && refines(givenSelfTypeOrCompleter(prefix.cls), symd.name) case _ => false From 1389bb86df39abf9dbe88a568aee4fd538128cf2 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 13 Feb 2023 22:23:12 +0000 Subject: [PATCH 145/657] Avoid bidirectional GADT typebounds from fullBounds --- .../tools/dotc/core/GadtConstraint.scala | 22 +++++++- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/pos/i14287.min.scala | 7 +++ tests/pos/i14287.scala | 16 ++++++ tests/pos/i15523.avoid.scala | 8 +++ tests/pos/i15523.scala | 7 +++ tests/pos/i16777.scala | 52 +++++++++++++++++++ 7 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i14287.min.scala create mode 100644 tests/pos/i14287.scala create mode 100644 tests/pos/i15523.avoid.scala create mode 100644 tests/pos/i15523.scala create mode 100644 tests/pos/i16777.scala diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index a863a982a44d..211c8e637b1f 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -71,12 +71,26 @@ class GadtConstraint private ( externalize(constraint.nonParamBounds(param)).bounds def fullLowerBound(param: TypeParamRef)(using Context): Type = - constraint.minLower(param).foldLeft(nonParamBounds(param).lo) { + val self = externalize(param) + constraint.minLower(param).filterNot { p => + val sym = paramSymbol(p) + sym.name.is(NameKinds.UniqueName) && { + val hi = sym.info.hiBound + !hi.isExactlyAny && self <:< hi + } + }.foldLeft(nonParamBounds(param).lo) { (t, u) => t | externalize(u) } def fullUpperBound(param: TypeParamRef)(using Context): Type = - constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (t, u) => + val self = externalize(param) + constraint.minUpper(param).filterNot { p => + val sym = paramSymbol(p) + sym.name.is(NameKinds.UniqueName) && { + val lo = sym.info.loBound + !lo.isExactlyNothing && lo <:< self + } + }.foldLeft(nonParamBounds(param).hi) { (t, u) => val eu = externalize(u) // Any as the upper bound means "no bound", but if F is higher-kinded, // Any & F = F[_]; this is wrong for us so we need to short-circuit @@ -96,6 +110,10 @@ class GadtConstraint private ( def tvarOrError(sym: Symbol)(using Context): TypeVar = mapping(sym).ensuring(_ != null, i"not a constrainable symbol: $sym").uncheckedNN + private def paramSymbol(p: TypeParamRef): Symbol = reverseMapping(p) match + case sym: Symbol => sym + case null => NoSymbol + @tailrec final def stripInternalTypeVar(tp: Type): Type = tp match case tv: TypeVar => val inst = constraint.instType(tv) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index a87d6dd7e703..328039fd8a41 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1765,7 +1765,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else report.error(new DuplicateBind(b, cdef), b.srcPos) if (!ctx.isAfterTyper) { val bounds = ctx.gadt.fullBounds(sym) - if (bounds != null) sym.info = bounds + if (bounds != null) sym.info = checkNonCyclic(sym, bounds, reportErrors = true) } b case t: UnApply if t.symbol.is(Inline) => Inlines.inlinedUnapply(t) diff --git a/tests/pos/i14287.min.scala b/tests/pos/i14287.min.scala new file mode 100644 index 000000000000..8f7773ab0ac8 --- /dev/null +++ b/tests/pos/i14287.min.scala @@ -0,0 +1,7 @@ +enum Foo[+F[_]]: + case Bar[B[_]](value: Foo[B]) extends Foo[B] + +class Test: + def test[X[_]](foo: Foo[X]): Foo[X] = foo match + case Foo.Bar(Foo.Bar(x)) => Foo.Bar(x) + case _ => foo diff --git a/tests/pos/i14287.scala b/tests/pos/i14287.scala new file mode 100644 index 000000000000..1291dc8adefc --- /dev/null +++ b/tests/pos/i14287.scala @@ -0,0 +1,16 @@ +// scalac: -Yno-deep-subtypes +enum Free[+F[_], A]: + case Return(a: A) + case Suspend(s: F[A]) + case FlatMap[F[_], A, B]( + s: Free[F, A], + f: A => Free[F, B]) extends Free[F, B] + + def flatMap[F2[x] >: F[x], B](f: A => Free[F2,B]): Free[F2,B] = + FlatMap(this, f) + + @scala.annotation.tailrec + final def step: Free[F, A] = this match + case FlatMap(FlatMap(fx, f), g) => fx.flatMap(x => f(x).flatMap(y => g(y))).step + case FlatMap(Return(x), f) => f(x).step + case _ => this diff --git a/tests/pos/i15523.avoid.scala b/tests/pos/i15523.avoid.scala new file mode 100644 index 000000000000..afbfc1a69d60 --- /dev/null +++ b/tests/pos/i15523.avoid.scala @@ -0,0 +1,8 @@ +// scalac: -Werror +// like the original, but with a case body `a` +// which caused type avoidance to infinitely recurse +sealed trait Parent +final case class Leaf[A, B >: A](a: A, b: B) extends Parent + +def run(x: Parent) = x match + case Leaf(a, _) => a diff --git a/tests/pos/i15523.scala b/tests/pos/i15523.scala new file mode 100644 index 000000000000..cf63613c29ac --- /dev/null +++ b/tests/pos/i15523.scala @@ -0,0 +1,7 @@ +// scalac: -Werror +sealed trait Parent +final case class Leaf[A, B >: A](a: A, b: B) extends Parent + +def run(x: Parent): Unit = x match { + case Leaf(a, b) => +} diff --git a/tests/pos/i16777.scala b/tests/pos/i16777.scala new file mode 100644 index 000000000000..4218aea29d9f --- /dev/null +++ b/tests/pos/i16777.scala @@ -0,0 +1,52 @@ +// scalac: -Ykind-projector:underscores + +sealed abstract class Free[+S[_, _], +E, +A] { + @inline final def flatMap[S1[e, a] >: S[e, a], B, E1 >: E](fun: A => Free[S1, E1, B]): Free[S1, E1, B] = Free.FlatMapped[S1, E, E1, A, B](this, fun) + @inline final def map[B](fun: A => B): Free[S, E, B] = flatMap(a => Free.pure[S, B](fun(a))) + @inline final def as[B](as: => B): Free[S, E, B] = map(_ => as) + @inline final def *>[S1[e, a] >: S[e, a], B, E1 >: E](sc: Free[S1, E1, B]): Free[S1, E1, B] = flatMap(_ => sc) + @inline final def <*[S1[e, a] >: S[e, a], B, E1 >: E](sc: Free[S1, E1, B]): Free[S1, E1, A] = flatMap(r => sc.as(r)) + + @inline final def void: Free[S, E, Unit] = map(_ => ()) + + // FIXME: Scala 3.1.4 bug: false unexhaustive match warning + /// @nowarn("msg=pattern case: Free.FlatMapped") + @inline final def foldMap[S1[e, a] >: S[e, a], G[+_, +_]](transform: S1 ~>> G)(implicit G: Monad2[G]): G[E, A] = { + this match { + case Free.Pure(a) => G.pure(a) + case Free.Suspend(a) => transform.apply(a) + case Free.FlatMapped(sub, cont) => + sub match { + case Free.FlatMapped(sub2, cont2) => sub2.flatMap(a => cont2(a).flatMap(cont)).foldMap(transform) + case another => G.flatMap(another.foldMap(transform))(cont(_).foldMap(transform)) + } + } + } +} + +trait ~>>[-F[_, _], +G[_, _]] { + def apply[E, A](f: F[E, A]): G[E, A] +} + +object Free { + @inline def pure[S[_, _], A](a: A): Free[S, Nothing, A] = Pure(a) + @inline def lift[S[_, _], E, A](s: S[E, A]): Free[S, E, A] = Suspend(s) + + final case class Pure[S[_, _], A](a: A) extends Free[S, Nothing, A] { + override def toString: String = s"Pure:[$a]" + } + final case class Suspend[S[_, _], E, A](a: S[E, A]) extends Free[S, E, A] { + override def toString: String = s"Suspend:[$a]" + } + final case class FlatMapped[S[_, _], E, E1 >: E, A, B](sub: Free[S, E, A], cont: A => Free[S, E1, B]) extends Free[S, E1, B] { + override def toString: String = s"FlatMapped:[sub=$sub]" + } +} + +type Monad2[F[+_, +_]] = Monad3[λ[(`-R`, `+E`, `+A`) => F[E, A]]] + +trait Monad3[F[-_, +_, +_]] { + def flatMap[R, E, A, B](r: F[R, E, A])(f: A => F[R, E, B]): F[R, E, B] + def flatten[R, E, A](r: F[R, E, F[R, E, A]]): F[R, E, A] = flatMap(r)(identity) + def pure[A](a: A): F[Any, Nothing, A] +} From 354b5360be90466201f7b401dff2b099f3f2ad84 Mon Sep 17 00:00:00 2001 From: David Hua Date: Tue, 14 Feb 2023 02:03:52 -0500 Subject: [PATCH 146/657] Respect cacheResult flag in safe initialization cache. --- .../tools/dotc/transform/init/Cache.scala | 51 ++++++++++--------- tests/init/neg/apply2.scala | 4 +- 2 files changed, 30 insertions(+), 25 deletions(-) mode change 100644 => 100755 tests/init/neg/apply2.scala diff --git a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala index 14a52d995131..39bf134b0ce3 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala @@ -85,12 +85,14 @@ class Cache[Config, Res]: * * The algorithmic skeleton is as follows: * + * if don't cache result then + * return eval(expr) * if this.current.contains(config, expr) then * return cached value * else * val assumed = this.last(config, expr) or bottom value if absent * this.current(config, expr) = assumed - * val actual = eval(exp) + * val actual = eval(expr) * * if assumed != actual then * this.changed = true @@ -98,28 +100,31 @@ class Cache[Config, Res]: * */ def cachedEval(config: Config, expr: Tree, cacheResult: Boolean, default: Res)(eval: Tree => Res): Res = - this.get(config, expr) match - case Some(value) => value - case None => - val assumeValue: Res = - this.last.get(config, expr) match - case Some(value) => value - case None => - this.last = this.last.updatedNested(config, expr, default) - default - - this.current = this.current.updatedNested(config, expr, assumeValue) - - val actual = eval(expr) - if actual != assumeValue then - // println("Changed! from = " + assumeValue + ", to = " + actual) - this.changed = true - // TODO: respect cacheResult to reduce cache size - this.current = this.current.updatedNested(config, expr, actual) - // this.current = this.current.removed(config, expr) - end if - - actual + if !cacheResult then + eval(expr) + else + this.get(config, expr) match + case Some(value) => value + case None => + val assumeValue: Res = + this.last.get(config, expr) match + case Some(value) => value + case None => + this.last = this.last.updatedNested(config, expr, default) + default + + this.current = this.current.updatedNested(config, expr, assumeValue) + + val actual = eval(expr) + if actual != assumeValue then + // println("Changed! from = " + assumeValue + ", to = " + actual) + this.changed = true + this.current = this.current.updatedNested(config, expr, actual) + // this.current = this.current.removed(config, expr) + end if + + actual + end if end cachedEval def hasChanged = changed diff --git a/tests/init/neg/apply2.scala b/tests/init/neg/apply2.scala old mode 100644 new mode 100755 index 83f64a6dd3c7..c6c7fe5fedd2 --- a/tests/init/neg/apply2.scala +++ b/tests/init/neg/apply2.scala @@ -3,8 +3,8 @@ object O: println(n) class B: - val a = A(this) + val a = A(this) // error val b = new B - val n = 10 // error + val n = 10 end O From 9f28641da43ed10aeeaa9aa6b97f572021ed693c Mon Sep 17 00:00:00 2001 From: David Hua Date: Tue, 14 Feb 2023 02:34:42 -0500 Subject: [PATCH 147/657] Add tests for clause interleaving to safe initialization test suite. --- tests/init/neg/interleaving-params.scala | 9 +++++++++ tests/init/pos/interleaving-overload.scala | 20 ++++++++++++++++++++ tests/init/pos/interleaving-params.scala | 8 ++++++++ 3 files changed, 37 insertions(+) create mode 100755 tests/init/neg/interleaving-params.scala create mode 100755 tests/init/pos/interleaving-overload.scala create mode 100755 tests/init/pos/interleaving-params.scala diff --git a/tests/init/neg/interleaving-params.scala b/tests/init/neg/interleaving-params.scala new file mode 100755 index 000000000000..f0f9cbaf3f53 --- /dev/null +++ b/tests/init/neg/interleaving-params.scala @@ -0,0 +1,9 @@ +import scala.language.experimental.clauseInterleaving + +class Params{ + def bar[T](x: T)[T]: String = ??? // error + def zoo(x: Int)[T, U](x: U): T = ??? // error + def bbb[T <: U](x: U)[U]: U = ??? // error // error + def f0[T](implicit x: T)[U](y: U) = (x,y) // error + def f1[T](implicit x: T)[U] = (x,y) // error +} \ No newline at end of file diff --git a/tests/init/pos/interleaving-overload.scala b/tests/init/pos/interleaving-overload.scala new file mode 100755 index 000000000000..4d9214bf4d09 --- /dev/null +++ b/tests/init/pos/interleaving-overload.scala @@ -0,0 +1,20 @@ +import scala.language.experimental.clauseInterleaving + +class A{ + + def f1[T](x: Any)[U] = ??? + def f1[T](x: Int)[U] = ??? + + f1(1) + f1("hello") + + case class B[U](x: Int) + def b[U](x: Int) = B[U](x) + + def f2[T]: [U] => Int => B[U] = [U] => (x: Int) => b[U](x) + + f2(1) + f2[Any](1) + f2[Any][Any](1) + +} \ No newline at end of file diff --git a/tests/init/pos/interleaving-params.scala b/tests/init/pos/interleaving-params.scala new file mode 100755 index 000000000000..08531a84e696 --- /dev/null +++ b/tests/init/pos/interleaving-params.scala @@ -0,0 +1,8 @@ +import scala.language.experimental.clauseInterleaving + +class Params{ + type U + def foo[T](x: T)[U >: x.type <: T](using U)[L <: List[U]](l: L): L = ??? + def aaa(x: U): U = ??? + def bbb[T <: U](x: U)[U]: U = ??? +} \ No newline at end of file From 994282177170ca1fab4a0fe3197c0efe4729e06a Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 14 Feb 2023 09:24:15 +0100 Subject: [PATCH 148/657] Avoid infinite recursion in infoDependsOnPrefix --- compiler/src/dotty/tools/dotc/core/Types.scala | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 5ef6dbd39335..d4fa6a819dc4 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2508,15 +2508,20 @@ object Types { private def infoDependsOnPrefix(symd: SymDenotation, prefix: Type)(using Context): Boolean = def refines(tp: Type, name: Name): Boolean = tp match - case AndType(tp1, tp2) => - refines(tp1, name) || refines(tp2, name) + case tp: TypeRef => + tp.symbol match + case cls: ClassSymbol => + val otherd = cls.nonPrivateMembersNamed(name) + otherd.exists && !otherd.containsSym(symd.symbol) + case tsym => + refines(tsym.info.hiBound, name) + // avoid going through tp.denot, since that might call infoDependsOnPrefix again case RefinedType(parent, rname, _) => rname == name || refines(parent, name) - case tp: ClassInfo => - val otherd = tp.cls.nonPrivateMembersNamed(name) - otherd.exists && !otherd.containsSym(symd.symbol) case tp: TypeProxy => refines(tp.underlying, name) + case AndType(tp1, tp2) => + refines(tp1, name) || refines(tp2, name) case _ => false From a53b1855f793d59b0e43925697eb769ebbacba4c Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 14 Feb 2023 12:06:56 +0000 Subject: [PATCH 149/657] Disallow mixins where super calls bind to vals --- .../tools/dotc/transform/ResolveSuper.scala | 3 +++ tests/neg/t12715.scala | 17 +++++++++++++++++ tests/neg/t12715b.scala | 16 ++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 tests/neg/t12715.scala create mode 100644 tests/neg/t12715b.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala index dd109ce153eb..99b6be1eea8a 100644 --- a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -119,6 +119,9 @@ object ResolveSuper { report.error(IllegalSuperAccessor(base, memberName, targetName, acc, accTp, other.symbol, otherTp), base.srcPos) bcs = bcs.tail } + if sym.is(Accessor) then + report.error( + em"parent ${acc.owner} has a super call which binds to the value ${sym.showFullName}. Super calls can only target methods.", base) sym.orElse { val originalName = acc.name.asTermName.originalOfSuperAccessorName report.error(em"Member method ${originalName.debugString} of mixin ${acc.owner} is missing a concrete super implementation in $base.", base.srcPos) diff --git a/tests/neg/t12715.scala b/tests/neg/t12715.scala new file mode 100644 index 000000000000..b24d51a6e9fa --- /dev/null +++ b/tests/neg/t12715.scala @@ -0,0 +1,17 @@ +trait A { def f: String } +trait B extends A { def f = "B" } +trait C extends A { override val f = "C" } +trait D extends C { override val f = "D" } +trait E extends A, B { def d = super.f } +final class O1 extends B, C, D, E // error: parent trait E has a super call which binds to the value D.f. Super calls can only target methods. +final class O2 extends B, C, E, D // error: parent trait E has a super call which binds to the value C.f. Super calls can only target methods. +final class O3 extends B, E, C, D + +object Main: + def main(args: Array[String]): Unit = + println(O1().f) // D + println(O2().f) // D + println(O3().f) // D + println(O3().d) // B + O1().d // was: NoSuchMethodError: 'java.lang.String D.f$(D)' + O2().d // was: NoSuchMethodError: 'java.lang.String C.f$(C)' diff --git a/tests/neg/t12715b.scala b/tests/neg/t12715b.scala new file mode 100644 index 000000000000..da024116d4b3 --- /dev/null +++ b/tests/neg/t12715b.scala @@ -0,0 +1,16 @@ +trait B: + def f: Float = 1.0f + +class A(override val f: Float) extends B + +trait C extends B: + abstract override val f = super.f + 100.0f + +trait D extends B: + abstract override val f = super.f + 1000.0f + +class ACD10 extends A(10.0f) with C with D // error: parent trait D has a super call to method B.f, which binds to the value C.f. Super calls can only target methods. + +object Test: + def main(args: Array[String]): Unit = + new ACD10 // was: NoSuchMethodError: 'float C.f$(C)' From fe46a90b703c6f3855000c4bc8c7e62dec77b7bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 14 Feb 2023 13:17:40 +0100 Subject: [PATCH 150/657] Run CI on merge queue --- .github/workflows/cla.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 3ac31b0994f7..bb1aec1290c0 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -6,6 +6,7 @@ on: push: branches: - 'language-reference-stable' + merge_group: permissions: contents: write pull-requests: write From 50243538f7596afbd0bb0e79d5bb82aed2b61f85 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 6 Feb 2023 15:11:44 +0100 Subject: [PATCH 151/657] Fix Splicer.isEscapedVariable Consider that `val macro` expansion in the context can come from an outer macro that is being expanded (i.e. this is a nested macro). Nested macro expansion can occur when a macro summons an implicit macro. Fixes partially #16835 --- .../dotty/tools/dotc/transform/Splicer.scala | 7 +- tests/pos-macros/i16835/Macro_1.scala | 79 +++++++++++++++++++ tests/pos-macros/i16835/Show_1.scala | 11 +++ tests/pos-macros/i16835/Test_2.scala | 30 +++++++ 4 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 tests/pos-macros/i16835/Macro_1.scala create mode 100644 tests/pos-macros/i16835/Show_1.scala create mode 100644 tests/pos-macros/i16835/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 3d3cd1f41c84..bc4119ad0cff 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -86,7 +86,7 @@ object Splicer { } } - /** Checks that no symbol that whas generated within the macro expansion has an out of scope reference */ + /** Checks that no symbol that was generated within the macro expansion has an out of scope reference */ def checkEscapedVariables(tree: Tree, expansionOwner: Symbol)(using Context): tree.type = new TreeTraverser { private[this] var locals = Set.empty[Symbol] @@ -119,7 +119,10 @@ object Splicer { sym.exists && !sym.is(Package) && sym.owner.ownersIterator.exists(x => x == expansionOwner || // symbol was generated within this macro expansion - isMacroOwner(x) // symbol was generated within another macro expansion + { // symbol was generated within another macro expansion + isMacroOwner(x) && + !ctx.owner.ownersIterator.contains(x) + } ) && !locals.contains(sym) // symbol is not in current scope }.traverse(tree) diff --git a/tests/pos-macros/i16835/Macro_1.scala b/tests/pos-macros/i16835/Macro_1.scala new file mode 100644 index 000000000000..133d9f38d1da --- /dev/null +++ b/tests/pos-macros/i16835/Macro_1.scala @@ -0,0 +1,79 @@ +import scala.quoted.* +import scala.deriving.Mirror + +// derivation code is a slightly modified version of: https://github.com/lampepfl/dotty-macro-examples/blob/main/macroTypeClassDerivation/src/macro.scala +object Derivation { + + // Typeclass instance gets constructed as part of a macro + inline given deriveFullyConstrucedByMacro[A](using Mirror.ProductOf[A]): Show[A] = Derivation.deriveShow[A] + + // Typeclass instance is built inside as part of a method, only the 'show' impl is filled in by a macro + inline given derivePartiallyConstructedByMacro[A](using Mirror.ProductOf[A]): Show[A] = + new { + def show(value: A): String = Derivation.show(value) + } + + inline def show[T](value: T): String = ${ showValue('value) } + + inline def deriveShow[T]: Show[T] = ${ deriveCaseClassShow[T] } + + private def deriveCaseClassShow[T](using quotes: Quotes, tpe: Type[T]): Expr[Show[T]] = { + import quotes.reflect.* + // Getting the case fields of the case class + val fields: List[Symbol] = TypeTree.of[T].symbol.caseFields + + '{ + new Show[T] { + override def show(t: T): String = + ${ showValue('t) } + } + } + } + + def showValue[T: Type](value: Expr[T])(using Quotes): Expr[String] = { + import quotes.reflect.* + + val fields: List[Symbol] = TypeTree.of[T].symbol.caseFields + + val vTerm: Term = value.asTerm + val valuesExprs: List[Expr[String]] = fields.map(showField(vTerm, _)) + val exprOfList: Expr[List[String]] = Expr.ofList(valuesExprs) + '{ "{ " + $exprOfList.mkString(", ") + " }" } + } + + /** Create a quoted String representation of a given field of the case class */ + private def showField(using Quotes)(caseClassTerm: quotes.reflect.Term, field: quotes.reflect.Symbol): Expr[String] = { + import quotes.reflect.* + + val fieldValDef: ValDef = field.tree.asInstanceOf[ValDef] + val fieldTpe: TypeRepr = fieldValDef.tpt.tpe + val fieldName: String = fieldValDef.name + + val tcl: Term = lookupShowFor(fieldTpe) // Show[$fieldTpe] + val fieldValue: Term = Select(caseClassTerm, field) // v.field + val strRepr: Expr[String] = applyShow(tcl, fieldValue).asExprOf[String] + '{ ${ Expr(fieldName) } + ": " + $strRepr } // summon[Show[$fieldTpe]].show(v.field) + } + + /** Look up the Show[$t] typeclass for a given type t */ + private def lookupShowFor(using Quotes)(t: quotes.reflect.TypeRepr): quotes.reflect.Term = { + import quotes.reflect.* + t.asType match { + case '[tpe] => + Implicits.search(TypeRepr.of[Show[tpe]]) match { + case res: ImplicitSearchSuccess => res.tree + case failure: DivergingImplicit => report.errorAndAbort(s"Diverving: ${failure.explanation}") + case failure: NoMatchingImplicits => report.errorAndAbort(s"NoMatching: ${failure.explanation}") + case failure: AmbiguousImplicits => report.errorAndAbort(s"Ambiguous: ${failure.explanation}") + case failure: ImplicitSearchFailure => + report.errorAndAbort(s"catch all: ${failure.explanation}") + } + } + } + + /** Composes the tree: $tcl.show($arg) */ + private def applyShow(using Quotes)(tcl: quotes.reflect.Term, arg: quotes.reflect.Term): quotes.reflect.Term = { + import quotes.reflect.* + Apply(Select.unique(tcl, "show"), arg :: Nil) + } +} diff --git a/tests/pos-macros/i16835/Show_1.scala b/tests/pos-macros/i16835/Show_1.scala new file mode 100644 index 000000000000..61f6b2dccd80 --- /dev/null +++ b/tests/pos-macros/i16835/Show_1.scala @@ -0,0 +1,11 @@ +trait Show[A] { + def show(value: A): String +} + +object Show { + given identity: Show[String] = a => a + + given int: Show[Int] = _.toString() + + given list[A](using A: Show[A]): Show[List[A]] = _.map(A.show).toString() +} diff --git a/tests/pos-macros/i16835/Test_2.scala b/tests/pos-macros/i16835/Test_2.scala new file mode 100644 index 000000000000..61019b1417b6 --- /dev/null +++ b/tests/pos-macros/i16835/Test_2.scala @@ -0,0 +1,30 @@ +import scala.deriving.* + +object usage { + final case class Person(name: String, age: Int, otherNames: List[String], p2: Person2) + + final case class Person2(name: String, age: Int, otherNames: List[String]) + + locally { + import Derivation.deriveFullyConstrucedByMacro + // works for case classes without other nested case classes inside + summon[Show[Person2]] + + // also derives instances with nested case classes + summon[Show[Person]] + } + + locally { + import Derivation.derivePartiallyConstructedByMacro + + // works for case classes without other nested case classes inside + summon[Show[Person2]] + + // fails for case classes with other nested case classes inside, + // note how that error is not a `NonMatching', `Diverging` or `Ambiguous` implicit search error but something else + /* + catch all: given instance deriveWithConstructionOutsideMacro in object Derivation does not match type io.github.arainko.ducktape.issue_repros.Show[Person2] + */ + summon[Show[Person]] + } +} \ No newline at end of file From 715bca84aa9787be6bd07502795afb845ce2aa5c Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 14 Feb 2023 13:26:11 +0000 Subject: [PATCH 152/657] Drop failsafe checkNonCyclic and document GADT fullBounds change --- compiler/src/dotty/tools/dotc/core/GadtConstraint.scala | 9 ++++++++- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index 211c8e637b1f..4b580c06d11f 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -77,6 +77,13 @@ class GadtConstraint private ( sym.name.is(NameKinds.UniqueName) && { val hi = sym.info.hiBound !hi.isExactlyAny && self <:< hi + // drop any lower param that is a GADT symbol + // and is upper-bounded by a non-Any super-type of the original parameter + // e.g. in pos/i14287.min + // B$1 had info <: X and fullBounds >: B$2 <: X, and + // B$2 had info <: B$1 and fullBounds <: B$1 + // We can use the info of B$2 to drop the lower-bound of B$1 + // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. } }.foldLeft(nonParamBounds(param).lo) { (t, u) => t | externalize(u) @@ -88,7 +95,7 @@ class GadtConstraint private ( val sym = paramSymbol(p) sym.name.is(NameKinds.UniqueName) && { val lo = sym.info.loBound - !lo.isExactlyNothing && lo <:< self + !lo.isExactlyNothing && lo <:< self // same as fullLowerBounds } }.foldLeft(nonParamBounds(param).hi) { (t, u) => val eu = externalize(u) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 328039fd8a41..a87d6dd7e703 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1765,7 +1765,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else report.error(new DuplicateBind(b, cdef), b.srcPos) if (!ctx.isAfterTyper) { val bounds = ctx.gadt.fullBounds(sym) - if (bounds != null) sym.info = checkNonCyclic(sym, bounds, reportErrors = true) + if (bounds != null) sym.info = bounds } b case t: UnApply if t.symbol.is(Inline) => Inlines.inlinedUnapply(t) From 57bfabcff26cdea598391684ecbeeafb0700e125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 14 Feb 2023 16:21:51 +0100 Subject: [PATCH 153/657] Do not run CI on pushes to github queues --- .github/workflows/ci.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b70cc3bb9e70..fd2fd85cdce3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -14,6 +14,10 @@ on: ## - both (tags or tags-ignore) and (branches or branches-ignore), ## - or neither of them. ## But it's important to not have only one or the other. + tags: + - '*' + branches-ignore: + - 'gh-readonly-queue/*' pull_request: merge_group: schedule: From 677991ef8bceb0b7e9a110694f3c6931a34ddbff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 15 Feb 2023 10:27:41 +0100 Subject: [PATCH 154/657] Fix wildcard used to filter queue --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fd2fd85cdce3..370b66854051 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -17,7 +17,7 @@ on: tags: - '*' branches-ignore: - - 'gh-readonly-queue/*' + - 'gh-readonly-queue/**' pull_request: merge_group: schedule: From 96bfb69c933148ad0561e589a444fab1308d7951 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 8 Feb 2023 11:58:53 +0100 Subject: [PATCH 155/657] Handle pickled forward references in pickled expressions To compute forward references it was assumed that the owner of that symbol can be found in the TASTy. This is not always the case with TASTy expressions of pickled quotes. The owner might be outside the quote, in this case the context already has the owner of the referenced symbol. These are local symbols defined at the top-level of the TASTy. Fixes #16843 --- .../tools/dotc/core/tasty/TreeUnpickler.scala | 6 +++++- tests/pos-macros/i16843a/Macro_1.scala | 13 +++++++++++++ tests/pos-macros/i16843a/Test_2.scala | 1 + tests/pos-macros/i16843b/Macro_1.scala | 18 ++++++++++++++++++ tests/pos-macros/i16843b/Test_2.scala | 1 + 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/pos-macros/i16843a/Macro_1.scala create mode 100644 tests/pos-macros/i16843a/Test_2.scala create mode 100644 tests/pos-macros/i16843b/Macro_1.scala create mode 100644 tests/pos-macros/i16843b/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 91290b4ddd41..dfe04dbe6d2b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -74,6 +74,9 @@ class TreeUnpickler(reader: TastyReader, */ private val typeAtAddr = new mutable.HashMap[Addr, Type] + /** If this is a pickled quote, the owner of the quote, otherwise NoSymbol. */ + private var rootOwner: Symbol = NoSymbol + /** The root symbol denotation which are defined by the Tasty file associated with this * TreeUnpickler. Set by `enterTopLevel`. */ @@ -106,6 +109,7 @@ class TreeUnpickler(reader: TastyReader, /** The unpickled trees */ def unpickle(mode: UnpickleMode)(using Context): List[Tree] = { + if mode != UnpickleMode.TopLevel then rootOwner = ctx.owner assert(roots != null, "unpickle without previous enterTopLevel") val rdr = new TreeReader(reader) mode match { @@ -1635,7 +1639,7 @@ class TreeUnpickler(reader: TastyReader, pickling.println(i"no owner for $addr among $cs%, %") throw ex } - try search(children, NoSymbol) + try search(children, rootOwner) catch { case ex: TreeWithoutOwner => pickling.println(s"ownerTree = $ownerTree") diff --git a/tests/pos-macros/i16843a/Macro_1.scala b/tests/pos-macros/i16843a/Macro_1.scala new file mode 100644 index 000000000000..98c1505910e6 --- /dev/null +++ b/tests/pos-macros/i16843a/Macro_1.scala @@ -0,0 +1,13 @@ +import scala.quoted.* + +case class Foo(x: Int) + +inline def foo = ${ fooImpl } + +def fooImpl(using Quotes) = + val tmp = '{ + 1 match + case x @ (y: Int) => 0 + } + + '{} diff --git a/tests/pos-macros/i16843a/Test_2.scala b/tests/pos-macros/i16843a/Test_2.scala new file mode 100644 index 000000000000..25406428d0cf --- /dev/null +++ b/tests/pos-macros/i16843a/Test_2.scala @@ -0,0 +1 @@ +val x = foo diff --git a/tests/pos-macros/i16843b/Macro_1.scala b/tests/pos-macros/i16843b/Macro_1.scala new file mode 100644 index 000000000000..631bde56f1f1 --- /dev/null +++ b/tests/pos-macros/i16843b/Macro_1.scala @@ -0,0 +1,18 @@ +import scala.quoted.* + +inline def foo: Int = ${ fooImpl } + +def fooImpl(using Quotes): Expr[Int] = + '{ + val b = ${ + val a = '{ + (1: Int) match + case x @ (y: Int) => 0 + } + a + } + + (1: Int) match + case x @ (y: Int) => 0 + } + diff --git a/tests/pos-macros/i16843b/Test_2.scala b/tests/pos-macros/i16843b/Test_2.scala new file mode 100644 index 000000000000..54c769c9618f --- /dev/null +++ b/tests/pos-macros/i16843b/Test_2.scala @@ -0,0 +1 @@ +def test = foo From 0a7f47e81502e0f9bd37492da9061b0c4dc186e4 Mon Sep 17 00:00:00 2001 From: Paul Coral Date: Wed, 15 Feb 2023 16:44:55 +0100 Subject: [PATCH 156/657] Fix #16822 - Ignore synthetic local private - Update test suit --- .../tools/dotc/transform/CheckUnused.scala | 19 +++++++-------- .../fatal-warnings/i15503i.scala | 23 ++++++++++++++----- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 59878757c39b..6c47c12ac07c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -81,7 +81,7 @@ class CheckUnused extends MiniPhase: ctx override def prepareForIdent(tree: tpd.Ident)(using Context): Context = - if tree.symbol.exists then + if tree.symbol.exists then _key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then _key.unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) @@ -103,7 +103,7 @@ class CheckUnused extends MiniPhase: override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = _key.unusedDataApply{ud => // do not register the ValDef generated for `object` - if !tree.symbol.is(Module) then + if !tree.symbol.is(Module) then ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } @@ -335,7 +335,7 @@ object CheckUnused: * The optional name will be used to target the right import * as the same element can be imported with different renaming */ - def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit = + def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit = if !isConstructorOfSynth(sym) && !doNotRegister(sym) then if sym.isConstructor && sym.exists then registerUsed(sym.owner, None) // constructor are "implicitly" imported with the class @@ -371,7 +371,7 @@ object CheckUnused: implicitParamInScope += memDef else explicitParamInScope += memDef - else if currScopeType.top == ScopeType.Local then + else if currScopeType.top == ScopeType.Local then localDefInScope += memDef else if memDef.shouldReportPrivateDef then privateDefInScope += memDef @@ -578,10 +578,10 @@ object CheckUnused: else false - private def usedDefContains(using Context): Boolean = + private def usedDefContains(using Context): Boolean = sym.everySymbol.exists(usedDef.apply) - private def everySymbol(using Context): List[Symbol] = + private def everySymbol(using Context): List[Symbol] = List(sym, sym.companionClass, sym.companionModule, sym.moduleClass).filter(_.exists) end extension @@ -614,10 +614,11 @@ object CheckUnused: private def isValidParam(using Context): Boolean = val sym = memDef.symbol (sym.is(Param) || sym.isAllOf(PrivateParamAccessor | Local, butNot = CaseAccessor)) && - !isSyntheticMainParam(sym) && - !sym.shouldNotReportParamOwner + !isSyntheticMainParam(sym) && + !sym.shouldNotReportParamOwner && + (!sym.exists || !sym.owner.isAllOf(Synthetic | PrivateLocal)) - private def shouldReportPrivateDef(using Context): Boolean = + private def shouldReportPrivateDef(using Context): Boolean = currScopeType.top == ScopeType.Template && !memDef.symbol.isConstructor && memDef.symbol.is(Private, butNot = SelfName | Synthetic | CaseAccessor) extension (imp: tpd.Import) diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 33e04f34daa8..7eae207d952d 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -78,13 +78,13 @@ package foo.test.companionprivate: package foo.test.i16678: def foo(func: Int => String, value: Int): String = func(value) // OK - def run = + def run = println(foo(number => number.toString, value = 5)) // OK println(foo(number => "", value = 5)) // error println(foo(func = number => "", value = 5)) // error println(foo(func = number => number.toString, value = 5)) // OK println(foo(func = _.toString, value = 5)) // OK - + package foo.test.possibleclasses: case class AllCaseClass( k: Int, // OK @@ -93,7 +93,7 @@ package foo.test.possibleclasses: s: Int, // error /* But not these */ val t: Int, // OK private val z: Int // error - ) + ) case class AllCaseUsed( k: Int, // OK @@ -113,7 +113,7 @@ package foo.test.possibleclasses: s: Int, // error val t: Int, // OK private val z: Int // error - ) + ) class AllUsed( k: Int, // OK @@ -124,10 +124,21 @@ package foo.test.possibleclasses: private val z: Int // OK ) { def a = k + y + s + t + z - } + } package foo.test.from.i16675: case class PositiveNumber private (i: Int) // OK object PositiveNumber: - def make(i: Int): Option[PositiveNumber] = //OK + def make(i: Int): Option[PositiveNumber] = //OK Option.when(i >= 0)(PositiveNumber(i)) // OK + +package foo.test.i16822: + enum ExampleEnum { + case Build(context: String) // OK + case List // OK + } + + def demo = { + val x = ExampleEnum.List // OK + println(x) // OK + } From 9571b42a2ff63e897cb566c09259f5f8d1e7a021 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 7 Feb 2023 13:27:09 +0100 Subject: [PATCH 157/657] Add reflect `defn.FunctionClass` overloads The old `FunctionClass` will need to be deprecated as we will remove `ErasedFunctionN` and `ErasedContextFunctionN`. We will replace this API with a simpler version that can return `FunctionN` or `ContextFunctionN`, the only two stable function classes we have in the compiler/TASTy. Other new function classes will be encoded with the more general refined function type encoding, generalization of the `PolyFunction` encoding. This implies that we won't need to add other kind of function classes to the reflect API. Part of the fix for #16847 --- .../quoted/runtime/impl/QuotesImpl.scala | 5 ++++ library/src/scala/quoted/Quotes.scala | 19 ++++++++++++++ .../stdlibExperimentalDefinitions.scala | 4 ++- tests/run-macros/tasty-definitions-1.check | 26 +++++++++++++++++++ .../tasty-definitions-1/quoted_1.scala | 1 + 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 4d08e0582d1d..2daaf749359f 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2768,7 +2768,12 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def SomeModule: Symbol = dotc.core.Symbols.defn.SomeClass.companionModule def ProductClass: Symbol = dotc.core.Symbols.defn.ProductClass def FunctionClass(arity: Int, isImplicit: Boolean = false, isErased: Boolean = false): Symbol = + if arity < 0 then throw IllegalArgumentException(s"arity: $arity") dotc.core.Symbols.defn.FunctionSymbol(arity, isImplicit, isErased) + def FunctionClass(arity: Int): Symbol = + FunctionClass(arity, false, false) + def FunctionClass(arity: Int, isContextual: Boolean): Symbol = + FunctionClass(arity, isContextual, false) def TupleClass(arity: Int): Symbol = dotc.core.Symbols.defn.TupleType(arity).nn.classSymbol.asClass def isTupleClass(sym: Symbol): Boolean = diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 22ec107aeae8..321725d4e84c 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -4254,8 +4254,27 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * - ... * - Nth element is `FunctionN` */ + // TODO: deprecate in 3.4 and stabilize FunctionClass(Int)/FunctionClass(Int,Boolean) + // @deprecated("Use overload of `FunctionClass` with 1 or 2 arguments","3.4") def FunctionClass(arity: Int, isImplicit: Boolean = false, isErased: Boolean = false): Symbol + /** Class symbol of a function class `scala.FunctionN`. + * + * @param arity the arity of the function where `0 <= arity` + * @return class symbol of `scala.FunctionN` where `N == arity` + */ + @experimental + def FunctionClass(arity: Int): Symbol + + /** Class symbol of a context function class `scala.FunctionN` or `scala.ContextFunctionN`. + * + * @param arity the arity of the function where `0 <= arity` + * @param isContextual if it is a `scala.ContextFunctionN` + * @return class symbol of `scala.FunctionN` or `scala.ContextFunctionN` where `N == arity` + */ + @experimental + def FunctionClass(arity: Int, isContextual: Boolean): Symbol + /** Function-like object that maps arity to symbols for classes `scala.TupleX`. * - 0th element is `NoSymbol` * - 1st element is `NoSymbol` diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index 30e7c5af6c2a..eff76720a7e2 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -66,7 +66,9 @@ val experimentalDefinitionInLibrary = Set( "scala.annotation.MacroAnnotation", //// New APIs: Quotes - // Can be stabilized in 3.3.0 (unsure) or later + // Should be stabilized in 3.4.0 + "scala.quoted.Quotes.reflectModule.defnModule.FunctionClass", + // Can be stabilized in 3.4.0 (unsure) or later "scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings", "scala.quoted.Quotes.reflectModule.FlagsModule.JavaAnnotation", // Cant be stabilized yet. diff --git a/tests/run-macros/tasty-definitions-1.check b/tests/run-macros/tasty-definitions-1.check index 7ee9da3e64e8..c2674c1b7a06 100644 --- a/tests/run-macros/tasty-definitions-1.check +++ b/tests/run-macros/tasty-definitions-1.check @@ -57,31 +57,57 @@ Function23 Function24 Function25 ContextFunction0 +ContextFunction0 +ContextFunction1 ContextFunction1 ContextFunction2 +ContextFunction2 +ContextFunction3 ContextFunction3 ContextFunction4 +ContextFunction4 +ContextFunction5 ContextFunction5 ContextFunction6 +ContextFunction6 ContextFunction7 +ContextFunction7 +ContextFunction8 ContextFunction8 ContextFunction9 +ContextFunction9 ContextFunction10 +ContextFunction10 +ContextFunction11 ContextFunction11 ContextFunction12 +ContextFunction12 ContextFunction13 +ContextFunction13 +ContextFunction14 ContextFunction14 ContextFunction15 +ContextFunction15 +ContextFunction16 ContextFunction16 ContextFunction17 +ContextFunction17 +ContextFunction18 ContextFunction18 ContextFunction19 +ContextFunction19 ContextFunction20 +ContextFunction20 +ContextFunction21 ContextFunction21 ContextFunction22 +ContextFunction22 ContextFunction23 +ContextFunction23 +ContextFunction24 ContextFunction24 ContextFunction25 +ContextFunction25 ErasedFunction1 ErasedFunction2 ErasedFunction3 diff --git a/tests/run-macros/tasty-definitions-1/quoted_1.scala b/tests/run-macros/tasty-definitions-1/quoted_1.scala index 6ee80daeeb1d..baf0b929f202 100644 --- a/tests/run-macros/tasty-definitions-1/quoted_1.scala +++ b/tests/run-macros/tasty-definitions-1/quoted_1.scala @@ -60,6 +60,7 @@ object Macros { printout(defn.FunctionClass(i).name) for (i <- 0 to 25) + printout(defn.FunctionClass(i, isContextual = true).name) printout(defn.FunctionClass(i, isImplicit = true).name) for (i <- 1 to 25) From 22be8026245da199ea37d7721e861036de4834bd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 15 Feb 2023 21:39:51 +0000 Subject: [PATCH 158/657] GADT: move dropping GADT symbols into foldLeft --- .../tools/dotc/core/GadtConstraint.scala | 51 ++++++++----------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index 4b580c06d11f..c98445c61a7e 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -3,6 +3,7 @@ package dotc package core import Contexts.*, Decorators.*, Symbols.*, Types.* +import NameKinds.UniqueName import config.Printers.{gadts, gadtsConstr} import util.{SimpleIdentitySet, SimpleIdentityMap} import printing._ @@ -72,36 +73,30 @@ class GadtConstraint private ( def fullLowerBound(param: TypeParamRef)(using Context): Type = val self = externalize(param) - constraint.minLower(param).filterNot { p => - val sym = paramSymbol(p) - sym.name.is(NameKinds.UniqueName) && { - val hi = sym.info.hiBound - !hi.isExactlyAny && self <:< hi - // drop any lower param that is a GADT symbol - // and is upper-bounded by a non-Any super-type of the original parameter - // e.g. in pos/i14287.min - // B$1 had info <: X and fullBounds >: B$2 <: X, and - // B$2 had info <: B$1 and fullBounds <: B$1 - // We can use the info of B$2 to drop the lower-bound of B$1 - // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. - } - }.foldLeft(nonParamBounds(param).lo) { - (t, u) => t | externalize(u) + constraint.minLower(param).foldLeft(nonParamBounds(param).lo) { (acc, p) => + externalize(p) match + case tp: TypeRef + // drop any lower param that is a GADT symbol + // and is upper-bounded by a non-Any super-type of the original parameter + // e.g. in pos/i14287.min + // B$1 had info <: X and fullBounds >: B$2 <: X, and + // B$2 had info <: B$1 and fullBounds <: B$1 + // We can use the info of B$2 to drop the lower-bound of B$1 + // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. + if tp.name.is(UniqueName) && !tp.info.hiBound.isExactlyAny && self <:< tp.info.hiBound => acc + case tp => acc | tp } def fullUpperBound(param: TypeParamRef)(using Context): Type = val self = externalize(param) - constraint.minUpper(param).filterNot { p => - val sym = paramSymbol(p) - sym.name.is(NameKinds.UniqueName) && { - val lo = sym.info.loBound - !lo.isExactlyNothing && lo <:< self // same as fullLowerBounds - } - }.foldLeft(nonParamBounds(param).hi) { (t, u) => - val eu = externalize(u) - // Any as the upper bound means "no bound", but if F is higher-kinded, - // Any & F = F[_]; this is wrong for us so we need to short-circuit - if t.isAny then eu else t & eu + constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (acc, u) => + externalize(u) match + case tp: TypeRef // same as fullLowerBounds + if tp.name.is(UniqueName) && !tp.info.loBound.isExactlyNothing && tp.info.loBound <:< self => acc + case tp => + // Any as the upper bound means "no bound", but if F is higher-kinded, + // Any & F = F[_]; this is wrong for us so we need to short-circuit + if acc.isAny then tp else acc & tp } def externalize(tp: Type, theMap: TypeMap | Null = null)(using Context): Type = tp match @@ -117,10 +112,6 @@ class GadtConstraint private ( def tvarOrError(sym: Symbol)(using Context): TypeVar = mapping(sym).ensuring(_ != null, i"not a constrainable symbol: $sym").uncheckedNN - private def paramSymbol(p: TypeParamRef): Symbol = reverseMapping(p) match - case sym: Symbol => sym - case null => NoSymbol - @tailrec final def stripInternalTypeVar(tp: Type): Type = tp match case tv: TypeVar => val inst = constraint.instType(tv) From 86d89379e5185dab1ff6978f8caf67f24955a259 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 15 Feb 2023 22:25:14 +0000 Subject: [PATCH 159/657] GADT: Use isPatternBound, ofc... --- compiler/src/dotty/tools/dotc/core/GadtConstraint.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index c98445c61a7e..74d668e7ca87 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -83,7 +83,7 @@ class GadtConstraint private ( // B$2 had info <: B$1 and fullBounds <: B$1 // We can use the info of B$2 to drop the lower-bound of B$1 // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. - if tp.name.is(UniqueName) && !tp.info.hiBound.isExactlyAny && self <:< tp.info.hiBound => acc + if tp.symbol.isPatternBound && !tp.info.hiBound.isExactlyAny && self <:< tp.info.hiBound => acc case tp => acc | tp } @@ -92,7 +92,7 @@ class GadtConstraint private ( constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (acc, u) => externalize(u) match case tp: TypeRef // same as fullLowerBounds - if tp.name.is(UniqueName) && !tp.info.loBound.isExactlyNothing && tp.info.loBound <:< self => acc + if tp.symbol.isPatternBound && !tp.info.loBound.isExactlyNothing && tp.info.loBound <:< self => acc case tp => // Any as the upper bound means "no bound", but if F is higher-kinded, // Any & F = F[_]; this is wrong for us so we need to short-circuit From 37aeb3d667a9e77fcfbb78c30112419acc01df9c Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 15 Feb 2023 22:30:09 +0000 Subject: [PATCH 160/657] GADT: Use =:= instead of Any/Nothing --- .../tools/dotc/core/GadtConstraint.scala | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala index 74d668e7ca87..bb65cce84042 100644 --- a/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/GadtConstraint.scala @@ -75,15 +75,14 @@ class GadtConstraint private ( val self = externalize(param) constraint.minLower(param).foldLeft(nonParamBounds(param).lo) { (acc, p) => externalize(p) match - case tp: TypeRef - // drop any lower param that is a GADT symbol - // and is upper-bounded by a non-Any super-type of the original parameter - // e.g. in pos/i14287.min - // B$1 had info <: X and fullBounds >: B$2 <: X, and - // B$2 had info <: B$1 and fullBounds <: B$1 - // We can use the info of B$2 to drop the lower-bound of B$1 - // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. - if tp.symbol.isPatternBound && !tp.info.hiBound.isExactlyAny && self <:< tp.info.hiBound => acc + // drop any lower param that is a GADT symbol + // and is upper-bounded by a non-Any super-type of the original parameter + // e.g. in pos/i14287.min + // B$1 had info <: X and fullBounds >: B$2 <: X, and + // B$2 had info <: B$1 and fullBounds <: B$1 + // We can use the info of B$2 to drop the lower-bound of B$1 + // and return non-bidirectional bounds B$1 <: X and B$2 <: B$1. + case tp: TypeRef if tp.symbol.isPatternBound && self =:= tp.info.hiBound => acc case tp => acc | tp } @@ -91,8 +90,7 @@ class GadtConstraint private ( val self = externalize(param) constraint.minUpper(param).foldLeft(nonParamBounds(param).hi) { (acc, u) => externalize(u) match - case tp: TypeRef // same as fullLowerBounds - if tp.symbol.isPatternBound && !tp.info.loBound.isExactlyNothing && tp.info.loBound <:< self => acc + case tp: TypeRef if tp.symbol.isPatternBound && self =:= tp.info.loBound => acc // like fullLowerBound case tp => // Any as the upper bound means "no bound", but if F is higher-kinded, // Any & F = F[_]; this is wrong for us so we need to short-circuit From a0ec2dc1d5d6299409b4273125ead1f9ab57cdf7 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 15 Feb 2023 13:37:28 +0000 Subject: [PATCH 161/657] Space: Move isSubspace cache to Space instance This is in preparation for making SpaceEngine an object, so also remove the other vals too. --- .../dotty/tools/dotc/core/Definitions.scala | 3 ++ .../tools/dotc/transform/patmat/Space.scala | 53 ++++++++++--------- .../transform/patmat/SpaceEngineTest.scala | 2 +- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 854608143df9..d6e677547583 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -530,9 +530,12 @@ class Definitions { }) @tu lazy val ListClass: Symbol = requiredClass("scala.collection.immutable.List") + def ListType: TypeRef = ListClass.typeRef @tu lazy val ListModule: Symbol = requiredModule("scala.collection.immutable.List") @tu lazy val NilModule: Symbol = requiredModule("scala.collection.immutable.Nil") + def NilType: TermRef = NilModule.termRef @tu lazy val ConsClass: Symbol = requiredClass("scala.collection.immutable.::") + def ConsType: TypeRef = ConsClass.typeRef @tu lazy val SeqFactoryClass: Symbol = requiredClass("scala.collection.SeqFactory") @tu lazy val SingletonClass: ClassSymbol = diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index f4c4863d073d..7071e3b46829 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -55,7 +55,16 @@ import collection.mutable /** space definition */ -sealed trait Space +sealed trait Space: + private val isSubspaceCache = mutable.HashMap.empty[Space, Boolean] + + def isSubspace(b: Space)(engine: SpaceEngine)(using Context): Boolean = + if this == Empty then true + else if b == Empty then false + else trace(s"isSubspace(${engine.show(this)}, ${engine.show(b)})", debug) { + isSubspaceCache.getOrElseUpdate(b, engine.computeIsSubspace(this, b)) + } +end Space /** Empty space */ case object Empty extends Space @@ -349,13 +358,6 @@ object SpaceEngine { class SpaceEngine(using Context) extends SpaceLogic { import tpd._ - private val scalaSeqFactoryClass = defn.SeqFactoryClass - private val scalaListType = defn.ListClass.typeRef - private val scalaNilType = defn.NilModule.termRef - private val scalaConsType = defn.ConsClass.typeRef - - private val constantNullType = ConstantType(Constant(null)) - override def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type): Space = trace(s"atomic intersection: ${AndType(tp1, tp2).show}", debug) { // Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1). if !ctx.mode.is(Mode.SafeNulls) && (tp1.isNullType || tp2.isNullType) then @@ -398,7 +400,7 @@ class SpaceEngine(using Context) extends SpaceLogic { val funRef = fun1.tpe.asInstanceOf[TermRef] if (fun.symbol.name == nme.unapplySeq) val (arity, elemTp, resultTp) = unapplySeqInfo(fun.tpe.widen.finalResultType, fun.srcPos) - if (fun.symbol.owner == scalaSeqFactoryClass && scalaListType.appliedTo(elemTp) <:< pat.tpe) + if (fun.symbol.owner == defn.SeqFactoryClass && defn.ListType.appliedTo(elemTp) <:< pat.tpe) // The exhaustivity and reachability logic already handles decomposing sum types (into its subclasses) // and product types (into its components). To get better counter-examples for patterns that are of type // List (or a super-type of list, like LinearSeq) we project them into spaces that use `::` and Nil. @@ -519,16 +521,16 @@ class SpaceEngine(using Context) extends SpaceLogic { /** Space of the pattern: unapplySeq(a, b, c: _*) */ def projectSeq(pats: List[Tree]): Space = { - if (pats.isEmpty) return Typ(scalaNilType, false) + if (pats.isEmpty) return Typ(defn.NilType, false) val (items, zero) = if (isWildcardStarArg(pats.last)) - (pats.init, Typ(scalaListType.appliedTo(pats.last.tpe.elemType), false)) + (pats.init, Typ(defn.ListType.appliedTo(pats.last.tpe.elemType), false)) else - (pats, Typ(scalaNilType, false)) + (pats, Typ(defn.NilType, false)) - val unapplyTp = scalaConsType.classSymbol.companionModule.termRef.select(nme.unapply) + val unapplyTp = defn.ConsType.classSymbol.companionModule.termRef.select(nme.unapply) items.foldRight[Space](zero) { (pat, acc) => - val consTp = scalaConsType.appliedTo(pats.head.tpe.widen) + val consTp = defn.ConsType.appliedTo(pats.head.tpe.widen) Prod(consTp, unapplyTp, project(pat) :: acc :: Nil) } } @@ -538,13 +540,14 @@ class SpaceEngine(using Context) extends SpaceLogic { private val isSubspaceCache = mutable.HashMap.empty[(Space, Space, Context), Boolean] - override def isSubspace(a: Space, b: Space)(using Context): Boolean = - isSubspaceCache.getOrElseUpdate((a, b, ctx), super.isSubspace(a, b)) + override def isSubspace(a: Space, b: Space)(using Context): Boolean = a.isSubspace(b)(this) + + def computeIsSubspace(a: Space, b: Space)(using Context): Boolean = super.isSubspace(a, b) /** Is `tp1` a subtype of `tp2`? */ def isSubType(tp1: Type, tp2: Type): Boolean = trace(i"$tp1 <:< $tp2", debug, show = true) { - if tp1 == constantNullType && !ctx.mode.is(Mode.SafeNulls) - then tp2 == constantNullType + if tp1 == ConstantType(Constant(null)) && !ctx.mode.is(Mode.SafeNulls) + then tp2 == ConstantType(Constant(null)) else tp1 <:< tp2 } @@ -593,10 +596,10 @@ class SpaceEngine(using Context) extends SpaceLogic { if (isUnapplySeq) { val (arity, elemTp, resultTp) = unapplySeqInfo(resTp, unappSym.srcPos) - if (elemTp.exists) scalaListType.appliedTo(elemTp) :: Nil + if (elemTp.exists) defn.ListType.appliedTo(elemTp) :: Nil else { val sels = productSeqSelectors(resultTp, arity, unappSym.srcPos) - sels.init :+ scalaListType.appliedTo(sels.last) + sels.init :+ defn.ListType.appliedTo(sels.last) } } else { @@ -821,7 +824,7 @@ class SpaceEngine(using Context) extends SpaceLogic { case Empty => "empty" case Typ(c: ConstantType, _) => "" + c.value.value case Typ(tp: TermRef, _) => - if (flattenList && tp <:< scalaNilType) "" + if (flattenList && tp <:< defn.NilType) "" else tp.symbol.showName case Typ(tp, decomposed) => @@ -829,9 +832,9 @@ class SpaceEngine(using Context) extends SpaceLogic { if (ctx.definitions.isTupleNType(tp)) params(tp).map(_ => "_").mkString("(", ", ", ")") - else if (scalaListType.isRef(sym)) + else if (defn.ListType.isRef(sym)) if (flattenList) "_*" else "_: List" - else if (scalaConsType.isRef(sym)) + else if (defn.ConsType.isRef(sym)) if (flattenList) "_, _*" else "List(_, _*)" else if (tp.classSymbol.is(Sealed) && tp.classSymbol.hasAnonymousChild) "_: " + showType(tp) + " (anonymous)" @@ -843,7 +846,7 @@ class SpaceEngine(using Context) extends SpaceLogic { case Prod(tp, fun, params) => if (ctx.definitions.isTupleNType(tp)) "(" + params.map(doShow(_)).mkString(", ") + ")" - else if (tp.isRef(scalaConsType.symbol)) + else if (tp.isRef(defn.ConsType.symbol)) if (flattenList) params.map(doShow(_, flattenList)).filter(_.nonEmpty).mkString(", ") else params.map(doShow(_, flattenList = true)).filter(!_.isEmpty).mkString("List(", ", ", ")") else { @@ -961,7 +964,7 @@ class SpaceEngine(using Context) extends SpaceLogic { val isNullable = selTyp.classSymbol.isNullableClass val targetSpace = if isNullable - then project(OrType(selTyp, constantNullType, soft = false)) + then project(OrType(selTyp, ConstantType(Constant(null)), soft = false)) else project(selTyp) debug.println(s"targetSpace: ${show(targetSpace)}") diff --git a/compiler/test/dotty/tools/dotc/transform/patmat/SpaceEngineTest.scala b/compiler/test/dotty/tools/dotc/transform/patmat/SpaceEngineTest.scala index 699b36caa508..13dfb7af668a 100644 --- a/compiler/test/dotty/tools/dotc/transform/patmat/SpaceEngineTest.scala +++ b/compiler/test/dotty/tools/dotc/transform/patmat/SpaceEngineTest.scala @@ -20,7 +20,7 @@ class SpaceEngineTest: val engine = patmat.SpaceEngine() import engine.* - val tp = defn.ConsClass.typeRef.appliedTo(defn.AnyType) + val tp = defn.ConsType.appliedTo(defn.AnyType) val unappTp = requiredMethod("scala.collection.immutable.::.unapply").termRef val params = List(Empty, Typ(tp)) From 5a21922cb75d9dafde9a5f1cb8b1a8ab97f33e44 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 9 Jan 2023 17:29:16 +0000 Subject: [PATCH 162/657] Space: Remove SpaceLogic and make SpaceEngine stateless --- .../tools/dotc/transform/PatternMatcher.scala | 13 +- .../tools/dotc/transform/patmat/Space.scala | 136 +++++++----------- .../transform/patmat/SpaceEngineTest.scala | 4 +- 3 files changed, 60 insertions(+), 93 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 63ffdffbddef..82828078e586 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -2,11 +2,10 @@ package dotty.tools package dotc package transform -import scala.annotation.tailrec import core._ import MegaPhase._ -import collection.mutable import Symbols._, Contexts._, Types._, StdNames._, NameOps._ +import patmat.SpaceEngine import util.Spans._ import typer.Applications.* import SymUtils._ @@ -16,9 +15,12 @@ import Decorators._ import NameKinds.{PatMatStdBinderName, PatMatAltsName, PatMatResultName} import config.Printers.patmatch import reporting._ -import dotty.tools.dotc.ast._ +import ast._ import util.Property._ +import scala.annotation.tailrec +import scala.collection.mutable + /** The pattern matching transform. * After this phase, the only Match nodes remaining in the code are simple switches * where every pattern is an integer or string constant @@ -45,9 +47,8 @@ class PatternMatcher extends MiniPhase { val translated = new Translator(matchType, this).translateMatch(tree) // check exhaustivity and unreachability - val engine = new patmat.SpaceEngine - engine.checkExhaustivity(tree) - engine.checkRedundancy(tree) + SpaceEngine.checkExhaustivity(tree) + SpaceEngine.checkRedundancy(tree) translated.ensureConforms(matchType) } diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 7071e3b46829..b6da5f2dba9c 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -22,9 +22,10 @@ import transform.SymUtils._ import reporting._ import config.Printers.{exhaustivity => debug} import util.{SrcPos, NoSourcePosition} -import collection.mutable -/** Space logic for checking exhaustivity and unreachability of pattern matching +import scala.collection.mutable + +/* Space logic for checking exhaustivity and unreachability of pattern matching * * Space can be thought of as a set of possible values. A type or a pattern * both refer to spaces. The space of a type is the values that inhabit the @@ -53,16 +54,17 @@ import collection.mutable * */ - /** space definition */ sealed trait Space: + import SpaceEngine.* + private val isSubspaceCache = mutable.HashMap.empty[Space, Boolean] - def isSubspace(b: Space)(engine: SpaceEngine)(using Context): Boolean = + def isSubspace(b: Space)(using Context): Boolean = if this == Empty then true else if b == Empty then false - else trace(s"isSubspace(${engine.show(this)}, ${engine.show(b)})", debug) { - isSubspaceCache.getOrElseUpdate(b, engine.computeIsSubspace(this, b)) + else trace(s"isSubspace(${show(this)}, ${show(b)})", debug) { + isSubspaceCache.getOrElseUpdate(b, computeIsSubspace(this, b)) } end Space @@ -83,44 +85,8 @@ case class Prod(tp: Type, unappTp: TermRef, params: List[Space]) extends Space /** Union of spaces */ case class Or(spaces: Seq[Space]) extends Space -/** abstract space logic */ -trait SpaceLogic { - /** Is `tp1` a subtype of `tp2`? */ - def isSubType(tp1: Type, tp2: Type): Boolean - - /** True if we can assume that the two unapply methods are the same. - * That is, given the same parameter, they return the same result. - * - * We assume that unapply methods are pure, but the same method may - * be called with different prefixes, thus behaving differently. - */ - def isSameUnapply(tp1: TermRef, tp2: TermRef): Boolean - - /** Return a space containing the values of both types. - * - * The types should be atomic (non-decomposable) and unrelated (neither - * should be a subtype of the other). - */ - def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type): Space - - /** Is the type `tp` decomposable? i.e. all values of the type can be covered - * by its decomposed types. - * - * Abstract sealed class, OrType, Boolean and Java enums can be decomposed. - */ - def canDecompose(tp: Type): Boolean - - /** Return term parameter types of the extractor `unapp` */ - def signature(unapp: TermRef, scrutineeTp: Type, argLen: Int): List[Type] - - /** Get components of decomposable types */ - def decompose(tp: Type): List[Typ] - - /** Whether the extractor covers the given type */ - def covers(unapp: TermRef, scrutineeTp: Type, argLen: Int): Boolean - - /** Display space in string format */ - def show(sp: Space): String +object SpaceEngine { + import tpd._ /** Simplify space such that a space equal to `Empty` becomes `Empty` */ def simplify(space: Space)(using Context): Space = trace(s"simplify ${show(space)} --> ", debug, show)(space match { @@ -174,7 +140,9 @@ trait SpaceLogic { } /** Is `a` a subspace of `b`? Equivalent to `simplify(simplify(a) - simplify(b)) == Empty`, but faster */ - def isSubspace(a: Space, b: Space)(using Context): Boolean = trace(s"isSubspace(${show(a)}, ${show(b)})", debug) { + def isSubspace(a: Space, b: Space)(using Context): Boolean = a.isSubspace(b) + + def computeIsSubspace(a: Space, b: Space)(using Context): Boolean = { def tryDecompose1(tp: Type) = canDecompose(tp) && isSubspace(Or(decompose(tp)), b) def tryDecompose2(tp: Type) = canDecompose(tp) && isSubspace(a, Or(decompose(tp))) @@ -302,9 +270,6 @@ trait SpaceLogic { Or(spaces) } } -} - -object SpaceEngine { /** Is the unapply or unapplySeq irrefutable? * @param unapp The unapply function reference @@ -352,13 +317,13 @@ object SpaceEngine { case _ => false } -} - -/** Scala implementation of space logic */ -class SpaceEngine(using Context) extends SpaceLogic { - import tpd._ - override def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type): Space = trace(s"atomic intersection: ${AndType(tp1, tp2).show}", debug) { + /** Return a space containing the values of both types. + * + * The types should be atomic (non-decomposable) and unrelated (neither + * should be a subtype of the other). + */ + def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug) { // Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1). if !ctx.mode.is(Mode.SafeNulls) && (tp1.isNullType || tp2.isNullType) then // Since projections of types don't include null, intersection with null is empty. @@ -373,7 +338,7 @@ class SpaceEngine(using Context) extends SpaceLogic { } /** Return the space that represents the pattern `pat` */ - def project(pat: Tree): Space = pat match { + def project(pat: Tree)(using Context): Space = pat match { case Literal(c) => if (c.value.isInstanceOf[Symbol]) Typ(c.value.asInstanceOf[Symbol].termRef, decomposed = false) @@ -436,12 +401,12 @@ class SpaceEngine(using Context) extends SpaceLogic { Typ(pat.tpe.narrow, decomposed = false) } - private def project(tp: Type): Space = tp match { + private def project(tp: Type)(using Context): Space = tp match { case OrType(tp1, tp2) => Or(project(tp1) :: project(tp2) :: Nil) case tp => Typ(tp, decomposed = true) } - private def unapplySeqInfo(resTp: Type, pos: SrcPos): (Int, Type, Type) = { + private def unapplySeqInfo(resTp: Type, pos: SrcPos)(using Context): (Int, Type, Type) = { var resultTp = resTp var elemTp = unapplySeqTypeElemTp(resultTp) var arity = productArity(resultTp, pos) @@ -488,7 +453,7 @@ class SpaceEngine(using Context) extends SpaceLogic { * If `isValue` is true, then pattern-bound symbols are erased to its upper bound. * This is needed to avoid spurious unreachable warnings. See tests/patmat/i6197.scala. */ - private def erase(tp: Type, inArray: Boolean = false, isValue: Boolean = false): Type = trace(i"$tp erased to", debug) { + private def erase(tp: Type, inArray: Boolean = false, isValue: Boolean = false)(using Context): Type = trace(i"$tp erased to", debug) { tp match { case tp @ AppliedType(tycon, args) if tycon.typeSymbol.isPatternBound => @@ -520,7 +485,7 @@ class SpaceEngine(using Context) extends SpaceLogic { /** Space of the pattern: unapplySeq(a, b, c: _*) */ - def projectSeq(pats: List[Tree]): Space = { + def projectSeq(pats: List[Tree])(using Context): Space = { if (pats.isEmpty) return Typ(defn.NilType, false) val (items, zero) = if (isWildcardStarArg(pats.last)) @@ -535,29 +500,30 @@ class SpaceEngine(using Context) extends SpaceLogic { } } - def isPrimToBox(tp: Type, pt: Type): Boolean = + def isPrimToBox(tp: Type, pt: Type)(using Context): Boolean = tp.isPrimitiveValueType && (defn.boxedType(tp).classSymbol eq pt.classSymbol) - private val isSubspaceCache = mutable.HashMap.empty[(Space, Space, Context), Boolean] - - override def isSubspace(a: Space, b: Space)(using Context): Boolean = a.isSubspace(b)(this) - - def computeIsSubspace(a: Space, b: Space)(using Context): Boolean = super.isSubspace(a, b) - /** Is `tp1` a subtype of `tp2`? */ - def isSubType(tp1: Type, tp2: Type): Boolean = trace(i"$tp1 <:< $tp2", debug, show = true) { + def isSubType(tp1: Type, tp2: Type)(using Context): Boolean = trace(i"$tp1 <:< $tp2", debug, show = true) { if tp1 == ConstantType(Constant(null)) && !ctx.mode.is(Mode.SafeNulls) then tp2 == ConstantType(Constant(null)) else tp1 <:< tp2 } - def isSameUnapply(tp1: TermRef, tp2: TermRef): Boolean = + /** True if we can assume that the two unapply methods are the same. + * That is, given the same parameter, they return the same result. + * + * We assume that unapply methods are pure, but the same method may + * be called with different prefixes, thus behaving differently. + */ + def isSameUnapply(tp1: TermRef, tp2: TermRef)(using Context): Boolean = // always assume two TypeTest[S, T].unapply are the same if they are equal in types (tp1.prefix.isStable && tp2.prefix.isStable || tp1.symbol == defn.TypeTest_unapply) && tp1 =:= tp2 - /** Parameter types of the case class type `tp`. Adapted from `unapplyPlan` in patternMatcher */ - def signature(unapp: TermRef, scrutineeTp: Type, argLen: Int): List[Type] = { + /** Return term parameter types of the extractor `unapp`. + * Parameter types of the case class type `tp`. Adapted from `unapplyPlan` in patternMatcher */ + def signature(unapp: TermRef, scrutineeTp: Type, argLen: Int)(using Context): List[Type] = { val unappSym = unapp.symbol // println("scrutineeTp = " + scrutineeTp.show) @@ -620,14 +586,14 @@ class SpaceEngine(using Context) extends SpaceLogic { } /** Whether the extractor covers the given type */ - def covers(unapp: TermRef, scrutineeTp: Type, argLen: Int): Boolean = + def covers(unapp: TermRef, scrutineeTp: Type, argLen: Int)(using Context): Boolean = SpaceEngine.isIrrefutable(unapp, argLen) || unapp.symbol == defn.TypeTest_unapply && { val AppliedType(_, _ :: tp :: Nil) = unapp.prefix.widen.dealias: @unchecked scrutineeTp <:< tp } /** Decompose a type into subspaces -- assume the type can be decomposed */ - def decompose(tp: Type): List[Typ] = trace(i"decompose($tp)", debug, show(_: Seq[Space])) { + def decompose(tp: Type)(using Context): List[Typ] = trace(i"decompose($tp)", debug, showSpaces) { def rec(tp: Type, mixins: List[Type]): List[Typ] = tp.dealias match { case AndType(tp1, tp2) => def decomposeComponent(tpA: Type, tpB: Type): List[Typ] = @@ -708,7 +674,7 @@ class SpaceEngine(using Context) extends SpaceLogic { } /** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */ - def canDecompose(tp: Type): Boolean = + def canDecompose(tp: Type)(using Context): Boolean = val res = tp.dealias match case AppliedType(tycon, _) if canDecompose(tycon) => true case tp: NamedType if canDecompose(tp.prefix) => true @@ -735,7 +701,7 @@ class SpaceEngine(using Context) extends SpaceLogic { * C --> C if current owner is C !!! * */ - def showType(tp: Type, showTypeArgs: Boolean = false): String = { + def showType(tp: Type, showTypeArgs: Boolean = false)(using Context): String = { val enclosingCls = ctx.owner.enclosingClass def isOmittable(sym: Symbol) = @@ -776,7 +742,7 @@ class SpaceEngine(using Context) extends SpaceLogic { } /** Whether the counterexample is satisfiable. The space is flattened and non-empty. */ - def satisfiable(sp: Space): Boolean = { + def satisfiable(sp: Space)(using Context): Boolean = { def impossible: Nothing = throw new AssertionError("`satisfiable` only accepts flattened space.") def genConstraint(space: Space): List[(Type, Type)] = space match { @@ -807,10 +773,10 @@ class SpaceEngine(using Context) extends SpaceLogic { checkConstraint(genConstraint(sp))(using ctx.fresh.setNewTyperState()) } - def show(ss: Seq[Space]): String = ss.map(show).mkString(", ") + def showSpaces(ss: Seq[Space])(using Context): String = ss.map(show).mkString(", ") /** Display spaces */ - def show(s: Space): String = { + def show(s: Space)(using Context): String = { def params(tp: Type): List[Type] = tp.classSymbol.primaryConstructor.info.firstParamTypes /** does the companion object of the given symbol have custom unapply */ @@ -862,7 +828,7 @@ class SpaceEngine(using Context) extends SpaceLogic { doShow(s, flattenList = false) } - private def exhaustivityCheckable(sel: Tree): Boolean = { + private def exhaustivityCheckable(sel: Tree)(using Context): Boolean = { val seen = collection.mutable.Set.empty[Type] // Possible to check everything, but be compatible with scalac by default @@ -891,8 +857,8 @@ class SpaceEngine(using Context) extends SpaceLogic { res } - /** Whehter counter-examples should be further checked? True for GADTs. */ - private def shouldCheckExamples(tp: Type): Boolean = + /** Whether counter-examples should be further checked? True for GADTs. */ + private def shouldCheckExamples(tp: Type)(using Context): Boolean = new TypeAccumulator[Boolean] { override def apply(b: Boolean, tp: Type): Boolean = tp match { case tref: TypeRef if tref.symbol.is(TypeParam) && variance != 1 => true @@ -903,7 +869,7 @@ class SpaceEngine(using Context) extends SpaceLogic { /** Return the underlying type of non-module, non-constant, non-enum case singleton types. * Also widen ExprType to its result type, and rewrap any annotation wrappers. * For example, with `val opt = None`, widen `opt.type` to `None.type`. */ - def toUnderlying(tp: Type): Type = trace(i"toUnderlying($tp)", show = true)(tp match { + def toUnderlying(tp: Type)(using Context): Type = trace(i"toUnderlying($tp)", show = true)(tp match { case _: ConstantType => tp case tp: TermRef if tp.symbol.is(Module) => tp case tp: TermRef if tp.symbol.isAllOf(EnumCase) => tp @@ -913,7 +879,7 @@ class SpaceEngine(using Context) extends SpaceLogic { case _ => tp }) - def checkExhaustivity(_match: Match): Unit = { + def checkExhaustivity(_match: Match)(using Context): Unit = { val Match(sel, cases) = _match debug.println(i"checking exhaustivity of ${_match}") @@ -939,10 +905,10 @@ class SpaceEngine(using Context) extends SpaceLogic { if uncovered.nonEmpty then val hasMore = uncovered.lengthCompare(6) > 0 val deduped = dedup(uncovered.take(6)) - report.warning(PatternMatchExhaustivity(show(deduped), hasMore), sel.srcPos) + report.warning(PatternMatchExhaustivity(showSpaces(deduped), hasMore), sel.srcPos) } - private def redundancyCheckable(sel: Tree): Boolean = + private def redundancyCheckable(sel: Tree)(using Context): Boolean = // Ignore Expr[T] and Type[T] for unreachability as a special case. // Quote patterns produce repeated calls to the same unapply method, but with different implicit parameters. // Since we assume that repeated calls to the same unapply method overlap @@ -952,7 +918,7 @@ class SpaceEngine(using Context) extends SpaceLogic { && !sel.tpe.widen.isRef(defn.QuotedExprClass) && !sel.tpe.widen.isRef(defn.QuotedTypeClass) - def checkRedundancy(_match: Match): Unit = { + def checkRedundancy(_match: Match)(using Context): Unit = { val Match(sel, _) = _match val cases = _match.cases.toIndexedSeq debug.println(i"checking redundancy in $_match") diff --git a/compiler/test/dotty/tools/dotc/transform/patmat/SpaceEngineTest.scala b/compiler/test/dotty/tools/dotc/transform/patmat/SpaceEngineTest.scala index 13dfb7af668a..c13ef0532348 100644 --- a/compiler/test/dotty/tools/dotc/transform/patmat/SpaceEngineTest.scala +++ b/compiler/test/dotty/tools/dotc/transform/patmat/SpaceEngineTest.scala @@ -11,14 +11,14 @@ import vulpix.TestConfiguration, TestConfiguration.basicClasspath import org.junit, junit.Test, junit.Assert.* class SpaceEngineTest: + import SpaceEngine.* + @Test def isSubspaceTest1: Unit = inCompilerContext(basicClasspath) { // Testing the property of `isSubspace` that: // isSubspace(a, b) <=> simplify(simplify(a) - simplify(a)) == Empty // Previously there were no simplify calls, // and this is a counter-example, // for which you need either to simplify(b) or simplify the minus result. - val engine = patmat.SpaceEngine() - import engine.* val tp = defn.ConsType.appliedTo(defn.AnyType) val unappTp = requiredMethod("scala.collection.immutable.::.unapply").termRef From 53a782751ac5eb21d84b137e8d23d4d49bd422f0 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 9 Jan 2023 20:34:44 +0000 Subject: [PATCH 163/657] Space: dedupe into intersectUnrelatedAtomicTypes --- .../tools/dotc/transform/patmat/Space.scala | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b6da5f2dba9c..d903d9e122a4 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -187,25 +187,19 @@ object SpaceEngine { else if (isSubType(tp2, tp1)) b else if (canDecompose(tp1)) tryDecompose1(tp1) else if (canDecompose(tp2)) tryDecompose2(tp2) - else intersectUnrelatedAtomicTypes(tp1, tp2) + else intersectUnrelatedAtomicTypes(tp1, tp2)(a) case (Typ(tp1, _), Prod(tp2, fun, ss)) => if (isSubType(tp2, tp1)) b else if (canDecompose(tp1)) tryDecompose1(tp1) else if (isSubType(tp1, tp2)) a // problematic corner case: inheriting a case class - else intersectUnrelatedAtomicTypes(tp1, tp2) match - case Typ(tp, _) => Prod(tp, fun, ss) - case sp => sp + else intersectUnrelatedAtomicTypes(tp1, tp2)(b) case (Prod(tp1, fun, ss), Typ(tp2, _)) => if (isSubType(tp1, tp2)) a else if (canDecompose(tp2)) tryDecompose2(tp2) else if (isSubType(tp2, tp1)) a // problematic corner case: inheriting a case class - else intersectUnrelatedAtomicTypes(tp1, tp2) match - case Typ(tp, _) => Prod(tp, fun, ss) - case sp => sp + else intersectUnrelatedAtomicTypes(tp1, tp2)(a) case (Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) => - if (!isSameUnapply(fun1, fun2)) intersectUnrelatedAtomicTypes(tp1, tp2) match - case Typ(tp, _) => Prod(tp, fun1, ss1) - case sp => sp + if (!isSameUnapply(fun1, fun2)) intersectUnrelatedAtomicTypes(tp1, tp2)(a) else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) Empty else Prod(tp1, fun1, ss1.zip(ss2).map((intersect _).tupled)) } @@ -323,13 +317,15 @@ object SpaceEngine { * The types should be atomic (non-decomposable) and unrelated (neither * should be a subtype of the other). */ - def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug) { + def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(sp: Space)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug) { // Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1). if !ctx.mode.is(Mode.SafeNulls) && (tp1.isNullType || tp2.isNullType) then // Since projections of types don't include null, intersection with null is empty. Empty else - val intersection = Typ(AndType(tp1, tp2), decomposed = false) + val intersection = sp match + case sp: Prod => sp.copy(AndType(tp1, tp2)) + case _ => Typ(AndType(tp1, tp2), decomposed = false) // unrelated numeric value classes can equal each other, so let's not consider type space intersection empty if tp1.classSymbol.isNumericValueClass && tp2.classSymbol.isNumericValueClass then intersection else if isPrimToBox(tp1, tp2) || isPrimToBox(tp2, tp1) then intersection From 23cee40af14f625c607e96e1bc31f092cc16715d Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 16 Feb 2023 10:58:36 +0000 Subject: [PATCH 164/657] Space: Prep for caching decompose/canDecompose on Typ --- .../tools/dotc/transform/patmat/Space.scala | 157 ++++++++---------- 1 file changed, 67 insertions(+), 90 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index d903d9e122a4..3003bea962ca 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -92,16 +92,16 @@ object SpaceEngine { def simplify(space: Space)(using Context): Space = trace(s"simplify ${show(space)} --> ", debug, show)(space match { case Prod(tp, fun, spaces) => val sps = spaces.mapconserve(simplify) - if (sps.contains(Empty)) Empty - else if (canDecompose(tp) && decompose(tp).isEmpty) Empty + if sps.contains(Empty) then Empty + else if canDecompose(tp) && decompose(tp).isEmpty then Empty else if sps eq spaces then space else Prod(tp, fun, sps) case Or(spaces) => val spaces2 = spaces.map(simplify).filter(_ != Empty) if spaces2.isEmpty then Empty else if spaces2.lengthIs == 1 then spaces2.head else if spaces2.corresponds(spaces)(_ eq _) then space else Or(spaces2) - case Typ(tp, _) => - if (canDecompose(tp) && decompose(tp).isEmpty) Empty + case typ: Typ => + if canDecompose(typ.tp) && decompose(typ.tp).isEmpty then Empty else space case _ => space }) @@ -143,25 +143,21 @@ object SpaceEngine { def isSubspace(a: Space, b: Space)(using Context): Boolean = a.isSubspace(b) def computeIsSubspace(a: Space, b: Space)(using Context): Boolean = { - def tryDecompose1(tp: Type) = canDecompose(tp) && isSubspace(Or(decompose(tp)), b) - def tryDecompose2(tp: Type) = canDecompose(tp) && isSubspace(a, Or(decompose(tp))) - val a2 = simplify(a) val b2 = simplify(b) if (a ne a2) || (b ne b2) then isSubspace(a2, b2) else (a, b) match { case (Empty, _) => true case (_, Empty) => false - case (Or(ss), _) => - ss.forall(isSubspace(_, b)) - case (Typ(tp1, _), Typ(tp2, _)) => + case (Or(ss), _) => ss.forall(isSubspace(_, b)) + case (a @ Typ(tp1, _), Or(ss)) => // optimization: don't go to subtraction too early + ss.exists(isSubspace(a, _)) + || canDecompose(tp1) && isSubspace(Or(decompose(tp1)), b) + case (_, Or(_)) => simplify(minus(a, b)) == Empty + case (a @ Typ(tp1, _), b @ Typ(tp2, _)) => isSubType(tp1, tp2) - || canDecompose(tp1) && tryDecompose1(tp1) - || canDecompose(tp2) && tryDecompose2(tp2) - case (Typ(tp1, _), Or(ss)) => // optimization: don't go to subtraction too early - ss.exists(isSubspace(a, _)) || tryDecompose1(tp1) - case (_, Or(_)) => - simplify(minus(a, b)) == Empty + || canDecompose(tp1) && isSubspace(Or(decompose(tp1)), b) + || canDecompose(tp2) && isSubspace(a, Or(decompose(tp2))) case (Prod(tp1, _, _), Typ(tp2, _)) => isSubType(tp1, tp2) case (Typ(tp1, _), Prod(tp2, fun, ss)) => @@ -169,90 +165,74 @@ object SpaceEngine { && covers(fun, tp1, ss.length) && isSubspace(Prod(tp2, fun, signature(fun, tp1, ss.length).map(Typ(_, false))), b) case (Prod(_, fun1, ss1), Prod(_, fun2, ss2)) => - isSameUnapply(fun1, fun2) && ss1.zip(ss2).forall((isSubspace _).tupled) + isSameUnapply(fun1, fun2) && ss1.lazyZip(ss2).forall(isSubspace) } } /** Intersection of two spaces */ def intersect(a: Space, b: Space)(using Context): Space = trace(s"${show(a)} & ${show(b)}", debug, show) { - def tryDecompose1(tp: Type) = intersect(Or(decompose(tp)), b) - def tryDecompose2(tp: Type) = intersect(a, Or(decompose(tp))) - (a, b) match { case (Empty, _) | (_, Empty) => Empty case (_, Or(ss)) => Or(ss.map(intersect(a, _)).filter(_ ne Empty)) case (Or(ss), _) => Or(ss.map(intersect(_, b)).filter(_ ne Empty)) - case (Typ(tp1, _), Typ(tp2, _)) => - if (isSubType(tp1, tp2)) a - else if (isSubType(tp2, tp1)) b - else if (canDecompose(tp1)) tryDecompose1(tp1) - else if (canDecompose(tp2)) tryDecompose2(tp2) + case (a @ Typ(tp1, _), b @ Typ(tp2, _)) => + if isSubType(tp1, tp2) then a + else if isSubType(tp2, tp1) then b + else if canDecompose(tp1) then intersect(Or(decompose(tp1)), b) + else if canDecompose(tp2) then intersect(a, Or(decompose(tp2))) else intersectUnrelatedAtomicTypes(tp1, tp2)(a) - case (Typ(tp1, _), Prod(tp2, fun, ss)) => - if (isSubType(tp2, tp1)) b - else if (canDecompose(tp1)) tryDecompose1(tp1) - else if (isSubType(tp1, tp2)) a // problematic corner case: inheriting a case class + case (a @ Typ(tp1, _), Prod(tp2, fun, ss)) => + if isSubType(tp2, tp1) then b + else if canDecompose(tp1) then intersect(Or(decompose(tp1)), b) + else if isSubType(tp1, tp2) then a // problematic corner case: inheriting a case class else intersectUnrelatedAtomicTypes(tp1, tp2)(b) - case (Prod(tp1, fun, ss), Typ(tp2, _)) => - if (isSubType(tp1, tp2)) a - else if (canDecompose(tp2)) tryDecompose2(tp2) - else if (isSubType(tp2, tp1)) a // problematic corner case: inheriting a case class + case (Prod(tp1, fun, ss), b @ Typ(tp2, _)) => + if isSubType(tp1, tp2) then a + else if canDecompose(tp2) then intersect(a, Or(decompose(tp2))) + else if isSubType(tp2, tp1) then a // problematic corner case: inheriting a case class else intersectUnrelatedAtomicTypes(tp1, tp2)(a) - case (Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) => - if (!isSameUnapply(fun1, fun2)) intersectUnrelatedAtomicTypes(tp1, tp2)(a) - else if (ss1.zip(ss2).exists(p => simplify(intersect(p._1, p._2)) == Empty)) Empty - else Prod(tp1, fun1, ss1.zip(ss2).map((intersect _).tupled)) + case (a @ Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) => + if !isSameUnapply(fun1, fun2) then intersectUnrelatedAtomicTypes(tp1, tp2)(a) + else if ss1.lazyZip(ss2).exists((a, b) => simplify(intersect(a, b)) == Empty) then Empty + else Prod(tp1, fun1, ss1.lazyZip(ss2).map(intersect)) } } /** The space of a not covered by b */ def minus(a: Space, b: Space)(using Context): Space = trace(s"${show(a)} - ${show(b)}", debug, show) { - def tryDecompose1(tp: Type) = minus(Or(decompose(tp)), b) - def tryDecompose2(tp: Type) = minus(a, Or(decompose(tp))) - (a, b) match { case (Empty, _) => Empty case (_, Empty) => a - case (Typ(tp1, _), Typ(tp2, _)) => - if (isSubType(tp1, tp2)) Empty - else if (canDecompose(tp1)) tryDecompose1(tp1) - else if (canDecompose(tp2)) tryDecompose2(tp2) + case (Or(ss), _) => Or(ss.map(minus(_, b))) + case (_, Or(ss)) => ss.foldLeft(a)(minus) + case (a @ Typ(tp1, _), b @ Typ(tp2, _)) => + if isSubType(tp1, tp2) then Empty + else if canDecompose(tp1) then minus(Or(decompose(tp1)), b) + else if canDecompose(tp2) then minus(a, Or(decompose(tp2))) else a - case (Typ(tp1, _), Prod(tp2, fun, ss)) => + case (a @ Typ(tp1, _), Prod(tp2, fun, ss)) => // rationale: every instance of `tp1` is covered by `tp2(_)` if isSubType(tp1, tp2) && covers(fun, tp1, ss.length) then minus(Prod(tp1, fun, signature(fun, tp1, ss.length).map(Typ(_, false))), b) - else if canDecompose(tp1) then - tryDecompose1(tp1) - else - a - case (Or(ss), _) => - Or(ss.map(minus(_, b))) - case (_, Or(ss)) => - ss.foldLeft(a)(minus) - case (Prod(tp1, fun, ss), Typ(tp2, _)) => + else if canDecompose(tp1) then minus(Or(decompose(tp1)), b) + else a + case (Prod(tp1, fun, ss), b @ Typ(tp2, _)) => // uncovered corner case: tp2 :< tp1, may happen when inheriting case class - if (isSubType(tp1, tp2)) - Empty - else if (simplify(a) == Empty) - Empty - else if (canDecompose(tp2)) - tryDecompose2(tp2) - else - a + if isSubType(tp1, tp2) then Empty + else if simplify(a) == Empty then Empty + else if canDecompose(tp2) then minus(a, Or(decompose(tp2))) + else a case (Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) - if (!isSameUnapply(fun1, fun2)) => a + if !isSameUnapply(fun1, fun2) => a case (Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) - if (fun1.symbol.name == nme.unapply && ss1.length != ss2.length) => a - case (Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) => - - val range = (0 until ss1.size).toList + if fun1.symbol.name == nme.unapply && ss1.length != ss2.length => a + case (a @ Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) => + val range = ss1.indices.toList val cache = Array.fill[Space | Null](ss2.length)(null) def sub(i: Int) = if cache(i) == null then cache(i) = minus(ss1(i), ss2(i)) cache(i).nn - end sub if range.exists(i => isSubspace(ss1(i), sub(i))) then a else if cache.forall(sub => isSubspace(sub.nn, Empty)) then Empty @@ -610,26 +590,26 @@ object SpaceEngine { else decomposeComponent(tp2, tp1) - case OrType(tp1, tp2) => List(Typ(tp1, true), Typ(tp2, true)) + case OrType(tp1, tp2) => List(Typ(tp1, decomposed = true), Typ(tp2, decomposed = true)) case tp if tp.isRef(defn.BooleanClass) => List( - Typ(ConstantType(Constant(true)), true), - Typ(ConstantType(Constant(false)), true) + Typ(ConstantType(Constant(true)), decomposed = true), + Typ(ConstantType(Constant(false)), decomposed = true) ) case tp if tp.isRef(defn.UnitClass) => - Typ(ConstantType(Constant(())), true) :: Nil + Typ(ConstantType(Constant(())), decomposed = true) :: Nil case tp if tp.classSymbol.isAllOf(JavaEnumTrait) => - tp.classSymbol.children.map(sym => Typ(sym.termRef, true)) + tp.classSymbol.children.map(sym => Typ(sym.termRef, decomposed = true)) case tp @ AppliedType(tycon, targs) if tp.classSymbol.children.isEmpty && canDecompose(tycon) => // It might not obvious that it's OK to apply the type arguments of a parent type to child types. // But this is guarded by `tp.classSymbol.children.isEmpty`, // meaning we'll decompose to the same class, just not the same type. // For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`. - rec(tycon, Nil).map(typ => Typ(tp.derivedAppliedType(typ.tp, targs))) + rec(tycon, Nil).map(typ => Typ(tp.derivedAppliedType(typ.tp, targs), decomposed = true)) case tp: NamedType if canDecompose(tp.prefix) => - rec(tp.prefix, Nil).map(typ => Typ(tp.derivedSelect(typ.tp))) + rec(tp.prefix, Nil).map(typ => Typ(tp.derivedSelect(typ.tp), decomposed = true)) case tp => def getChildren(sym: Symbol): List[Symbol] = @@ -641,30 +621,27 @@ object SpaceEngine { else List(child) } val children = getChildren(tp.classSymbol) - debug.println(s"candidates for ${tp.show} : [${children.map(_.show).mkString(", ")}]") + debug.println(i"candidates for $tp : $children") val parts = children.map { sym => val sym1 = if (sym.is(ModuleClass)) sym.sourceModule else sym val refined = TypeOps.refineUsingParent(tp, sym1, mixins) + debug.println(i"$sym1 refined to $refined") - debug.println(sym1.show + " refined to " + refined.show) - - def inhabited(tp: Type): Boolean = - tp.dealias match { - case AndType(tp1, tp2) => !TypeComparer.provablyDisjoint(tp1, tp2) - case OrType(tp1, tp2) => inhabited(tp1) || inhabited(tp2) - case tp: RefinedType => inhabited(tp.parent) - case tp: TypeRef => inhabited(tp.prefix) - case _ => true - } + def inhabited(tp: Type): Boolean = tp.dealias match + case AndType(tp1, tp2) => !TypeComparer.provablyDisjoint(tp1, tp2) + case OrType(tp1, tp2) => inhabited(tp1) || inhabited(tp2) + case tp: RefinedType => inhabited(tp.parent) + case tp: TypeRef => inhabited(tp.prefix) + case _ => true - if (inhabited(refined)) refined + if inhabited(refined) then refined else NoType - } filter(_.exists) + }.filter(_.exists) - debug.println(s"${tp.show} decomposes to [${parts.map(_.show).mkString(", ")}]") + debug.println(i"$tp decomposes to $parts") - parts.map(Typ(_, true)) + parts.map(Typ(_, decomposed = true)) } rec(tp, Nil) } From e316f65a54e17c86bf06d83ff6c447f27a72c690 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 16 Feb 2023 11:07:05 +0000 Subject: [PATCH 165/657] Space: Cache decompose/canDecompose on Typ --- .../tools/dotc/transform/patmat/Space.scala | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 3003bea962ca..d10fb537cb3a 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -77,7 +77,18 @@ case object Empty extends Space * @param decomposed: does the space result from decomposition? Used for pretty print * */ -case class Typ(tp: Type, decomposed: Boolean = true) extends Space +case class Typ(tp: Type, decomposed: Boolean = true) extends Space: + private var myCanDecompose: java.lang.Boolean = _ + private var myDecompose: List[Typ] = _ + + def canDecompose(using Context): Boolean = + if myCanDecompose == null then myCanDecompose = SpaceEngine.canDecompose(tp) + myCanDecompose + + def decompose(using Context): List[Typ] = + if myDecompose == null then myDecompose = SpaceEngine.decompose(tp) + myDecompose +end Typ /** Space representing an extractor pattern */ case class Prod(tp: Type, unappTp: TermRef, params: List[Space]) extends Space @@ -101,7 +112,7 @@ object SpaceEngine { else if spaces2.lengthIs == 1 then spaces2.head else if spaces2.corresponds(spaces)(_ eq _) then space else Or(spaces2) case typ: Typ => - if canDecompose(typ.tp) && decompose(typ.tp).isEmpty then Empty + if canDecompose(typ) && decompose(typ).isEmpty then Empty else space case _ => space }) @@ -152,12 +163,12 @@ object SpaceEngine { case (Or(ss), _) => ss.forall(isSubspace(_, b)) case (a @ Typ(tp1, _), Or(ss)) => // optimization: don't go to subtraction too early ss.exists(isSubspace(a, _)) - || canDecompose(tp1) && isSubspace(Or(decompose(tp1)), b) + || canDecompose(a) && isSubspace(Or(decompose(a)), b) case (_, Or(_)) => simplify(minus(a, b)) == Empty case (a @ Typ(tp1, _), b @ Typ(tp2, _)) => isSubType(tp1, tp2) - || canDecompose(tp1) && isSubspace(Or(decompose(tp1)), b) - || canDecompose(tp2) && isSubspace(a, Or(decompose(tp2))) + || canDecompose(a) && isSubspace(Or(decompose(a)), b) + || canDecompose(b) && isSubspace(a, Or(decompose(b))) case (Prod(tp1, _, _), Typ(tp2, _)) => isSubType(tp1, tp2) case (Typ(tp1, _), Prod(tp2, fun, ss)) => @@ -178,17 +189,17 @@ object SpaceEngine { case (a @ Typ(tp1, _), b @ Typ(tp2, _)) => if isSubType(tp1, tp2) then a else if isSubType(tp2, tp1) then b - else if canDecompose(tp1) then intersect(Or(decompose(tp1)), b) - else if canDecompose(tp2) then intersect(a, Or(decompose(tp2))) + else if canDecompose(a) then intersect(Or(decompose(a)), b) + else if canDecompose(b) then intersect(a, Or(decompose(b))) else intersectUnrelatedAtomicTypes(tp1, tp2)(a) case (a @ Typ(tp1, _), Prod(tp2, fun, ss)) => if isSubType(tp2, tp1) then b - else if canDecompose(tp1) then intersect(Or(decompose(tp1)), b) + else if canDecompose(a) then intersect(Or(decompose(a)), b) else if isSubType(tp1, tp2) then a // problematic corner case: inheriting a case class else intersectUnrelatedAtomicTypes(tp1, tp2)(b) case (Prod(tp1, fun, ss), b @ Typ(tp2, _)) => if isSubType(tp1, tp2) then a - else if canDecompose(tp2) then intersect(a, Or(decompose(tp2))) + else if canDecompose(b) then intersect(a, Or(decompose(b))) else if isSubType(tp2, tp1) then a // problematic corner case: inheriting a case class else intersectUnrelatedAtomicTypes(tp1, tp2)(a) case (a @ Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) => @@ -207,20 +218,20 @@ object SpaceEngine { case (_, Or(ss)) => ss.foldLeft(a)(minus) case (a @ Typ(tp1, _), b @ Typ(tp2, _)) => if isSubType(tp1, tp2) then Empty - else if canDecompose(tp1) then minus(Or(decompose(tp1)), b) - else if canDecompose(tp2) then minus(a, Or(decompose(tp2))) + else if canDecompose(a) then minus(Or(decompose(a)), b) + else if canDecompose(b) then minus(a, Or(decompose(b))) else a case (a @ Typ(tp1, _), Prod(tp2, fun, ss)) => // rationale: every instance of `tp1` is covered by `tp2(_)` if isSubType(tp1, tp2) && covers(fun, tp1, ss.length) then minus(Prod(tp1, fun, signature(fun, tp1, ss.length).map(Typ(_, false))), b) - else if canDecompose(tp1) then minus(Or(decompose(tp1)), b) + else if canDecompose(a) then minus(Or(decompose(a)), b) else a case (Prod(tp1, fun, ss), b @ Typ(tp2, _)) => // uncovered corner case: tp2 :< tp1, may happen when inheriting case class if isSubType(tp1, tp2) then Empty else if simplify(a) == Empty then Empty - else if canDecompose(tp2) then minus(a, Or(decompose(tp2))) + else if canDecompose(b) then minus(a, Or(decompose(b))) else a case (Prod(tp1, fun1, ss1), Prod(tp2, fun2, ss2)) if !isSameUnapply(fun1, fun2) => a @@ -568,6 +579,9 @@ object SpaceEngine { scrutineeTp <:< tp } + def canDecompose(typ: Typ)(using Context): Boolean = typ.canDecompose + def decompose(typ: Typ)(using Context): List[Typ] = typ.decompose + /** Decompose a type into subspaces -- assume the type can be decomposed */ def decompose(tp: Type)(using Context): List[Typ] = trace(i"decompose($tp)", debug, showSpaces) { def rec(tp: Type, mixins: List[Type]): List[Typ] = tp.dealias match { From 512a3073ac7b0a46a7571dcfcce9f7dfd9c8509f Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 16 Feb 2023 11:44:24 +0000 Subject: [PATCH 166/657] Space: Move out Typ-wrapping in decompose --- .../tools/dotc/transform/patmat/Space.scala | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index d10fb537cb3a..de92a3c01076 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -584,19 +584,13 @@ object SpaceEngine { /** Decompose a type into subspaces -- assume the type can be decomposed */ def decompose(tp: Type)(using Context): List[Typ] = trace(i"decompose($tp)", debug, showSpaces) { - def rec(tp: Type, mixins: List[Type]): List[Typ] = tp.dealias match { + def rec(tp: Type, mixins: List[Type]): List[Type] = tp.dealias match { case AndType(tp1, tp2) => - def decomposeComponent(tpA: Type, tpB: Type): List[Typ] = - rec(tpA, tpB :: mixins).flatMap { - case Typ(tp, _) => - if tp <:< tpB then - Typ(tp, decomposed = true) :: Nil - else if tpB <:< tp then - Typ(tpB, decomposed = true) :: Nil - else if TypeComparer.provablyDisjoint(tp, tpB) then - Nil - else - Typ(AndType(tp, tpB), decomposed = true) :: Nil + def decomposeComponent(tpA: Type, tpB: Type): List[Type] = + rec(tpA, tpB :: mixins).collect { + case tp if tp <:< tpB => tp + case tp if tpB <:< tp => tpB + case tp if !TypeComparer.provablyDisjoint(tp, tpB) => AndType(tp, tpB) } if canDecompose(tp1) then @@ -604,26 +598,26 @@ object SpaceEngine { else decomposeComponent(tp2, tp1) - case OrType(tp1, tp2) => List(Typ(tp1, decomposed = true), Typ(tp2, decomposed = true)) + case OrType(tp1, tp2) => List(tp1, tp2) case tp if tp.isRef(defn.BooleanClass) => List( - Typ(ConstantType(Constant(true)), decomposed = true), - Typ(ConstantType(Constant(false)), decomposed = true) + ConstantType(Constant(true)), + ConstantType(Constant(false)) ) case tp if tp.isRef(defn.UnitClass) => - Typ(ConstantType(Constant(())), decomposed = true) :: Nil + ConstantType(Constant(())) :: Nil case tp if tp.classSymbol.isAllOf(JavaEnumTrait) => - tp.classSymbol.children.map(sym => Typ(sym.termRef, decomposed = true)) + tp.classSymbol.children.map(_.termRef) case tp @ AppliedType(tycon, targs) if tp.classSymbol.children.isEmpty && canDecompose(tycon) => // It might not obvious that it's OK to apply the type arguments of a parent type to child types. // But this is guarded by `tp.classSymbol.children.isEmpty`, // meaning we'll decompose to the same class, just not the same type. // For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`. - rec(tycon, Nil).map(typ => Typ(tp.derivedAppliedType(typ.tp, targs), decomposed = true)) + rec(tycon, Nil).map(tp.derivedAppliedType(_, targs)) case tp: NamedType if canDecompose(tp.prefix) => - rec(tp.prefix, Nil).map(typ => Typ(tp.derivedSelect(typ.tp), decomposed = true)) + rec(tp.prefix, Nil).map(tp.derivedSelect) case tp => def getChildren(sym: Symbol): List[Symbol] = @@ -655,9 +649,9 @@ object SpaceEngine { debug.println(i"$tp decomposes to $parts") - parts.map(Typ(_, decomposed = true)) + parts } - rec(tp, Nil) + rec(tp, Nil).map(Typ(_, decomposed = true)) } /** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */ From 7f4009b891da72a3fe1fa3d44a16ba5f7105c761 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 16 Feb 2023 14:59:55 +0000 Subject: [PATCH 167/657] Space: Redefine canDecompose in terms of decompose --- .../tools/dotc/transform/patmat/Space.scala | 113 ++++++++---------- 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index de92a3c01076..cffe6a378649 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -78,15 +78,12 @@ case object Empty extends Space * */ case class Typ(tp: Type, decomposed: Boolean = true) extends Space: - private var myCanDecompose: java.lang.Boolean = _ private var myDecompose: List[Typ] = _ - def canDecompose(using Context): Boolean = - if myCanDecompose == null then myCanDecompose = SpaceEngine.canDecompose(tp) - myCanDecompose + def canDecompose(using Context): Boolean = decompose != SpaceEngine.ListOfTypNoType def decompose(using Context): List[Typ] = - if myDecompose == null then myDecompose = SpaceEngine.decompose(tp) + if myDecompose == null then myDecompose = SpaceEngine.decompose(tp).map(Typ(_, decomposed = true)) myDecompose end Typ @@ -104,7 +101,7 @@ object SpaceEngine { case Prod(tp, fun, spaces) => val sps = spaces.mapconserve(simplify) if sps.contains(Empty) then Empty - else if canDecompose(tp) && decompose(tp).isEmpty then Empty + else if decompose(tp).isEmpty then Empty else if sps eq spaces then space else Prod(tp, fun, sps) case Or(spaces) => val spaces2 = spaces.map(simplify).filter(_ != Empty) @@ -112,7 +109,7 @@ object SpaceEngine { else if spaces2.lengthIs == 1 then spaces2.head else if spaces2.corresponds(spaces)(_ eq _) then space else Or(spaces2) case typ: Typ => - if canDecompose(typ) && decompose(typ).isEmpty then Empty + if decompose(typ).isEmpty then Empty else space case _ => space }) @@ -583,43 +580,53 @@ object SpaceEngine { def decompose(typ: Typ)(using Context): List[Typ] = typ.decompose /** Decompose a type into subspaces -- assume the type can be decomposed */ - def decompose(tp: Type)(using Context): List[Typ] = trace(i"decompose($tp)", debug, showSpaces) { - def rec(tp: Type, mixins: List[Type]): List[Type] = tp.dealias match { + def decompose(tp: Type)(using Context): List[Type] = trace(i"decompose($tp)", debug) { + var lastType: Type = NoType + var lastParts: List[Type] = Nil + def dec(tp: Type) = + if tp eq lastType then lastParts + else + lastType = tp + lastParts = decompose(tp) + lastParts + def canDec(tp: Type) = + lastParts = dec(tp) + lastParts != ListOfNoType + + def rec(tp: Type, mixins: List[Type]): List[Type] = tp.dealias match case AndType(tp1, tp2) => - def decomposeComponent(tpA: Type, tpB: Type): List[Type] = - rec(tpA, tpB :: mixins).collect { - case tp if tp <:< tpB => tp - case tp if tpB <:< tp => tpB - case tp if !TypeComparer.provablyDisjoint(tp, tpB) => AndType(tp, tpB) - } - - if canDecompose(tp1) then - decomposeComponent(tp1, tp2) - else - decomposeComponent(tp2, tp1) - - case OrType(tp1, tp2) => List(tp1, tp2) - case tp if tp.isRef(defn.BooleanClass) => - List( - ConstantType(Constant(true)), - ConstantType(Constant(false)) - ) - case tp if tp.isRef(defn.UnitClass) => - ConstantType(Constant(())) :: Nil - case tp if tp.classSymbol.isAllOf(JavaEnumTrait) => - tp.classSymbol.children.map(_.termRef) - - case tp @ AppliedType(tycon, targs) if tp.classSymbol.children.isEmpty && canDecompose(tycon) => + var tpB = tp2 + var parts = rec(tp1, tp2 :: mixins) + if parts == ListOfNoType then + tpB = tp1 + parts = rec(tp2, tp1 :: mixins) + if parts == ListOfNoType then ListOfNoType + else parts.collect: + case tp if tp <:< tpB => tp + case tp if tpB <:< tp => tpB + case tp if !TypeComparer.provablyDisjoint(tp, tpB) => AndType(tp, tpB) + + case OrType(tp1, tp2) => List(tp1, tp2) + case _: SingletonType => ListOfNoType + case tp if tp.isRef(defn.BooleanClass) => List(ConstantType(Constant(true)), ConstantType(Constant(false))) + case tp if tp.isRef(defn.UnitClass) => ConstantType(Constant(())) :: Nil + case tp if tp.classSymbol.isAllOf(JavaEnumTrait) => tp.classSymbol.children.map(_.termRef) + case tp: NamedType if canDec(tp.prefix) => dec(tp.prefix).map(tp.derivedSelect) + + case tp @ AppliedType(tycon, targs) if tp.classSymbol.children.isEmpty && canDec(tycon) => // It might not obvious that it's OK to apply the type arguments of a parent type to child types. // But this is guarded by `tp.classSymbol.children.isEmpty`, // meaning we'll decompose to the same class, just not the same type. // For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`. - rec(tycon, Nil).map(tp.derivedAppliedType(_, targs)) - - case tp: NamedType if canDecompose(tp.prefix) => - rec(tp.prefix, Nil).map(tp.derivedSelect) + dec(tycon).map(tp.derivedAppliedType(_, targs)) - case tp => + case tp if { + val cls = tp.classSymbol + cls.is(Sealed) + && cls.isOneOf(AbstractOrTrait) + && !cls.hasAnonymousChild + && cls.children.nonEmpty + } => def getChildren(sym: Symbol): List[Symbol] = sym.children.flatMap { child => if child eq sym then List(sym) // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz... @@ -646,33 +653,17 @@ object SpaceEngine { if inhabited(refined) then refined else NoType }.filter(_.exists) - debug.println(i"$tp decomposes to $parts") - parts - } - rec(tp, Nil).map(Typ(_, decomposed = true)) + + case _ => ListOfNoType + end rec + + rec(tp, Nil) } - /** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */ - def canDecompose(tp: Type)(using Context): Boolean = - val res = tp.dealias match - case AppliedType(tycon, _) if canDecompose(tycon) => true - case tp: NamedType if canDecompose(tp.prefix) => true - case _: SingletonType => false - case _: OrType => true - case AndType(tp1, tp2) => canDecompose(tp1) || canDecompose(tp2) - case _ => - val cls = tp.classSymbol - cls.is(Sealed) - && cls.isOneOf(AbstractOrTrait) - && !cls.hasAnonymousChild - && cls.children.nonEmpty - || cls.isAllOf(JavaEnumTrait) - || tp.isRef(defn.BooleanClass) - || tp.isRef(defn.UnitClass) - //debug.println(s"decomposable: ${tp.show} = $res") - res + val ListOfNoType = List(NoType) + val ListOfTypNoType = ListOfNoType.map(Typ(_, decomposed = true)) /** Show friendly type name with current scope in mind * From 917b6ea7658f4b8e1536b3f6c3d4fcd2b6a326c9 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 16 Feb 2023 15:08:38 +0000 Subject: [PATCH 168/657] Space: Use a custom Parts extractor --- .../tools/dotc/transform/patmat/Space.scala | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index cffe6a378649..64e0d9a94a1a 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -83,7 +83,9 @@ case class Typ(tp: Type, decomposed: Boolean = true) extends Space: def canDecompose(using Context): Boolean = decompose != SpaceEngine.ListOfTypNoType def decompose(using Context): List[Typ] = - if myDecompose == null then myDecompose = SpaceEngine.decompose(tp).map(Typ(_, decomposed = true)) + if myDecompose == null then myDecompose = tp match + case SpaceEngine.Parts(parts) => parts.map(Typ(_, decomposed = true)) + case _ => SpaceEngine.ListOfTypNoType myDecompose end Typ @@ -581,18 +583,6 @@ object SpaceEngine { /** Decompose a type into subspaces -- assume the type can be decomposed */ def decompose(tp: Type)(using Context): List[Type] = trace(i"decompose($tp)", debug) { - var lastType: Type = NoType - var lastParts: List[Type] = Nil - def dec(tp: Type) = - if tp eq lastType then lastParts - else - lastType = tp - lastParts = decompose(tp) - lastParts - def canDec(tp: Type) = - lastParts = dec(tp) - lastParts != ListOfNoType - def rec(tp: Type, mixins: List[Type]): List[Type] = tp.dealias match case AndType(tp1, tp2) => var tpB = tp2 @@ -611,14 +601,14 @@ object SpaceEngine { case tp if tp.isRef(defn.BooleanClass) => List(ConstantType(Constant(true)), ConstantType(Constant(false))) case tp if tp.isRef(defn.UnitClass) => ConstantType(Constant(())) :: Nil case tp if tp.classSymbol.isAllOf(JavaEnumTrait) => tp.classSymbol.children.map(_.termRef) - case tp: NamedType if canDec(tp.prefix) => dec(tp.prefix).map(tp.derivedSelect) + case tp @ TypeRef(Parts(parts), _) => parts.map(tp.derivedSelect) - case tp @ AppliedType(tycon, targs) if tp.classSymbol.children.isEmpty && canDec(tycon) => + case tp @ AppliedType(Parts(parts), targs) if tp.classSymbol.children.isEmpty => // It might not obvious that it's OK to apply the type arguments of a parent type to child types. // But this is guarded by `tp.classSymbol.children.isEmpty`, // meaning we'll decompose to the same class, just not the same type. // For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`. - dec(tycon).map(tp.derivedAppliedType(_, targs)) + parts.map(tp.derivedAppliedType(_, targs)) case tp if { val cls = tp.classSymbol @@ -665,6 +655,12 @@ object SpaceEngine { val ListOfNoType = List(NoType) val ListOfTypNoType = ListOfNoType.map(Typ(_, decomposed = true)) + object Parts: + def unapply(tp: Type)(using Context): PartsExtractor = PartsExtractor(decompose(tp)) + + final class PartsExtractor(val get: List[Type]) extends AnyVal: + def isEmpty: Boolean = get == ListOfNoType + /** Show friendly type name with current scope in mind * * E.g. C.this.B --> B if current owner is C From f5bc2a00b0d4dae90b5ac433d88313767e0e65c5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 16 Feb 2023 16:32:07 +0100 Subject: [PATCH 169/657] Fix beta-reduction with `Nothing` and `null` args Fixes part of #15165 --- compiler/src/dotty/tools/dotc/transform/BetaReduce.scala | 5 ++++- tests/pos/i15165.scala | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i15165.scala diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index 97dc4697db6d..c586bb06cc29 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -128,7 +128,10 @@ object BetaReduce: ref.symbol case _ => val flags = Synthetic | (param.symbol.flags & Erased) - val tpe = if arg.tpe.dealias.isInstanceOf[ConstantType] then arg.tpe.dealias else arg.tpe.widen + val tpe = + if arg.tpe.isBottomType then param.tpe.widenTermRefExpr + else if arg.tpe.dealias.isInstanceOf[ConstantType] then arg.tpe.dealias + else arg.tpe.widen val binding = ValDef(newSymbol(ctx.owner, param.name, flags, tpe, coord = arg.span), arg).withSpan(arg.span) if !(tpe.isInstanceOf[ConstantType] && isPureExpr(arg)) then bindings += binding diff --git a/tests/pos/i15165.scala b/tests/pos/i15165.scala new file mode 100644 index 000000000000..15e89c90e900 --- /dev/null +++ b/tests/pos/i15165.scala @@ -0,0 +1,6 @@ +def test1 = { (y: Int) => y + 1 }.apply(???) + +class C: + def x: Int = 8 + +def test2 = { (c: C) => c.x }.apply(null) From 82feb9bfb69d189c0b917f07f26ef2f4c9cab17c Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 16 Feb 2023 15:53:07 +0100 Subject: [PATCH 170/657] Register usage of symbols in non-inferred type trees in CheckUnused fixes lampepfl#16930 --- .../tools/dotc/transform/CheckUnused.scala | 59 ++++++++++--------- .../fatal-warnings/i16930.scala | 22 +++++++ 2 files changed, 54 insertions(+), 27 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i16930.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6c47c12ac07c..663f7f15b96f 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -46,12 +46,11 @@ class CheckUnused extends MiniPhase: */ private val _key = Property.Key[UnusedData] - extension (k: Property.Key[UnusedData]) - private def unusedDataApply[U](f: UnusedData => U)(using Context): Context = - ctx.property(_key).foreach(f) - ctx - private def getUnusedData(using Context): Option[UnusedData] = - ctx.property(_key) + private def unusedDataApply[U](f: UnusedData => U)(using Context): Context = + ctx.property(_key).foreach(f) + ctx + private def getUnusedData(using Context): Option[UnusedData] = + ctx.property(_key) override def phaseName: String = CheckUnused.phaseName @@ -71,7 +70,7 @@ class CheckUnused extends MiniPhase: // ========== END + REPORTING ========== override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = - _key.unusedDataApply(ud => reportUnused(ud.getUnused)) + unusedDataApply(ud => reportUnused(ud.getUnused)) tree // ========== MiniPhase Prepare ========== @@ -81,15 +80,15 @@ class CheckUnused extends MiniPhase: ctx override def prepareForIdent(tree: tpd.Ident)(using Context): Context = - if tree.symbol.exists then - _key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) + if tree.symbol.exists then + unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then - _key.unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) + unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) else ctx override def prepareForSelect(tree: tpd.Select)(using Context): Context = - _key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) + unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) override def prepareForBlock(tree: tpd.Block)(using Context): Context = pushInBlockTemplatePackageDef(tree) @@ -101,7 +100,7 @@ class CheckUnused extends MiniPhase: pushInBlockTemplatePackageDef(tree) override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = - _key.unusedDataApply{ud => + unusedDataApply{ud => // do not register the ValDef generated for `object` if !tree.symbol.is(Module) then ud.registerDef(tree) @@ -109,7 +108,7 @@ class CheckUnused extends MiniPhase: } override def prepareForDefDef(tree: tpd.DefDef)(using Context): Context = - _key.unusedDataApply{ ud => + unusedDataApply{ ud => import ud.registerTrivial tree.registerTrivial ud.registerDef(tree) @@ -117,17 +116,17 @@ class CheckUnused extends MiniPhase: } override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context = - _key.unusedDataApply{ ud => + unusedDataApply{ ud => if !tree.symbol.is(Param) then // Ignore type parameter (as Scala 2) ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } override def prepareForBind(tree: tpd.Bind)(using Context): Context = - _key.unusedDataApply(_.registerPatVar(tree)) + unusedDataApply(_.registerPatVar(tree)) override def prepareForTypeTree(tree: tpd.TypeTree)(using Context): Context = - typeTraverser(_key.unusedDataApply).traverse(tree.tpe) + if !tree.isInstanceOf[tpd.InferredTypeTree] then typeTraverser(unusedDataApply).traverse(tree.tpe) ctx // ========== MiniPhase Transform ========== @@ -145,27 +144,27 @@ class CheckUnused extends MiniPhase: tree override def transformValDef(tree: tpd.ValDef)(using Context): tpd.Tree = - _key.unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + unusedDataApply(_.removeIgnoredUsage(tree.symbol)) tree override def transformDefDef(tree: tpd.DefDef)(using Context): tpd.Tree = - _key.unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + unusedDataApply(_.removeIgnoredUsage(tree.symbol)) tree override def transformTypeDef(tree: tpd.TypeDef)(using Context): tpd.Tree = - _key.unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + unusedDataApply(_.removeIgnoredUsage(tree.symbol)) tree // ---------- MiniPhase HELPERS ----------- private def pushInBlockTemplatePackageDef(tree: tpd.Block | tpd.Template | tpd.PackageDef)(using Context): Context = - _key.unusedDataApply { ud => + unusedDataApply { ud => ud.pushScope(UnusedData.ScopeType.fromTree(tree)) } ctx private def popOutBlockTemplatePackageDef()(using Context): Context = - _key.unusedDataApply { ud => + unusedDataApply { ud => ud.popScope() } ctx @@ -188,7 +187,7 @@ class CheckUnused extends MiniPhase: val newCtx = if tree.symbol.exists then ctx.withOwner(tree.symbol) else ctx tree match case imp:tpd.Import => - _key.unusedDataApply(_.registerImport(imp)) + unusedDataApply(_.registerImport(imp)) traverseChildren(tree)(using newCtx) case ident: Ident => prepareForIdent(ident) @@ -198,7 +197,7 @@ class CheckUnused extends MiniPhase: traverseChildren(tree)(using newCtx) case _: (tpd.Block | tpd.Template | tpd.PackageDef) => //! DIFFERS FROM MINIPHASE - _key.unusedDataApply { ud => + unusedDataApply { ud => ud.inNewScope(ScopeType.fromTree(tree))(traverseChildren(tree)(using newCtx)) } case t:tpd.ValDef => @@ -216,9 +215,10 @@ class CheckUnused extends MiniPhase: case t: tpd.Bind => prepareForBind(t) traverseChildren(tree)(using newCtx) + case _: tpd.InferredTypeTree => case t@tpd.TypeTree() => //! DIFFERS FROM MINIPHASE - typeTraverser(_key.unusedDataApply).traverse(t.tpe) + typeTraverser(unusedDataApply).traverse(t.tpe) traverseChildren(tree)(using newCtx) case _ => //! DIFFERS FROM MINIPHASE @@ -228,9 +228,14 @@ class CheckUnused extends MiniPhase: /** This is a type traverser which catch some special Types not traversed by the term traverser above */ private def typeTraverser(dt: (UnusedData => Any) => Unit)(using Context) = new TypeTraverser: - override def traverse(tp: Type): Unit = tp match - case AnnotatedType(_, annot) => dt(_.registerUsed(annot.symbol, None)) - case _ => traverseChildren(tp) + override def traverse(tp: Type): Unit = + if tp.typeSymbol.exists then dt(_.registerUsed(tp.typeSymbol, Some(tp.typeSymbol.name))) + tp match + case AnnotatedType(_, annot) => + dt(_.registerUsed(annot.symbol, None)) + traverseChildren(tp) + case _ => + traverseChildren(tp) /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = diff --git a/tests/neg-custom-args/fatal-warnings/i16930.scala b/tests/neg-custom-args/fatal-warnings/i16930.scala new file mode 100644 index 000000000000..1f6c5bf1a09f --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16930.scala @@ -0,0 +1,22 @@ +// scalac: -Wunused:imports + +trait Outer: + trait Used + trait Unused + +object Test { + val outer: Outer = ??? + import outer.{Used, Unused} // error + def foo(x: Any): Used = x.asInstanceOf[Used] +} + +trait Outer1: + trait UnusedToo1 + trait Unused1 + def unusedToo1: UnusedToo1 + +object Test1 { + val outer1: Outer1 = ??? + import outer1.{Unused1, UnusedToo1} // error // error + def foo() = outer1.unusedToo1 // in this case UnusedToo1 is not used explicitly, only inferred +} From 4cc4339ed4c727dfd8039002ebdc48fc6bbf0b50 Mon Sep 17 00:00:00 2001 From: Paul Coral Date: Sat, 18 Feb 2023 16:41:59 +0100 Subject: [PATCH 171/657] Traverse annotations instead of just registering - Traverse the tree of annotations - Update test suits --- .../tools/dotc/transform/CheckUnused.scala | 18 +++++++++--------- .../fatal-warnings/i15503i.scala | 9 +++++++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6c47c12ac07c..f66412e16d36 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -28,6 +28,7 @@ import dotty.tools.dotc.core.Types.ConstantType import dotty.tools.dotc.core.NameKinds.WildcardParamName import dotty.tools.dotc.core.Types.TermRef import dotty.tools.dotc.core.Types.NameFilter +import dotty.tools.dotc.core.Symbols.Symbol @@ -103,6 +104,7 @@ class CheckUnused extends MiniPhase: override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = _key.unusedDataApply{ud => // do not register the ValDef generated for `object` + traverseAnnotations(tree.symbol) if !tree.symbol.is(Module) then ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) @@ -112,6 +114,7 @@ class CheckUnused extends MiniPhase: _key.unusedDataApply{ ud => import ud.registerTrivial tree.registerTrivial + traverseAnnotations(tree.symbol) ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } @@ -119,11 +122,13 @@ class CheckUnused extends MiniPhase: override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context = _key.unusedDataApply{ ud => if !tree.symbol.is(Param) then // Ignore type parameter (as Scala 2) + traverseAnnotations(tree.symbol) ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } override def prepareForBind(tree: tpd.Bind)(using Context): Context = + traverseAnnotations(tree.symbol) _key.unusedDataApply(_.registerPatVar(tree)) override def prepareForTypeTree(tree: tpd.TypeTree)(using Context): Context = @@ -232,6 +237,10 @@ class CheckUnused extends MiniPhase: case AnnotatedType(_, annot) => dt(_.registerUsed(annot.symbol, None)) case _ => traverseChildren(tp) + /** This traverse the annotations of the symbol */ + private def traverseAnnotations(sym: Symbol)(using Context): Unit = + sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree)) + /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = import CheckUnused.WarnTypes @@ -274,7 +283,6 @@ object CheckUnused: private class UnusedData: import dotty.tools.dotc.transform.CheckUnused.UnusedData.UnusedResult import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack} - import dotty.tools.dotc.core.Symbols.Symbol import UnusedData.ScopeType /** The current scope during the tree traversal */ @@ -324,11 +332,6 @@ object CheckUnused: execInNewScope popScope() - /** Register all annotations of this symbol's denotation */ - def registerUsedAnnotation(sym: Symbol)(using Context): Unit = - val annotSym = sym.denot.annotations.map(_.symbol) - annotSym.foreach(s => registerUsed(s, None)) - /** * Register a found (used) symbol along with its name * @@ -363,8 +366,6 @@ object CheckUnused: /** Register (or not) some `val` or `def` according to the context, scope and flags */ def registerDef(memDef: tpd.MemberDef)(using Context): Unit = - // register the annotations for usage - registerUsedAnnotation(memDef.symbol) if memDef.isValidMemberDef then if memDef.isValidParam then if memDef.symbol.isOneOf(GivenOrImplicit) then @@ -378,7 +379,6 @@ object CheckUnused: /** Register pattern variable */ def registerPatVar(patvar: tpd.Bind)(using Context): Unit = - registerUsedAnnotation(patvar.symbol) if !patvar.symbol.isUnusedAnnot then patVarsInScope += patvar diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 7eae207d952d..ccf9344319d2 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -142,3 +142,12 @@ package foo.test.i16822: val x = ExampleEnum.List // OK println(x) // OK } + +package foo.test.i16877: + import scala.collection.immutable.HashMap // OK + import scala.annotation.StaticAnnotation // OK + + class ExampleAnnotation(val a: Object) extends StaticAnnotation // OK + + @ExampleAnnotation(new HashMap()) // OK + class Test //OK From d5fd114a52f4e7f591c44f5acf07603d819a201d Mon Sep 17 00:00:00 2001 From: Paul Coral Date: Sat, 18 Feb 2023 17:16:38 +0100 Subject: [PATCH 172/657] Ignore parameter of accessors - Do not report parameter of accessors - Update test suit --- .../tools/dotc/transform/CheckUnused.scala | 4 +- .../fatal-warnings/i15503e.scala | 3 ++ .../fatal-warnings/i15503i.scala | 43 +++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 663f7f15b96f..2f1804ad9218 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -80,7 +80,7 @@ class CheckUnused extends MiniPhase: ctx override def prepareForIdent(tree: tpd.Ident)(using Context): Context = - if tree.symbol.exists then + if tree.symbol.exists then unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) @@ -621,7 +621,7 @@ object CheckUnused: (sym.is(Param) || sym.isAllOf(PrivateParamAccessor | Local, butNot = CaseAccessor)) && !isSyntheticMainParam(sym) && !sym.shouldNotReportParamOwner && - (!sym.exists || !sym.owner.isAllOf(Synthetic | PrivateLocal)) + (!sym.exists || !(sym.owner.isAllOf(Synthetic | PrivateLocal) || sym.owner.is(Accessor))) private def shouldReportPrivateDef(using Context): Boolean = currScopeType.top == ScopeType.Template && !memDef.symbol.isConstructor && memDef.symbol.is(Private, butNot = SelfName | Synthetic | CaseAccessor) diff --git a/tests/neg-custom-args/fatal-warnings/i15503e.scala b/tests/neg-custom-args/fatal-warnings/i15503e.scala index 79112942a205..cd56587327cd 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503e.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503e.scala @@ -52,3 +52,6 @@ package foo.test.trivial: def f77(x: Int) = foo // error } object Y + +package foo.test.i16955: + class S(var r: String) // OK \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 7eae207d952d..bedf9a8f4791 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -126,6 +126,49 @@ package foo.test.possibleclasses: def a = k + y + s + t + z } +package foo.test.possibleclasses.withvar: + case class AllCaseClass( + k: Int, // OK + private var y: Int // OK /* Kept as it can be taken from pattern */ + )( + s: Int, // error /* But not these */ + var t: Int, // OK + private var z: Int // error + ) + + case class AllCaseUsed( + k: Int, // OK + private var y: Int // OK + )( + s: Int, // OK + var t: Int, // OK + private var z: Int // OK + ) { + def a = k + y + s + t + z + } + + class AllClass( + k: Int, // error + private var y: Int // error + )( + s: Int, // error + var t: Int, // OK + private var z: Int // error + ) + + class AllUsed( + k: Int, // OK + private var y: Int // OK + )( + s: Int, // OK + var t: Int, // OK + private var z: Int // OK + ) { + def a = k + y + s + t + z + } + + + package foo.test.from.i16675: case class PositiveNumber private (i: Int) // OK object PositiveNumber: From f18961558e06289eb33ee0632b6ce61ec4efb619 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 3 Oct 2022 23:10:38 -0700 Subject: [PATCH 173/657] Clarify ambiguous reference error message --- .../dotty/tools/dotc/reporting/messages.scala | 30 ++++++++----- tests/neg/ambiref.check | 16 +++---- tests/neg/i12682.check | 45 +++++++++++++++++++ tests/neg/i12682.scala | 13 ++++++ tests/neg/i13558.check | 8 ++-- tests/neg/i9803.check | 4 +- 6 files changed, 91 insertions(+), 25 deletions(-) create mode 100644 tests/neg/i12682.check create mode 100644 tests/neg/i12682.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index d7920e1f1b36..92ee1b44def0 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1328,21 +1328,29 @@ class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec } def msg(using Context) = - i"""|Reference to $name is ambiguous, - |it is both ${bindingString(newPrec, ctx)} + i"""|Reference to $name is ambiguous. + |It is both ${bindingString(newPrec, ctx)} |and ${bindingString(prevPrec, prevCtx, " subsequently")}""" def explain(using Context) = - i"""|The compiler can't decide which of the possible choices you - |are referencing with $name: A definition of lower precedence - |in an inner scope, or a definition with higher precedence in - |an outer scope. - |Note: - | - Definitions in an enclosing scope take precedence over inherited definitions - | - Definitions take precedence over imports + val precedent = + if newPrec == prevPrec then """two bindings of equal precedence + |were introduced in the same scope.""".stripMargin + else """a binding of lower precedence + |in an inner scope cannot shadow a binding with higher precedence in + |an outer scope.""".stripMargin + + i"""|The identifier $name is ambiguous because $precedent + | + |The precedence of the different kinds of bindings, from highest to lowest, is: + | - Definitions in an enclosing scope + | - Inherited definitions and top-level definitions in packages + | - Names introduced by imports | - Named imports take precedence over wildcard imports - | - You may replace a name when imported using - | ${hl("import")} scala.{ $name => ${name.show + "Tick"} } + | - Definitions from packages in other files + |Note: + | - When importing, you can avoid naming conflicts by renaming: + | ${hl("import")} scala.{$name => ${name.show}Tick} |""" } diff --git a/tests/neg/ambiref.check b/tests/neg/ambiref.check index 95b542c7aae3..5d701b3b3b71 100644 --- a/tests/neg/ambiref.check +++ b/tests/neg/ambiref.check @@ -1,32 +1,32 @@ -- [E049] Reference Error: tests/neg/ambiref.scala:8:14 ---------------------------------------------------------------- 8 | println(x) // error | ^ - | Reference to x is ambiguous, - | it is both defined in object Test + | Reference to x is ambiguous. + | It is both defined in object Test | and inherited subsequently in class D | | longer explanation available when compiling with `-explain` -- [E049] Reference Error: tests/neg/ambiref.scala:10:14 --------------------------------------------------------------- 10 | println(x) // error | ^ - | Reference to x is ambiguous, - | it is both defined in object Test + | Reference to x is ambiguous. + | It is both defined in object Test | and inherited subsequently in anonymous class test1.C {...} | | longer explanation available when compiling with `-explain` -- [E049] Reference Error: tests/neg/ambiref.scala:17:14 --------------------------------------------------------------- 17 | println(y) // error | ^ - | Reference to y is ambiguous, - | it is both defined in method c + | Reference to y is ambiguous. + | It is both defined in method c | and inherited subsequently in anonymous class D {...} | | longer explanation available when compiling with `-explain` -- [E049] Reference Error: tests/neg/ambiref.scala:25:16 --------------------------------------------------------------- 25 | println(y) // error | ^ - | Reference to y is ambiguous, - | it is both defined in method c + | Reference to y is ambiguous. + | It is both defined in method c | and inherited subsequently in class E | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i12682.check b/tests/neg/i12682.check new file mode 100644 index 000000000000..3b421b44ebe5 --- /dev/null +++ b/tests/neg/i12682.check @@ -0,0 +1,45 @@ +-- [E049] Reference Error: tests/neg/i12682.scala:6:12 ----------------------------------------------------------------- +6 | val x = m(1) // error + | ^ + | Reference to m is ambiguous. + | It is both defined in object C + | and inherited subsequently in object T + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | The identifier m is ambiguous because a binding of lower precedence + | in an inner scope cannot shadow a binding with higher precedence in + | an outer scope. + | + | The precedence of the different kinds of bindings, from highest to lowest, is: + | - Definitions in an enclosing scope + | - Inherited definitions and top-level definitions in packages + | - Names introduced by imports + | - Named imports take precedence over wildcard imports + | - Definitions from packages in other files + | Note: + | - When importing, you can avoid naming conflicts by renaming: + | import scala.{m => mTick} + --------------------------------------------------------------------------------------------------------------------- +-- [E049] Reference Error: tests/neg/i12682.scala:13:10 ---------------------------------------------------------------- +13 | def d = m(42) // error + | ^ + | Reference to m is ambiguous. + | It is both imported by import X._ + | and imported subsequently by import Y._ + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | The identifier m is ambiguous because two bindings of equal precedence + | were introduced in the same scope. + | + | The precedence of the different kinds of bindings, from highest to lowest, is: + | - Definitions in an enclosing scope + | - Inherited definitions and top-level definitions in packages + | - Names introduced by imports + | - Named imports take precedence over wildcard imports + | - Definitions from packages in other files + | Note: + | - When importing, you can avoid naming conflicts by renaming: + | import scala.{m => mTick} + -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i12682.scala b/tests/neg/i12682.scala new file mode 100644 index 000000000000..0b37816ef0df --- /dev/null +++ b/tests/neg/i12682.scala @@ -0,0 +1,13 @@ +// scalac: -explain + +object C: + def m(x: Int) = 1 + object T extends K: + val x = m(1) // error +class K: + def m(i: Int) = 2 +object X extends K +object Y extends K +object D: + import X.*, Y.* + def d = m(42) // error diff --git a/tests/neg/i13558.check b/tests/neg/i13558.check index ab10a42cdd32..7b4b5215a0a3 100644 --- a/tests/neg/i13558.check +++ b/tests/neg/i13558.check @@ -8,8 +8,8 @@ | | failed with: | - | Reference to id is ambiguous, - | it is both imported by import testcode.ExtensionB._ + | Reference to id is ambiguous. + | It is both imported by import testcode.ExtensionB._ | and imported subsequently by import testcode.ExtensionA._ -- [E008] Not Found Error: tests/neg/i13558.scala:29:14 ---------------------------------------------------------------- 29 | println(a.id) // error @@ -21,6 +21,6 @@ | | failed with: | - | Reference to id is ambiguous, - | it is both imported by import testcode.ExtensionA._ + | Reference to id is ambiguous. + | It is both imported by import testcode.ExtensionA._ | and imported subsequently by import testcode.ExtensionB._ diff --git a/tests/neg/i9803.check b/tests/neg/i9803.check index cc7d56d585b0..20225f1f5bc5 100644 --- a/tests/neg/i9803.check +++ b/tests/neg/i9803.check @@ -1,8 +1,8 @@ -- [E049] Reference Error: tests/neg/i9803.scala:15:10 ----------------------------------------------------------------- 15 | println(f421()) // error | ^^^^ - | Reference to f421 is ambiguous, - | it is both imported by name by import bugs.shadowing.x.f421 + | Reference to f421 is ambiguous. + | It is both imported by name by import bugs.shadowing.x.f421 | and imported by name subsequently by import bugs.shadowing.y.f421 | | longer explanation available when compiling with `-explain` From 363996c3cafab13f7ef3988aee0a6be69a1a9f19 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 18 Feb 2023 10:07:07 -0800 Subject: [PATCH 174/657] Retain and expand new rule help, add more words --- .../dotty/tools/dotc/reporting/messages.scala | 13 ++++++----- tests/neg/i12682.check | 22 ++++++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 92ee1b44def0..a82e49a048ab 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1334,21 +1334,24 @@ class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec def explain(using Context) = val precedent = - if newPrec == prevPrec then """two bindings of equal precedence + if newPrec == prevPrec then """two name bindings of equal precedence |were introduced in the same scope.""".stripMargin - else """a binding of lower precedence + else """a name binding of lower precedence |in an inner scope cannot shadow a binding with higher precedence in |an outer scope.""".stripMargin i"""|The identifier $name is ambiguous because $precedent | - |The precedence of the different kinds of bindings, from highest to lowest, is: + |The precedence of the different kinds of name bindings, from highest to lowest, is: | - Definitions in an enclosing scope | - Inherited definitions and top-level definitions in packages - | - Names introduced by imports - | - Named imports take precedence over wildcard imports + | - Names introduced by import of a specific name + | - Names introduced by wildcard import | - Definitions from packages in other files |Note: + | - As a rule, definitions take precedence over imports. + | - Definitions in an enclosing scope take precedence over inherited definitions, + | which can result in ambiguities in nested classes. | - When importing, you can avoid naming conflicts by renaming: | ${hl("import")} scala.{$name => ${name.show}Tick} |""" diff --git a/tests/neg/i12682.check b/tests/neg/i12682.check index 3b421b44ebe5..605414938529 100644 --- a/tests/neg/i12682.check +++ b/tests/neg/i12682.check @@ -7,17 +7,20 @@ |--------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | The identifier m is ambiguous because a binding of lower precedence + | The identifier m is ambiguous because a name binding of lower precedence | in an inner scope cannot shadow a binding with higher precedence in | an outer scope. | - | The precedence of the different kinds of bindings, from highest to lowest, is: + | The precedence of the different kinds of name bindings, from highest to lowest, is: | - Definitions in an enclosing scope | - Inherited definitions and top-level definitions in packages - | - Names introduced by imports - | - Named imports take precedence over wildcard imports + | - Names introduced by import of a specific name + | - Names introduced by wildcard import | - Definitions from packages in other files | Note: + | - As a rule, definitions take precedence over imports. + | - Definitions in an enclosing scope take precedence over inherited definitions, + | which can result in ambiguities in nested classes. | - When importing, you can avoid naming conflicts by renaming: | import scala.{m => mTick} --------------------------------------------------------------------------------------------------------------------- @@ -30,16 +33,19 @@ |-------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | The identifier m is ambiguous because two bindings of equal precedence + | The identifier m is ambiguous because two name bindings of equal precedence | were introduced in the same scope. | - | The precedence of the different kinds of bindings, from highest to lowest, is: + | The precedence of the different kinds of name bindings, from highest to lowest, is: | - Definitions in an enclosing scope | - Inherited definitions and top-level definitions in packages - | - Names introduced by imports - | - Named imports take precedence over wildcard imports + | - Names introduced by import of a specific name + | - Names introduced by wildcard import | - Definitions from packages in other files | Note: + | - As a rule, definitions take precedence over imports. + | - Definitions in an enclosing scope take precedence over inherited definitions, + | which can result in ambiguities in nested classes. | - When importing, you can avoid naming conflicts by renaming: | import scala.{m => mTick} -------------------------------------------------------------------------------------------------------------------- From b50910452d6f77ce32acbb913c3f131e432c336e Mon Sep 17 00:00:00 2001 From: Paul Coral Date: Sun, 19 Feb 2023 12:35:23 +0100 Subject: [PATCH 175/657] Improve override detection in CheckUnused - CheckUnused detects override from base type in addition of `override` flag - Update test suit --- .../tools/dotc/transform/CheckUnused.scala | 19 +++++++++++++------ .../fatal-warnings/i15503e.scala | 14 +++++++++++++- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index a1ccccdb12e2..49ce64b00b88 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -574,12 +574,14 @@ object CheckUnused: private def shouldNotReportParamOwner(using Context): Boolean = if sym.exists then val owner = sym.owner - trivialDefs(owner) || - owner.is(Flags.Override) || + trivialDefs(owner) || // is a trivial def owner.isPrimaryConstructor || - owner.annotations.exists ( + owner.annotations.exists ( // @depreacated _.symbol == ctx.definitions.DeprecatedAnnot - ) + ) || + owner.isAllOf(Synthetic | PrivateLocal) || + owner.is(Accessor) || + owner.isOverriden else false @@ -589,6 +591,11 @@ object CheckUnused: private def everySymbol(using Context): List[Symbol] = List(sym, sym.companionClass, sym.companionModule, sym.moduleClass).filter(_.exists) + /** A function is overriden. Either has `override flags` or parent has a matching member (type and name) */ + private def isOverriden(using Context): Boolean = + sym.is(Flags.Override) || + (if sym.exists then sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists) else false) + end extension extension (defdef: tpd.DefDef) @@ -620,8 +627,8 @@ object CheckUnused: val sym = memDef.symbol (sym.is(Param) || sym.isAllOf(PrivateParamAccessor | Local, butNot = CaseAccessor)) && !isSyntheticMainParam(sym) && - !sym.shouldNotReportParamOwner && - (!sym.exists || !(sym.owner.isAllOf(Synthetic | PrivateLocal) || sym.owner.is(Accessor))) + !sym.shouldNotReportParamOwner + private def shouldReportPrivateDef(using Context): Boolean = currScopeType.top == ScopeType.Template && !memDef.symbol.isConstructor && memDef.symbol.is(Private, butNot = SelfName | Synthetic | CaseAccessor) diff --git a/tests/neg-custom-args/fatal-warnings/i15503e.scala b/tests/neg-custom-args/fatal-warnings/i15503e.scala index cd56587327cd..56aec702a39e 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503e.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503e.scala @@ -54,4 +54,16 @@ package foo.test.trivial: object Y package foo.test.i16955: - class S(var r: String) // OK \ No newline at end of file + class S(var r: String) // OK + +package foo.test.i16865: + trait Foo: + def fn(a: Int, b: Int): Int // OK + trait Bar extends Foo + + object Ex extends Bar: + def fn(a: Int, b: Int): Int = b + 3 // OK + + object Ex2 extends Bar: + override def fn(a: Int, b: Int): Int = b + 3 // OK + From c5038b7d18d316c7e30e112a74c6054255f37336 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 20 Feb 2023 14:25:28 +0100 Subject: [PATCH 176/657] Fix race condition in new LazyVals --- library/src/scala/runtime/LazyVals.scala | 2 +- tests/run/i16806.check | 2 ++ tests/run/i16806.scala | 42 ++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/run/i16806.check create mode 100644 tests/run/i16806.scala diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index 5d1e8e74b89d..416ffc91d34a 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -45,7 +45,7 @@ object LazyVals { /* ------------- Start of public API ------------- */ - sealed trait LazyValControlState + sealed trait LazyValControlState extends Serializable /** * Used to indicate the state of a lazy val that is being diff --git a/tests/run/i16806.check b/tests/run/i16806.check new file mode 100644 index 000000000000..af917347162a --- /dev/null +++ b/tests/run/i16806.check @@ -0,0 +1,2 @@ +Success +Success \ No newline at end of file diff --git a/tests/run/i16806.scala b/tests/run/i16806.scala new file mode 100644 index 000000000000..f45652080458 --- /dev/null +++ b/tests/run/i16806.scala @@ -0,0 +1,42 @@ +import java.util.concurrent.Semaphore +import scala.runtime.LazyVals.Evaluating + +object Repro { + + case object DFBit + final class DFError extends Exception("") + final class DFType[+T](val value: T | DFError) extends AnyVal + + def asIR(dfType: DFType[DFBit.type]): DFBit.type = dfType.value match + case dfTypeIR: DFBit.type => dfTypeIR + case err: DFError => throw new DFError + + object Holder { + val s = new Semaphore(1, false) + final lazy val Bit = { + s.release() + new DFType[DFBit.type](DFBit) + } + } + + @main + def Test = + val a = new Thread() { + override def run(): Unit = + Holder.s.acquire() + val x = Holder.Bit.value + assert(!x.isInstanceOf[Evaluating.type]) + println("Success") + } + val b = new Thread() { + override def run(): Unit = + Holder.s.acquire() + val x = Holder.Bit.value + assert(!x.isInstanceOf[Evaluating.type]) + println("Success") + } + a.start() + b.start() + a.join(300) + b.join(300) +} \ No newline at end of file From fd3d0e69f3176167ceab1e7afc79ddacf887fe74 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 20 Feb 2023 14:54:23 +0100 Subject: [PATCH 177/657] Add MiMA exceptions --- project/MiMaFilters.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 8af2de7058ee..49e18db3fccb 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -26,6 +26,11 @@ object MiMaFilters { ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.into"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$into$"), // end of New experimental features in 3.3.X + + // Added java.io.Serializable as LazyValControlState supertype + ProblemFilters.exclude[MissingTypesProblem]("scala.runtime.LazyVals$LazyValControlState"), + ProblemFilters.exclude[MissingTypesProblem]("scala.runtime.LazyVals$Waiting"), + ) val TastyCore: Seq[ProblemFilter] = Seq( ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.TastyBuffer.reset"), From 3dd600de4cafa9926d4b17bba1050f1ce5196ade Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Mon, 21 Nov 2022 04:52:46 +0100 Subject: [PATCH 178/657] Port -Wnonunit-statement setting for dotty --- .../tools/dotc/config/ScalaSettings.scala | 1 + .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 6 + .../dotty/tools/dotc/typer/RefChecks.scala | 3 +- .../src/dotty/tools/dotc/typer/Typer.scala | 56 ++++- .../fatal-warnings/nonunit-statement.scala | 198 ++++++++++++++++++ 6 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/nonunit-statement.scala diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 5ae99ec7e6fa..8606d72546ef 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -160,6 +160,7 @@ private sealed trait WarningSettings: val Whelp: Setting[Boolean] = BooleanSetting("-W", "Print a synopsis of warning options.") val XfatalWarnings: Setting[Boolean] = BooleanSetting("-Werror", "Fail the compilation if there are any warnings.", aliases = List("-Xfatal-warnings")) val WvalueDiscard: Setting[Boolean] = BooleanSetting("-Wvalue-discard", "Warn when non-Unit expression results are unused.") + val WNonUnitStatement = BooleanSetting("-Wnonunit-statement", "Warn when block statements are non-Unit expressions.") val Wunused: Setting[List[ChoiceWithHelp[String]]] = MultiChoiceHelpSetting( name = "-Wunused", diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 9f0d71645833..b72ec2a19d76 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -189,6 +189,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case CannotBeAccessedID // errorNumber 173 case InlineGivenShouldNotBeFunctionID // errorNumber 174 case ValueDiscardingID // errorNumber 175 + case UnusedNonUnitValueID // errorNumber 176 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index f41d34b8c17c..5f42ac3b1452 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2806,3 +2806,9 @@ class ValueDiscarding(tp: Type)(using Context) def kind = MessageKind.PotentialIssue def msg(using Context) = i"discarded non-Unit value of type $tp" def explain(using Context) = "" + +class UnusedNonUnitValue(tp: Type)(using Context) + extends Message(UnusedNonUnitValueID): + def kind = MessageKind.PotentialIssue + def msg(using Context) = i"unused value of type $tp" + def explain(using Context) = "" diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 3d53371e603e..4ca00ce6366f 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1163,8 +1163,7 @@ class RefChecks extends MiniPhase { thisPhase => checkAllOverrides(cls) checkImplicitNotFoundAnnotation.template(cls.classDenot) tree - } - catch { + } catch { case ex: TypeError => report.error(ex, tree.srcPos) tree diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 5752409c51c1..be4ebc11de26 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1102,6 +1102,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val (stats1, exprCtx) = withoutMode(Mode.Pattern) { typedBlockStats(tree.stats) } + var expr1 = typedExpr(tree.expr, pt.dropIfProto)(using exprCtx) // If unsafe nulls is enabled inside a block but not enabled outside @@ -3128,7 +3129,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer traverse(xtree :: rest) case stat :: rest => val stat1 = typed(stat)(using ctx.exprContext(stat, exprOwner)) - checkStatementPurity(stat1)(stat, exprOwner) + if !checkInterestingResultInStatement(stat1) then checkStatementPurity(stat1)(stat, exprOwner) buf += stat1 traverse(rest)(using stat1.nullableContext) case nil => @@ -4212,6 +4213,59 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer typedExpr(cmp, defn.BooleanType) case _ => + private def checkInterestingResultInStatement(t: Tree)(using Context): Boolean = { + def isUninterestingSymbol(sym: Symbol): Boolean = + sym == NoSymbol || + sym.isConstructor || + sym.is(Package) || + sym.isPackageObject || + sym == defn.BoxedUnitClass || + sym == defn.AnyClass || + sym == defn.AnyRefAlias || + sym == defn.AnyValClass + def isUninterestingType(tpe: Type): Boolean = + tpe == NoType || + tpe.typeSymbol == defn.UnitClass || + defn.isBottomClass(tpe.typeSymbol) || + tpe =:= defn.UnitType || + tpe.typeSymbol == defn.BoxedUnitClass || + tpe =:= defn.AnyValType || + tpe =:= defn.AnyType || + tpe =:= defn.AnyRefType + def isJavaApplication(t: Tree): Boolean = t match { + case Apply(f, _) => f.symbol.is(JavaDefined) && !defn.ObjectClass.isSubClass(f.symbol.owner) + case _ => false + } + def checkInterestingShapes(t: Tree): Boolean = t match { + case If(_, thenpart, elsepart) => checkInterestingShapes(thenpart) || checkInterestingShapes(elsepart) + case Block(_, res) => checkInterestingShapes(res) + case Match(_, cases) => cases.exists(k => checkInterestingShapes(k.body)) + case _ => checksForInterestingResult(t) + } + def checksForInterestingResult(t: Tree): Boolean = ( + !t.isDef // ignore defs + && !isUninterestingSymbol(t.symbol) // ctors, package, Unit, Any + && !isUninterestingType(t.tpe) // bottom types, Unit, Any + && !isThisTypeResult(t) // buf += x + && !isSuperConstrCall(t) // just a thing + && !isJavaApplication(t) // Java methods are inherently side-effecting + // && !treeInfo.hasExplicitUnit(t) // suppressed by explicit expr: Unit // TODO Should explicit `: Unit` be added as warning suppression? + ) + if ctx.settings.WNonUnitStatement.value && !ctx.isAfterTyper && checkInterestingShapes(t) then + val where = t match { + case Block(_, res) => res + case If(_, thenpart, Literal(Constant(()))) => + thenpart match { + case Block(_, res) => res + case _ => thenpart + } + case _ => t + } + report.warning(UnusedNonUnitValue(where.tpe), t.srcPos) + true + else false + } + private def checkStatementPurity(tree: tpd.Tree)(original: untpd.Tree, exprOwner: Symbol)(using Context): Unit = if !tree.tpe.isErroneous && !ctx.isAfterTyper diff --git a/tests/neg-custom-args/fatal-warnings/nonunit-statement.scala b/tests/neg-custom-args/fatal-warnings/nonunit-statement.scala new file mode 100644 index 000000000000..399d132edfae --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/nonunit-statement.scala @@ -0,0 +1,198 @@ +// scalac: -Wnonunit-statement -Wvalue-discard +import collection.ArrayOps +import collection.mutable.{ArrayBuilder, LinkedHashSet, ListBuffer} +import concurrent._ +import scala.reflect.ClassTag + +class C { + import ExecutionContext.Implicits._ + def c = { + def improved = Future(42) + def stale = Future(27) + improved // error + stale + } +} +class D { + def d = { + class E + new E().toString // error + new E().toString * 2 + } +} +class F { + import ExecutionContext.Implicits._ + Future(42) // error +} +// unused template expression uses synthetic method of class +case class K(s: String) { + copy() // error +} +// mutations returning this are ok +class Mutate { + val b = ListBuffer.empty[Int] + b += 42 // nowarn, returns this.type + val xs = List(42) + 27 +: xs // error + + def f(x: Int): this.type = this + def g(): Unit = f(42) // nowarn +} +// some uninteresting expressions may warn for other reasons +class WhoCares { + null // error for purity + ??? // nowarn for impurity +} +// explicit Unit ascription to opt out of warning, even for funky applies +class Absolution { + def f(i: Int): Int = i+1 + import ExecutionContext.Implicits._ + // Future(42): Unit // nowarn { F(42)(ctx) }: Unit where annot is on F(42) + // f(42): Unit // nowarn +} +// warn uni-branched unless user disables it with -Wnonunit-if:false +class Boxed[A](a: A) { + def isEmpty = false + def foreach[U](f: A => U): Unit = + if (!isEmpty) f(a) // error (if) + def forall(f: A => Boolean): Unit = + if (!isEmpty) { + println(".") + f(a) // error (if) + } + def take(p: A => Boolean): Option[A] = { + while (isEmpty || !p(a)) () + Some(a).filter(p) + } +} +class Unibranch[A, B] { + def runWith[U](action: B => U): A => Boolean = { x => + val z = null.asInstanceOf[B] + val fellback = false + if (!fellback) action(z) // error (if) + !fellback + } + def f(i: Int): Int = { + def g = 17 + if (i < 42) { + g // error block statement + println("uh oh") + g // error (if) + } + while (i < 42) { + g // error + println("uh oh") + g // error + } + 42 + } +} +class Dibranch { + def i: Int = ??? + def j: Int = ??? + def f(b: Boolean): Int = { + // if-expr might have an uninteresting LUB + if (b) { // error, at least one branch looks interesting + println("true") + i + } + else { + println("false") + j + } + 42 + } +} +class Next[A] { + val all = ListBuffer.empty[A] + def f(it: Iterator[A], g: A => A): Unit = + while (it.hasNext) + all += g(it.next()) // nowarn +} +class Setting[A] { + def set = LinkedHashSet.empty[A] + def f(a: A): Unit = { + set += a // error because cannot know whether the `set` was supposed to be consumed or assigned + println(set) + } +} +// neither StringBuilder warns, because either append is Java method or returns this.type +// while loop looks like if branch with block1(block2, jump to label), where block2 typed as non-unit +class Strung { + def iterator = Iterator.empty[String] + def addString(b: StringBuilder, start: String, sep: String, end: String): StringBuilder = { + val jsb = b.underlying + if (start.length != 0) jsb.append(start) // error (value-discard) + val it = iterator + if (it.hasNext) { + jsb.append(it.next()) + while (it.hasNext) { + jsb.append(sep) // nowarn (java) + jsb.append(it.next()) // error (value-discard) + } + } + if (end.length != 0) jsb.append(end) // error (value-discard) + b + } + def f(b: java.lang.StringBuilder, it: Iterator[String]): String = { + while (it.hasNext) { + b.append("\n") // nowarn (java) + b.append(it.next()) // error (value-discard) + } + b.toString + } + def g(b: java.lang.StringBuilder, it: Iterator[String]): String = { + while (it.hasNext) it.next() // error + b.toString + } +} +class J { + import java.util.Collections + def xs: java.util.List[Int] = ??? + def f(): Int = { + Collections.checkedList[Int](xs, classOf[Int]) + 42 + } +} +class Variant { + var bs = ListBuffer.empty[Int] + val xs = ListBuffer.empty[Int] + private[this] val ys = ListBuffer.empty[Int] + private[this] var zs = ListBuffer.empty[Int] + def f(i: Int): Unit = { + bs.addOne(i) + xs.addOne(i) + ys.addOne(i) + zs.addOne(i) + println("done") + } +} +final class ArrayOops[A](private val xs: Array[A]) extends AnyVal { + def other: ArrayOps[A] = ??? + def transpose[B](implicit asArray: A => Array[B]): Array[Array[B]] = { + val aClass = xs.getClass.getComponentType + val bb = new ArrayBuilder.ofRef[Array[B]]()(ClassTag[Array[B]](aClass)) + if (xs.length == 0) bb.result() + else { + def mkRowBuilder() = ArrayBuilder.make[B](ClassTag[B](aClass.getComponentType)) + val bs = new ArrayOps(asArray(xs(0))).map((x: B) => mkRowBuilder()) + for (xs <- other) { + var i = 0 + for (x <- new ArrayOps(asArray(xs))) { + bs(i) += x + i += 1 + } + } + for (b <- new ArrayOps(bs)) bb += b.result() + bb.result() + } + } +} +class Depends { + def f[A](a: A): a.type = a + def g() = { + val d = new Depends + f(d) + () + } +} From 0ce5c32b7bf0e8ad8768011de71329be73ab6d3f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 14 Feb 2023 10:36:21 +0100 Subject: [PATCH 179/657] Fix HK quoted pattern type variables The issue was in the encoding into `{ExprMatchModule,TypeMatchModule}.unapply`. Specifically with the `TypeBindings` argument. This arguments holds the list of type variable definitions (`tpd.Bind` trees). We used a `Tuple` to list all the types inside. The problem is that higher-kinded type variables do not conform with the upper bounds of the tuple elements. The solution is to use an HList with any-kinded elements. --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 8 ++++++-- .../src/dotty/tools/dotc/core/Definitions.scala | 3 +++ .../tools/dotc/typer/QuotesAndSplices.scala | 4 ++-- .../quoted/runtime/impl/QuoteMatcher.scala | 4 ++-- .../scala/quoted/runtime/impl/QuotesImpl.scala | 4 ++-- .../scala/quoted/runtime/QuoteMatching.scala | 11 ++++++++--- project/MiMaFilters.scala | 1 + .../hk-quoted-type-patterns/Macro_1.scala | 17 +++++++++++++++++ .../hk-quoted-type-patterns/Test_2.scala | 5 +++++ 9 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 tests/pos-macros/hk-quoted-type-patterns/Macro_1.scala create mode 100644 tests/pos-macros/hk-quoted-type-patterns/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 1a202ece1e66..56cfbefabbfe 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -1502,7 +1502,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } - /** Creates the tuple type tree repesentation of the type trees in `ts` */ + /** Creates the tuple type tree representation of the type trees in `ts` */ def tupleTypeTree(elems: List[Tree])(using Context): Tree = { val arity = elems.length if arity <= Definitions.MaxTupleArity then @@ -1513,10 +1513,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { else nestedPairsTypeTree(elems) } - /** Creates the nested pairs type tree repesentation of the type trees in `ts` */ + /** Creates the nested pairs type tree representation of the type trees in `ts` */ def nestedPairsTypeTree(ts: List[Tree])(using Context): Tree = ts.foldRight[Tree](TypeTree(defn.EmptyTupleModule.termRef))((x, acc) => AppliedTypeTree(TypeTree(defn.PairClass.typeRef), x :: acc :: Nil)) + /** Creates the nested higher-kinded pairs type tree representation of the type trees in `ts` */ + def hkNestedPairsTypeTree(ts: List[Tree])(using Context): Tree = + ts.foldRight[Tree](TypeTree(defn.QuoteMatching_KNil.typeRef))((x, acc) => AppliedTypeTree(TypeTree(defn.QuoteMatching_KCons.typeRef), x :: acc :: Nil)) + /** Replaces all positions in `tree` with zero-extent positions */ private def focusPositions(tree: Tree)(using Context): Tree = { val transformer = new tpd.TreeMap { diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 854608143df9..74d210a4bbad 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -865,6 +865,9 @@ class Definitions { @tu lazy val QuoteMatching_ExprMatchModule: Symbol = QuoteMatchingClass.requiredClass("ExprMatchModule") @tu lazy val QuoteMatching_TypeMatch: Symbol = QuoteMatchingClass.requiredMethod("TypeMatch") @tu lazy val QuoteMatching_TypeMatchModule: Symbol = QuoteMatchingClass.requiredClass("TypeMatchModule") + @tu lazy val QuoteMatchingModule: Symbol = requiredModule("scala.quoted.runtime.QuoteMatching") + @tu lazy val QuoteMatching_KNil: Symbol = QuoteMatchingModule.requiredType("KNil") + @tu lazy val QuoteMatching_KCons: Symbol = QuoteMatchingModule.requiredType("KCons") @tu lazy val ToExprModule: Symbol = requiredModule("scala.quoted.ToExpr") @tu lazy val ToExprModule_BooleanToExpr: Symbol = ToExprModule.requiredMethod("BooleanToExpr") diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 2fe5770c5b4b..65d8abfdf6a7 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -364,7 +364,7 @@ trait QuotesAndSplices { * * ``` * case scala.internal.quoted.Expr.unapply[ - * Tuple1[t @ _], // Type binging definition + * KList[t @ _, KNil], // Type binging definition * Tuple2[Type[t], Expr[List[t]]] // Typing the result of the pattern match * ]( * Tuple2.unapply @@ -411,7 +411,7 @@ trait QuotesAndSplices { val replaceBindings = new ReplaceBindings val patType = defn.tupleType(splices.tpes.map(tpe => replaceBindings(tpe.widen))) - val typeBindingsTuple = tpd.tupleTypeTree(typeBindings.values.toList) + val typeBindingsTuple = tpd.hkNestedPairsTypeTree(typeBindings.values.toList) val replaceBindingsInTree = new TreeMap { private var bindMap = Map.empty[Symbol, Symbol] diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index d85d92de5455..7c952dbbe142 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -121,9 +121,9 @@ object QuoteMatcher { private def withEnv[T](env: Env)(body: Env ?=> T): T = body(using env) - def treeMatch(scrutineeTerm: Tree, patternTerm: Tree)(using Context): Option[Tuple] = + def treeMatch(scrutineeTree: Tree, patternTree: Tree)(using Context): Option[Tuple] = given Env = Map.empty - scrutineeTerm =?= patternTerm + scrutineeTree =?= patternTree /** Check that all trees match with `mtch` and concatenate the results with &&& */ private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => Matching): Matching = (l1, l2) match { diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 9ccec4683747..df118793a2f3 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -3092,14 +3092,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler new TypeImpl(tree, SpliceScope.getCurrent).asInstanceOf[scala.quoted.Type[T]] object ExprMatch extends ExprMatchModule: - def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] = + def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] = val scrutineeTree = reflect.asTerm(scrutinee) val patternTree = reflect.asTerm(pattern) treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]] end ExprMatch object TypeMatch extends TypeMatchModule: - def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: scala.quoted.Type[?])(using pattern: scala.quoted.Type[?]): Option[Tup] = + def unapply[TypeBindings, Tup <: Tuple](scrutinee: scala.quoted.Type[?])(using pattern: scala.quoted.Type[?]): Option[Tup] = val scrutineeTree = reflect.TypeTree.of(using scrutinee) val patternTree = reflect.TypeTree.of(using pattern) treeMatch(scrutineeTree, patternTree).asInstanceOf[Option[Tup]] diff --git a/library/src/scala/quoted/runtime/QuoteMatching.scala b/library/src/scala/quoted/runtime/QuoteMatching.scala index 2a76143e9868..c95ffe87b5dc 100644 --- a/library/src/scala/quoted/runtime/QuoteMatching.scala +++ b/library/src/scala/quoted/runtime/QuoteMatching.scala @@ -17,7 +17,7 @@ trait QuoteMatching: * - `ExprMatch.unapply('{ f(0, myInt) })('{ f(patternHole[Int], patternHole[Int]) }, _)` * will return `Some(Tuple2('{0}, '{ myInt }))` * - `ExprMatch.unapply('{ f(0, "abc") })('{ f(0, patternHole[Int]) }, _)` - * will return `None` due to the missmatch of types in the hole + * will return `None` due to the mismatch of types in the hole * * Holes: * - scala.quoted.runtime.Patterns.patternHole[T]: hole that matches an expression `x` of type `Expr[U]` @@ -27,7 +27,7 @@ trait QuoteMatching: * @param pattern `Expr[Any]` containing the pattern tree * @return None if it did not match, `Some(tup)` if it matched where `tup` contains `Expr[Ti]`` */ - def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: Expr[Any])(using pattern: Expr[Any]): Option[Tup] + def unapply[TypeBindings, Tup <: Tuple](scrutinee: Expr[Any])(using pattern: Expr[Any]): Option[Tup] } val TypeMatch: TypeMatchModule @@ -40,5 +40,10 @@ trait QuoteMatching: * @param pattern `Type[?]` containing the pattern tree * @return None if it did not match, `Some(tup)` if it matched where `tup` contains `Type[Ti]`` */ - def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: Type[?])(using pattern: Type[?]): Option[Tup] + def unapply[TypeBindings, Tup <: Tuple](scrutinee: Type[?])(using pattern: Type[?]): Option[Tup] } + +object QuoteMatching: + type KList + type KCons[+H <: AnyKind, +T <: KList] <: KList + type KNil <: KList diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 8af2de7058ee..cf344fe8ca33 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -16,6 +16,7 @@ object MiMaFilters { ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$"), ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Break"), ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Label"), + ProblemFilters.exclude[MissingClassProblem]("scala.quoted.runtime.QuoteMatching$"), // Scala.js only: new runtime support class in 3.2.3; not available to users ProblemFilters.exclude[MissingClassProblem]("scala.scalajs.runtime.AnonFunctionXXL"), diff --git a/tests/pos-macros/hk-quoted-type-patterns/Macro_1.scala b/tests/pos-macros/hk-quoted-type-patterns/Macro_1.scala new file mode 100644 index 000000000000..0d2df1504918 --- /dev/null +++ b/tests/pos-macros/hk-quoted-type-patterns/Macro_1.scala @@ -0,0 +1,17 @@ +import scala.quoted._ + +private def impl(x: Expr[Any])(using Quotes): Expr[Unit] = { + x match + case '{ foo[x] } => + assert(Type.show[x] == "scala.Int", Type.show[x]) + case '{ type f[X]; foo[`f`] } => + assert(Type.show[f] == "[A >: scala.Nothing <: scala.Any] => scala.collection.immutable.List[A]", Type.show[f]) + case '{ type f <: AnyKind; foo[`f`] } => + assert(Type.show[f] == "[K >: scala.Nothing <: scala.Any, V >: scala.Nothing <: scala.Any] => scala.collection.immutable.Map[K, V]", Type.show[f]) + case x => throw MatchError(x.show) + '{} +} + +inline def test(inline x: Any): Unit = ${ impl('x) } + +def foo[T <: AnyKind]: Any = ??? diff --git a/tests/pos-macros/hk-quoted-type-patterns/Test_2.scala b/tests/pos-macros/hk-quoted-type-patterns/Test_2.scala new file mode 100644 index 000000000000..3cb9113f2452 --- /dev/null +++ b/tests/pos-macros/hk-quoted-type-patterns/Test_2.scala @@ -0,0 +1,5 @@ +@main +def Test = + test(foo[Int]) + test(foo[List]) + test(foo[Map]) From 9344e514246e9cbdf119c94a1871cc4dfc18ad19 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Mon, 20 Feb 2023 15:51:54 +0100 Subject: [PATCH 180/657] Remove usage of JDK version dependent method from neg/i16820 test. `formatted` is a deprecated Scala extension method and a function with a the same was added to String in JDK15 --- tests/neg/i16820.check | 12 ++++-------- tests/neg/i16820.scala | 4 +--- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/tests/neg/i16820.check b/tests/neg/i16820.check index 5f2e82bbf2ef..f51bfafef899 100644 --- a/tests/neg/i16820.check +++ b/tests/neg/i16820.check @@ -8,15 +8,11 @@ | method g in object Test must be called with () argument | | longer explanation available when compiling with `-explain` --- Error: tests/neg/i16820.scala:8:14 ---------------------------------------------------------------------------------- -8 | val x3 = "".formatted // error - | ^^^^^^^^^^^^ - | missing arguments for method formatted in class String --- Error: tests/neg/i16820.scala:9:40 ---------------------------------------------------------------------------------- -9 | val x4 = java.nio.file.Paths.get(".").toRealPath // error +-- Error: tests/neg/i16820.scala:7:40 ---------------------------------------------------------------------------------- +7 | val x3 = java.nio.file.Paths.get(".").toRealPath // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | missing arguments for method toRealPath in trait Path --- Error: tests/neg/i16820.scala:13:14 --------------------------------------------------------------------------------- -13 |def test = Foo(3) // error +-- Error: tests/neg/i16820.scala:11:14 --------------------------------------------------------------------------------- +11 |def test = Foo(3) // error | ^^^^^^ | missing arguments for method apply in object Foo diff --git a/tests/neg/i16820.scala b/tests/neg/i16820.scala index 7a93f3a5d355..abdc741b9f0e 100644 --- a/tests/neg/i16820.scala +++ b/tests/neg/i16820.scala @@ -4,9 +4,7 @@ object Test: val x1 = f // error val x2 = g // error - - val x3 = "".formatted // error - val x4 = java.nio.file.Paths.get(".").toRealPath // error + val x3 = java.nio.file.Paths.get(".").toRealPath // error // #14567 case class Foo(x: Int)(xs: String*) From 4ee457469b30b39f1e650b4a382a585033f4f385 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 20 Feb 2023 16:51:05 +0100 Subject: [PATCH 181/657] Do not depend on runtime lib in tests --- tests/run/i16806.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/run/i16806.scala b/tests/run/i16806.scala index f45652080458..0b0dfe1f6b35 100644 --- a/tests/run/i16806.scala +++ b/tests/run/i16806.scala @@ -1,5 +1,4 @@ import java.util.concurrent.Semaphore -import scala.runtime.LazyVals.Evaluating object Repro { @@ -25,14 +24,14 @@ object Repro { override def run(): Unit = Holder.s.acquire() val x = Holder.Bit.value - assert(!x.isInstanceOf[Evaluating.type]) + assert(x.isInstanceOf[DFBit.type]) println("Success") } val b = new Thread() { override def run(): Unit = Holder.s.acquire() val x = Holder.Bit.value - assert(!x.isInstanceOf[Evaluating.type]) + assert(x.isInstanceOf[DFBit.type]) println("Success") } a.start() From bc00ea104788715cbf2bfc7854fdb4d3a43d3e37 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 20 Feb 2023 16:00:01 +0000 Subject: [PATCH 182/657] Fix use of accessibleFrom when finding default arg getters --- compiler/src/dotty/tools/dotc/typer/Applications.scala | 2 +- tests/pos/i16814.scala | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i16814.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 36e3618d30f7..a8abd868fdef 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -272,7 +272,7 @@ object Applications { else def selectGetter(qual: Tree): Tree = val getterDenot = qual.tpe.member(getterName) - .accessibleFrom(qual.tpe.widenIfUnstable) // to reset Local + .accessibleFrom(qual.tpe.widenIfUnstable, superAccess = true) // to reset Local if (getterDenot.exists) qual.select(TermRef(qual.tpe, getterName, getterDenot)) else EmptyTree if !meth.isClassConstructor then diff --git a/tests/pos/i16814.scala b/tests/pos/i16814.scala new file mode 100644 index 000000000000..bffbb5788c0b --- /dev/null +++ b/tests/pos/i16814.scala @@ -0,0 +1,2 @@ +class Foo protected (foo: Int = 0) { } +class Bar extends Foo From 4386f3f75a0eb05b523ec205281849c2c93c1222 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 20 Feb 2023 11:15:33 +0100 Subject: [PATCH 183/657] Fix reflect.LambdaType type test Fixes #16961 --- .../src/scala/quoted/runtime/impl/QuotesImpl.scala | 4 ++-- .../src/dotty/tools/scaladoc/tasty/TypesSupport.scala | 4 ++-- tests/pos-macros/i16961/Macro_1.scala | 10 ++++++++++ tests/pos-macros/i16961/Test_2.scala | 1 + 4 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 tests/pos-macros/i16961/Macro_1.scala create mode 100644 tests/pos-macros/i16961/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 965a3e907878..6688d6e81a89 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2167,11 +2167,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end extension end PolyTypeMethods - type TypeLambda = dotc.core.Types.TypeLambda + type TypeLambda = dotc.core.Types.HKTypeLambda object TypeLambdaTypeTest extends TypeTest[TypeRepr, TypeLambda]: def unapply(x: TypeRepr): Option[TypeLambda & x.type] = x match - case tpe: (Types.TypeLambda & x.type) => Some(tpe) + case tpe: (Types.HKTypeLambda & x.type) => Some(tpe) case _ => None end TypeLambdaTypeTest diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala index 0cf2669407c8..c94eda9409b2 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/TypesSupport.scala @@ -290,10 +290,10 @@ trait TypesSupport: } inner(sc) ++ keyword(" match ").l ++ plain("{\n").l ++ casesTexts ++ plain(spaces + "}").l - case ParamRef(TypeLambda(names, _, _), i) => tpe(names.apply(i)).l - case ParamRef(m: MethodType, i) => tpe(m.paramNames(i)).l ++ plain(".type").l + case ParamRef(binder: LambdaType, i) => tpe(binder.paramNames(i)).l + case RecursiveType(tp) => inner(tp) case MatchCase(pattern, rhs) => diff --git a/tests/pos-macros/i16961/Macro_1.scala b/tests/pos-macros/i16961/Macro_1.scala new file mode 100644 index 000000000000..20ec6b439ec8 --- /dev/null +++ b/tests/pos-macros/i16961/Macro_1.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +inline def myMacro = ${ myMacroImpl } + +def myMacroImpl(using Quotes) = + import quotes.reflect.* + + PolyType(List("arg"))(_ => List(TypeBounds.empty), _ => TypeRepr.of[Any]) match + case _: TypeLambda => quotes.reflect.report.errorAndAbort("PolyType should not be a TypeLambda") + case _ => '{ () } // Ok diff --git a/tests/pos-macros/i16961/Test_2.scala b/tests/pos-macros/i16961/Test_2.scala new file mode 100644 index 000000000000..76a9e17659db --- /dev/null +++ b/tests/pos-macros/i16961/Test_2.scala @@ -0,0 +1 @@ +def test = myMacro From 9161b114dc0bfa92d0a72ea78ef674644dce8077 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 21 Feb 2023 19:01:42 +0100 Subject: [PATCH 184/657] Fix caching issue caused by incorrect isProvisional check A static TypeRef can still be provisional if it's currently being completed (see the logic in `Namer#TypeDefCompleter#typeSig`). Fixes #16950. --- compiler/src/dotty/tools/dotc/core/Types.scala | 5 ++--- tests/pos/i16950.scala | 11 +++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i16950.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d4fa6a819dc4..b9f4ce48092b 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -118,10 +118,9 @@ object Types { if t.mightBeProvisional then t.mightBeProvisional = t match case t: TypeRef => - !t.currentSymbol.isStatic && { + t.currentSymbol.isProvisional || !t.currentSymbol.isStatic && { (t: Type).mightBeProvisional = false // break cycles - t.symbol.isProvisional - || test(t.prefix, theAcc) + test(t.prefix, theAcc) || t.denot.infoOrCompleter.match case info: LazyType => true case info: AliasingBounds => test(info.alias, theAcc) diff --git a/tests/pos/i16950.scala b/tests/pos/i16950.scala new file mode 100644 index 000000000000..ac95a477136e --- /dev/null +++ b/tests/pos/i16950.scala @@ -0,0 +1,11 @@ +object Foo: + def bar(x : Bar.YOf[Any]): Unit = ??? + +trait K: + type CType <: Bar.YOf[Any] + def foo : K = + val x : CType = ??? + x // was: error: Found: CType, Expected: K + +object Bar: + type YOf[T] = K { type M } From e0ccff0484187a5fcbfd21cc850294bb83f0fb40 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 15 Jan 2023 17:57:34 +0000 Subject: [PATCH 185/657] Space: Cache simplify --- .../tools/dotc/transform/patmat/Space.scala | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 64e0d9a94a1a..b5cc9a034812 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -66,6 +66,12 @@ sealed trait Space: else trace(s"isSubspace(${show(this)}, ${show(b)})", debug) { isSubspaceCache.getOrElseUpdate(b, computeIsSubspace(this, b)) } + + private var mySimplified: Space = _ + + def simplify(using Context): Space = + if mySimplified == null then mySimplified = SpaceEngine.computeSimplify(this) + mySimplified end Space /** Empty space */ @@ -98,8 +104,13 @@ case class Or(spaces: Seq[Space]) extends Space object SpaceEngine { import tpd._ + def simplify(space: Space)(using Context): Space = space.simplify + def isSubspace(a: Space, b: Space)(using Context): Boolean = a.isSubspace(b) + def canDecompose(typ: Typ)(using Context): Boolean = typ.canDecompose + def decompose(typ: Typ)(using Context): List[Typ] = typ.decompose + /** Simplify space such that a space equal to `Empty` becomes `Empty` */ - def simplify(space: Space)(using Context): Space = trace(s"simplify ${show(space)} --> ", debug, show)(space match { + def computeSimplify(space: Space)(using Context): Space = trace(s"simplify ${show(space)} --> ", debug, show)(space match { case Prod(tp, fun, spaces) => val sps = spaces.mapconserve(simplify) if sps.contains(Empty) then Empty @@ -150,8 +161,6 @@ object SpaceEngine { } /** Is `a` a subspace of `b`? Equivalent to `simplify(simplify(a) - simplify(b)) == Empty`, but faster */ - def isSubspace(a: Space, b: Space)(using Context): Boolean = a.isSubspace(b) - def computeIsSubspace(a: Space, b: Space)(using Context): Boolean = { val a2 = simplify(a) val b2 = simplify(b) @@ -578,9 +587,6 @@ object SpaceEngine { scrutineeTp <:< tp } - def canDecompose(typ: Typ)(using Context): Boolean = typ.canDecompose - def decompose(typ: Typ)(using Context): List[Typ] = typ.decompose - /** Decompose a type into subspaces -- assume the type can be decomposed */ def decompose(tp: Type)(using Context): List[Type] = trace(i"decompose($tp)", debug) { def rec(tp: Type, mixins: List[Type]): List[Type] = tp.dealias match From d58805d2d4999249f552eaa10e70a45f5b3e0136 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Sun, 19 Feb 2023 16:00:16 +0000 Subject: [PATCH 186/657] Space: make code pass -Yexplicit-nulls --- .../tools/dotc/transform/patmat/Space.scala | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b5cc9a034812..3fd3064d2168 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -67,11 +67,15 @@ sealed trait Space: isSubspaceCache.getOrElseUpdate(b, computeIsSubspace(this, b)) } - private var mySimplified: Space = _ + private var mySimplified: Space | Null = _ def simplify(using Context): Space = - if mySimplified == null then mySimplified = SpaceEngine.computeSimplify(this) - mySimplified + val simplified = mySimplified + if simplified == null then + val simplified = SpaceEngine.computeSimplify(this) + mySimplified = simplified + simplified + else simplified end Space /** Empty space */ @@ -84,15 +88,19 @@ case object Empty extends Space * */ case class Typ(tp: Type, decomposed: Boolean = true) extends Space: - private var myDecompose: List[Typ] = _ + private var myDecompose: List[Typ] | Null = _ def canDecompose(using Context): Boolean = decompose != SpaceEngine.ListOfTypNoType def decompose(using Context): List[Typ] = - if myDecompose == null then myDecompose = tp match - case SpaceEngine.Parts(parts) => parts.map(Typ(_, decomposed = true)) - case _ => SpaceEngine.ListOfTypNoType - myDecompose + val decompose = myDecompose + if decompose == null then + val decompose = tp match + case SpaceEngine.Parts(parts) => parts.map(Typ(_, decomposed = true)) + case _ => SpaceEngine.ListOfTypNoType + myDecompose = decompose + decompose + else decompose end Typ /** Space representing an extractor pattern */ From 2a4aef069461e25da1821605f9ddd59df470ce72 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 20 Feb 2023 10:28:41 +0000 Subject: [PATCH 187/657] Space: Fix decomposable prefixes of TermRefs --- compiler/src/dotty/tools/dotc/core/Types.scala | 5 ++++- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d2fc225ff19d..e8cbced423bc 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2182,7 +2182,7 @@ object Types { // --- NamedTypes ------------------------------------------------------------------ - abstract class NamedType extends CachedProxyType, ValueType { self => + abstract class NamedType extends CachedProxyType, ValueType, Product { self => type ThisType >: this.type <: NamedType type ThisName <: Name @@ -2190,6 +2190,8 @@ object Types { val prefix: Type def designator: Designator protected def designator_=(d: Designator): Unit + def _1: Type + def _2: Designator assert(NamedType.validPrefix(prefix), s"invalid prefix $prefix") @@ -2905,6 +2907,7 @@ object Types { def apply(prefix: Type, designator: Name, denot: Denotation)(using Context): NamedType = if (designator.isTermName) TermRef.apply(prefix, designator.asTermName, denot) else TypeRef.apply(prefix, designator.asTypeName, denot) + def unapply(tp: NamedType): NamedType = tp def validPrefix(prefix: Type): Boolean = prefix.isValueType || (prefix eq NoPrefix) } diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 3fd3064d2168..859528e72e89 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -611,11 +611,11 @@ object SpaceEngine { case tp if !TypeComparer.provablyDisjoint(tp, tpB) => AndType(tp, tpB) case OrType(tp1, tp2) => List(tp1, tp2) - case _: SingletonType => ListOfNoType case tp if tp.isRef(defn.BooleanClass) => List(ConstantType(Constant(true)), ConstantType(Constant(false))) case tp if tp.isRef(defn.UnitClass) => ConstantType(Constant(())) :: Nil case tp if tp.classSymbol.isAllOf(JavaEnumTrait) => tp.classSymbol.children.map(_.termRef) - case tp @ TypeRef(Parts(parts), _) => parts.map(tp.derivedSelect) + case tp @ NamedType(Parts(parts), _) => parts.map(tp.derivedSelect) + case _: SingletonType => ListOfNoType case tp @ AppliedType(Parts(parts), targs) if tp.classSymbol.children.isEmpty => // It might not obvious that it's OK to apply the type arguments of a parent type to child types. From fbead6cc5e1da619eac968951aadfb6366c6fd10 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 20 Feb 2023 14:22:39 +0000 Subject: [PATCH 188/657] Space: Fix decomposable, dont recurse on Java enums --- .../src/dotty/tools/dotc/transform/patmat/Space.scala | 3 ++- tests/patmat/java-enum1/ParameterModifier.java | 8 ++++++++ tests/patmat/java-enum1/Test.scala | 6 ++++++ 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/patmat/java-enum1/ParameterModifier.java create mode 100644 tests/patmat/java-enum1/Test.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 859528e72e89..93800df6eeb8 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -613,9 +613,10 @@ object SpaceEngine { case OrType(tp1, tp2) => List(tp1, tp2) case tp if tp.isRef(defn.BooleanClass) => List(ConstantType(Constant(true)), ConstantType(Constant(false))) case tp if tp.isRef(defn.UnitClass) => ConstantType(Constant(())) :: Nil - case tp if tp.classSymbol.isAllOf(JavaEnumTrait) => tp.classSymbol.children.map(_.termRef) case tp @ NamedType(Parts(parts), _) => parts.map(tp.derivedSelect) case _: SingletonType => ListOfNoType + case tp if tp.classSymbol.isAllOf(JavaEnumTrait) => tp.classSymbol.children.map(_.termRef) + // the class of a java enum value is the enum class, so this must follow SingletonType to not loop infinitely case tp @ AppliedType(Parts(parts), targs) if tp.classSymbol.children.isEmpty => // It might not obvious that it's OK to apply the type arguments of a parent type to child types. diff --git a/tests/patmat/java-enum1/ParameterModifier.java b/tests/patmat/java-enum1/ParameterModifier.java new file mode 100644 index 000000000000..c9ddc157ba7e --- /dev/null +++ b/tests/patmat/java-enum1/ParameterModifier.java @@ -0,0 +1,8 @@ +public enum ParameterModifier { + Repeated, + Plain, + ByName; + + private ParameterModifier() { + } +} diff --git a/tests/patmat/java-enum1/Test.scala b/tests/patmat/java-enum1/Test.scala new file mode 100644 index 000000000000..b6ea483d8fb4 --- /dev/null +++ b/tests/patmat/java-enum1/Test.scala @@ -0,0 +1,6 @@ +class Test: + private def showParameterModifier(base: String, pm: ParameterModifier): String = pm match { + case ParameterModifier.Plain => base + case ParameterModifier.Repeated => base + "*" + case ParameterModifier.ByName => "=> " + base + } From 24cc7236edad681d94c951add27554b33219ae53 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 20 Feb 2023 21:16:21 +0000 Subject: [PATCH 189/657] Space: Fix isSubspace invariant by using simplify --- .../src/dotty/tools/dotc/transform/patmat/Space.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 93800df6eeb8..0c1ead0e71cb 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -61,10 +61,14 @@ sealed trait Space: private val isSubspaceCache = mutable.HashMap.empty[Space, Boolean] def isSubspace(b: Space)(using Context): Boolean = - if this == Empty then true + val a = this + val a2 = a.simplify + val b2 = b.simplify + if (a ne a2) || (b ne b2) then a2.isSubspace(b2) + else if a == Empty then true else if b == Empty then false else trace(s"isSubspace(${show(this)}, ${show(b)})", debug) { - isSubspaceCache.getOrElseUpdate(b, computeIsSubspace(this, b)) + isSubspaceCache.getOrElseUpdate(b, computeIsSubspace(a, b)) } private var mySimplified: Space | Null = _ From 1f57accfa3af2b620ec0e39b589afeebe8ebbe3b Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 21 Feb 2023 09:19:15 +0000 Subject: [PATCH 190/657] Space: Make new mutation `@sharable` --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 0c1ead0e71cb..87c8d3760067 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -23,6 +23,7 @@ import reporting._ import config.Printers.{exhaustivity => debug} import util.{SrcPos, NoSourcePosition} +import scala.annotation.internal.sharable import scala.collection.mutable /* Space logic for checking exhaustivity and unreachability of pattern matching @@ -58,7 +59,7 @@ import scala.collection.mutable sealed trait Space: import SpaceEngine.* - private val isSubspaceCache = mutable.HashMap.empty[Space, Boolean] + @sharable private val isSubspaceCache = mutable.HashMap.empty[Space, Boolean] def isSubspace(b: Space)(using Context): Boolean = val a = this @@ -71,7 +72,7 @@ sealed trait Space: isSubspaceCache.getOrElseUpdate(b, computeIsSubspace(a, b)) } - private var mySimplified: Space | Null = _ + @sharable private var mySimplified: Space | Null = _ def simplify(using Context): Space = val simplified = mySimplified From a5858985e8170b5fede85217a9f7d607dec160cc Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 22 Feb 2023 11:31:52 +0000 Subject: [PATCH 191/657] Space: Extract & document isDecomposableToChildren --- .../tools/dotc/transform/patmat/Space.scala | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 87c8d3760067..b33719063a3f 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -630,13 +630,7 @@ object SpaceEngine { // For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`. parts.map(tp.derivedAppliedType(_, targs)) - case tp if { - val cls = tp.classSymbol - cls.is(Sealed) - && cls.isOneOf(AbstractOrTrait) - && !cls.hasAnonymousChild - && cls.children.nonEmpty - } => + case tp if tp.classSymbol.isDecomposableToChildren => def getChildren(sym: Symbol): List[Symbol] = sym.children.flatMap { child => if child eq sym then List(sym) // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz... @@ -672,6 +666,18 @@ object SpaceEngine { rec(tp, Nil) } + extension (cls: Symbol) + /** A type is decomposable to children if it's sealed, + * abstract (or a trait) - so its not a sealed concrete class that can be instantiated on its own, + * has no anonymous children, which we wouldn't be able to name as counter-examples, + * but does have children. + * + * A sealed trait with no subclasses is considered not decomposable and thus is treated as an opaque type. + * A sealed trait with subclasses that then get removed after `refineUsingParent`, decomposes to the empty list. + * So that's why we consider whether a type has children. */ + def isDecomposableToChildren(using Context): Boolean = + cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) && !cls.hasAnonymousChild && cls.children.nonEmpty + val ListOfNoType = List(NoType) val ListOfTypNoType = ListOfNoType.map(Typ(_, decomposed = true)) From daef636b9431e4cdd3439489c6b2f1890e89dcaf Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 22 Feb 2023 11:36:24 +0000 Subject: [PATCH 192/657] Space: Initialise caches with null --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b33719063a3f..d69371520413 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -72,7 +72,7 @@ sealed trait Space: isSubspaceCache.getOrElseUpdate(b, computeIsSubspace(a, b)) } - @sharable private var mySimplified: Space | Null = _ + @sharable private var mySimplified: Space | Null = null def simplify(using Context): Space = val simplified = mySimplified @@ -93,7 +93,7 @@ case object Empty extends Space * */ case class Typ(tp: Type, decomposed: Boolean = true) extends Space: - private var myDecompose: List[Typ] | Null = _ + private var myDecompose: List[Typ] | Null = null def canDecompose(using Context): Boolean = decompose != SpaceEngine.ListOfTypNoType From 1a6224b8ced3b926733044e1c7700212303b3880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pa=C5=82ka?= Date: Wed, 15 Feb 2023 17:15:46 +0100 Subject: [PATCH 193/657] Preserve correct behaviour of structural selection for reflective and non-reflective access --- .../dotty/tools/dotc/core/Definitions.scala | 2 + .../src/dotty/tools/dotc/typer/Dynamic.scala | 38 ++++++++---- tests/run/i14340.check | 10 +++- tests/run/i14340.scala | 59 +++++++++++++++---- 4 files changed, 84 insertions(+), 25 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 174244b4a456..5416c0719467 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -808,6 +808,8 @@ class Definitions { @tu lazy val ClassTagModule: Symbol = ClassTagClass.companionModule @tu lazy val ClassTagModule_apply: Symbol = ClassTagModule.requiredMethod(nme.apply) + @tu lazy val ReflectSelectableTypeRef: TypeRef = requiredClassRef("scala.reflect.Selectable") + @tu lazy val TypeTestClass: ClassSymbol = requiredClass("scala.reflect.TypeTest") @tu lazy val TypeTest_unapply: Symbol = TypeTestClass.requiredMethod(nme.unapply) @tu lazy val TypeTestModule_identity: Symbol = TypeTestClass.companionModule.requiredMethod(nme.identity) diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 5de9ca6ab341..92d4f678b15c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -216,15 +216,33 @@ trait Dynamic { def fail(reason: String): Tree = errorTree(tree, em"Structural access not allowed on method $name because it $reason") - fun.tpe.widen match { - case tpe: ValueType if ValueClasses.isDerivedValueClass(tpe.classSymbol) => - val underlying = ValueClasses.valueClassUnbox(tpe.classSymbol.asClass).info.resultType.asSeenFrom(tpe, tpe.classSymbol) - val wrapped = structuralCall(nme.selectDynamic, Nil) - val resultTree = if wrapped.tpe.isExactlyAny then New(tpe, wrapped.cast(underlying) :: Nil) else wrapped - resultTree.cast(tpe) + extension (tree: Tree) + /** The implementations of `selectDynamic` and `applyDynamic` in `scala.reflect.SelectDynamic` have no information about the expected return type of a value/method which was declared in the refinement, + * only the JVM type after erasure can be obtained through reflection, e.g. + * + * class Foo(val i: Int) extends AnyVal + * class Reflective extends reflect.Selectable + * val reflective = new Reflective { + * def foo = Foo(1) // Foo at compile time, java.lang.Integer in reflection + * } + * + * Because of that reflective access cannot be implemented properly in `scala.reflect.SelectDynamic` itself + * because it's not known there if the value should be wrapped in a value class constructor call or not. + * Hence the logic of wrapping is performed here, relying on the fact that the implementations of `selectDynamic` and `applyDynamic` in `scala.reflect.SelectDynamic` are final. + */ + def maybeBoxingCast(tpe: Type) = + val maybeBoxed = + if ValueClasses.isDerivedValueClass(tpe.classSymbol) && qual.tpe <:< defn.ReflectSelectableTypeRef then + val genericUnderlying = ValueClasses.valueClassUnbox(tpe.classSymbol.asClass) + val underlying = tpe.select(genericUnderlying).widen.resultType + New(tpe, tree.cast(underlying) :: Nil) + else + tree + maybeBoxed.cast(tpe) + fun.tpe.widen match { case tpe: ValueType => - structuralCall(nme.selectDynamic, Nil).cast(tpe) + structuralCall(nme.selectDynamic, Nil).maybeBoxingCast(tpe) case tpe: MethodType => def isDependentMethod(tpe: Type): Boolean = tpe match { @@ -244,11 +262,7 @@ trait Dynamic { fail(i"has a parameter type with an unstable erasure") :: Nil else TypeErasure.erasure(tpe).asInstanceOf[MethodType].paramInfos.map(clsOf(_)) - val finalTpe = tpe.finalResultType - if ValueClasses.isDerivedValueClass(finalTpe.classSymbol) then - New(finalTpe, structuralCall(nme.applyDynamic, classOfs).cast(finalTpe) - .cast(ValueClasses.valueClassUnbox(finalTpe.classSymbol.asClass).info.resultType.asSeenFrom(finalTpe, finalTpe.classSymbol)) :: Nil) - else structuralCall(nme.applyDynamic, classOfs).cast(finalTpe) + structuralCall(nme.applyDynamic, classOfs).maybeBoxingCast(tpe.finalResultType) } // (@allanrenucci) I think everything below is dead code diff --git a/tests/run/i14340.check b/tests/run/i14340.check index db372c9502e9..e96afd98e6e9 100644 --- a/tests/run/i14340.check +++ b/tests/run/i14340.check @@ -1,4 +1,10 @@ 1 +1 +2 2 -3 -qux \ No newline at end of file +10 +10 +20 +20 +100 +100 \ No newline at end of file diff --git a/tests/run/i14340.scala b/tests/run/i14340.scala index e9d8733ccfa5..0670c7e471ac 100644 --- a/tests/run/i14340.scala +++ b/tests/run/i14340.scala @@ -1,20 +1,57 @@ +class Container1 extends reflect.Selectable + +class Container2(values: Map[String, Any], methods: Map[String, Int => Any]) extends Selectable: + def selectDynamic(name: String) = values(name) + def applyDynamic(name: String)(arg: Int) = methods(name)(arg) + class Foo(val value: Int) extends AnyVal class Bar[A](val value: A) extends AnyVal -class Container1 extends reflect.Selectable +object Helpers: + def foo = Foo(1) + def bar = Bar(Foo(2)) + def qux1 = Bar(new Container1 { def foo = Foo(10) }) + def qux2 = Bar(new Container2(Map("foo" -> Foo(20)), Map.empty).asInstanceOf[Container2 { def foo: Foo }]) + +@main def Test: Unit = + val cont1 = new Container1: + def foo = Helpers.foo + val bar = Helpers.bar + def qux1 = Helpers.qux1 + def qux2 = Helpers.qux2 + def fooFromInt(i: Int) = Foo(i) -class Container2 extends Selectable: - def selectDynamic(name: String) = Bar(name) + val cont2values = Map( + "foo" -> Helpers.foo, + "bar" -> Helpers.bar, + "qux1" -> Helpers.qux1, + "qux2" -> Helpers.qux2 + ) -val cont1 = new Container1: - def foo = new Foo(1) - val bar = new Bar(Foo(2)) - def fooFromInt(i: Int) = new Foo(i) + val cont2methods = Map( + "fooFromInt" -> { (i: Int) => Foo(i) } + ) -val cont2 = (new Container2).asInstanceOf[Container2 { def qux: Bar[String] }] + val cont2 = Container2(cont2values, cont2methods).asInstanceOf[Container2 { + def foo: Foo + def bar: Bar[Foo] + def qux1: Bar[Container1 { def foo: Foo }] + def qux2: Bar[Container2 { def foo: Foo }] + def fooFromInt(i: Int): Foo + }] + -@main def Test: Unit = println(cont1.foo.value) + println(cont2.foo.value) + println(cont1.bar.value.value) - println(cont1.fooFromInt(3).value) - println(cont2.qux.value) \ No newline at end of file + println(cont2.bar.value.value) + + println(cont1.qux1.value.foo.value) + println(cont2.qux1.value.foo.value) + + println(cont1.qux2.value.foo.value) + println(cont2.qux2.value.foo.value) + + println(cont1.fooFromInt(100).value) + println(cont2.fooFromInt(100).value) From 7622bb154dc2c79290fb00f6dec1aa6425184253 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 20 Feb 2023 11:47:33 +0100 Subject: [PATCH 194/657] Add way to debug -Xcheck-macros tree checking --- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 2daac7cdd604..133651fe08b2 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -729,6 +729,11 @@ object TreeChecker { try treeChecker.typed(expansion)(using checkingCtx) catch case err: java.lang.AssertionError => + val stack = + if !ctx.settings.Ydebug.value then "\nstacktrace available when compiling with `-Ydebug`" + else if err.getStackTrace == null then " no stacktrace" + else err.getStackTrace.nn.mkString(" ", " \n", "") + report.error( s"""Malformed tree was found while expanding macro with -Xcheck-macros. |The tree does not conform to the compiler's tree invariants. @@ -741,7 +746,7 @@ object TreeChecker { | |Error: |${err.getMessage} - | + |$stack |""", original ) From 7f0fe63d5488b6b95fe304e0a6cb2c9455d4f425 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 22 Feb 2023 17:52:13 +0100 Subject: [PATCH 195/657] Fix under-compilation when the method type in a SAM changes A lambda that implements a SAM type desugars (either at compile-time or runtime via invokedynamic) into a class that extends the SAM type, therefore we need to register an inheritance relationship to get the proper invalidation logic. Fixes sbt/zinc#830. --- .../src/dotty/tools/dotc/sbt/ExtractDependencies.scala | 9 +++++++++ sbt-test/source-dependencies/sam/A.scala | 3 +++ sbt-test/source-dependencies/sam/B.scala | 2 ++ sbt-test/source-dependencies/sam/build.sbt | 1 + sbt-test/source-dependencies/sam/changes/A.scala | 3 +++ sbt-test/source-dependencies/sam/test | 7 +++++++ 6 files changed, 25 insertions(+) create mode 100644 sbt-test/source-dependencies/sam/A.scala create mode 100644 sbt-test/source-dependencies/sam/B.scala create mode 100644 sbt-test/source-dependencies/sam/build.sbt create mode 100644 sbt-test/source-dependencies/sam/changes/A.scala create mode 100644 sbt-test/source-dependencies/sam/test diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index 3fb7a66dc89e..fe5c8d061c78 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -306,6 +306,13 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT } } + private def addInheritanceDependencies(tree: Closure)(using Context): Unit = + // If the tpt is empty, this is a non-SAM lambda, so no need to register + // an inheritance relationship. + if !tree.tpt.isEmpty then + val from = resolveDependencySource + _dependencies += ClassDependency(from, tree.tpt.tpe.classSymbol, LocalDependencyByInheritance) + private def addInheritanceDependencies(tree: Template)(using Context): Unit = if (tree.parents.nonEmpty) { val depContext = depContextOf(tree.symbol.owner) @@ -369,6 +376,8 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT case ref: RefTree => addMemberRefDependency(ref.symbol) addTypeDependency(ref.tpe) + case t: Closure => + addInheritanceDependencies(t) case t: Template => addInheritanceDependencies(t) case _ => diff --git a/sbt-test/source-dependencies/sam/A.scala b/sbt-test/source-dependencies/sam/A.scala new file mode 100644 index 000000000000..eb870b8332b6 --- /dev/null +++ b/sbt-test/source-dependencies/sam/A.scala @@ -0,0 +1,3 @@ +trait A { + def foo(): Int +} diff --git a/sbt-test/source-dependencies/sam/B.scala b/sbt-test/source-dependencies/sam/B.scala new file mode 100644 index 000000000000..87dfb28cdb33 --- /dev/null +++ b/sbt-test/source-dependencies/sam/B.scala @@ -0,0 +1,2 @@ +class B: + val f: A = () => 1 diff --git a/sbt-test/source-dependencies/sam/build.sbt b/sbt-test/source-dependencies/sam/build.sbt new file mode 100644 index 000000000000..63e314982c41 --- /dev/null +++ b/sbt-test/source-dependencies/sam/build.sbt @@ -0,0 +1 @@ +scalaVersion := sys.props("plugin.scalaVersion") diff --git a/sbt-test/source-dependencies/sam/changes/A.scala b/sbt-test/source-dependencies/sam/changes/A.scala new file mode 100644 index 000000000000..e9b339f2d1a4 --- /dev/null +++ b/sbt-test/source-dependencies/sam/changes/A.scala @@ -0,0 +1,3 @@ +trait A { + def foo(): String +} diff --git a/sbt-test/source-dependencies/sam/test b/sbt-test/source-dependencies/sam/test new file mode 100644 index 000000000000..3c4c9a0f001b --- /dev/null +++ b/sbt-test/source-dependencies/sam/test @@ -0,0 +1,7 @@ +> compile + +# change the SAM type +$ copy-file changes/A.scala A.scala + +# Both A.scala and B.scala should be recompiled, producing a compile error +-> compile From b889dbf6de279551141b34df3a78a305588db695 Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 23 Feb 2023 07:53:43 -0800 Subject: [PATCH 196/657] add regression test for pattern match on path-dependent type this was fixed recently (by #16827), but the test case is simpler than the one in the PR. minimized from user code on the Typelevel Discord --- tests/pos/t16827.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/pos/t16827.scala diff --git a/tests/pos/t16827.scala b/tests/pos/t16827.scala new file mode 100644 index 000000000000..17122fd9b580 --- /dev/null +++ b/tests/pos/t16827.scala @@ -0,0 +1,9 @@ +// scalac: -Werror + +trait Outer[F[_]]: + sealed trait Inner + trait Inner1 extends Inner + def foo(rv: Either[Inner, Int]) = + rv match + case Right(_) => + case Left(_) => From 92118c4201b96653f25d06f031a221a8a8e48559 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 21 Feb 2023 19:05:06 +0000 Subject: [PATCH 197/657] Avoid in ConstantTypes --- compiler/src/dotty/tools/dotc/core/Types.scala | 6 ++++++ .../dotty/tools/dotc/printing/Formatting.scala | 10 ++++++++++ tests/pos/i16954.scala | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 tests/pos/i16954.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d005cabebcfb..f53763cdf0bf 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -5736,6 +5736,12 @@ object Types { case tp @ SuperType(thistp, supertp) => derivedSuperType(tp, this(thistp), this(supertp)) + case tp @ ConstantType(const @ Constant(_: Type)) => + val classType = const.tpe + val classType1 = this(classType) + if classType eq classType1 then tp + else classType1 + case tp: LazyRef => LazyRef { refCtx => given Context = refCtx diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index 0acfd06de433..3f32b29654c9 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -71,6 +71,16 @@ object Formatting { given Show[TypeComparer.ApproxState] with def show(x: TypeComparer.ApproxState) = TypeComparer.ApproxState.Repr.show(x) + given Show[ast.TreeInfo.PurityLevel] with + def show(x: ast.TreeInfo.PurityLevel) = x match + case ast.TreeInfo.Path => "PurityLevel.Path" + case ast.TreeInfo.Pure => "PurityLevel.Pure" + case ast.TreeInfo.Idempotent => "PurityLevel.Idempotent" + case ast.TreeInfo.Impure => "PurityLevel.Impure" + case ast.TreeInfo.PurePath => "PurityLevel.PurePath" + case ast.TreeInfo.IdempotentPath => "PurityLevel.IdempotentPath" + case _ => s"PurityLevel(${x.x})" + given Show[Showable] = ShowAny given Show[Shown] = ShowAny given Show[Int] = ShowAny diff --git a/tests/pos/i16954.scala b/tests/pos/i16954.scala new file mode 100644 index 000000000000..ce7418e5e5e2 --- /dev/null +++ b/tests/pos/i16954.scala @@ -0,0 +1,18 @@ +class Test: + def test = + classOf[Test] + + def blck = + class Blck + val cls = classOf[Blck] + cls + + def expr = + class Expr + classOf[Expr] // was: "assertion failed: leak: Expr in { [..] }" crash + +object Test extends Test: + def main(args: Array[String]): Unit = + assert(test.getName == "Test", test.getName) + assert(blck.getName == "Test$Blck$1", blck.getName) + assert(expr.getName == "Test$Expr$1", expr.getName) From b75a4e9ec2f3f926d226684423bca6d816ed10e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Mon, 27 Feb 2023 12:05:40 +0100 Subject: [PATCH 198/657] Set reference version to 3.3.0-RC3 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 097876a63f92..82b7fee64879 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -80,7 +80,7 @@ object DottyJSPlugin extends AutoPlugin { object Build { import ScaladocConfigs._ - val referenceVersion = "3.3.0-RC2" + val referenceVersion = "3.3.0-RC3" val baseVersion = "3.3.1-RC1" From 6f75016212a9bed2e85a834c59fd23ba45a602b1 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 27 Feb 2023 12:11:24 +0100 Subject: [PATCH 199/657] Disable test for Scalajs --- tests/run/i16806.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/run/i16806.scala b/tests/run/i16806.scala index 0b0dfe1f6b35..16c0fb0d3ef5 100644 --- a/tests/run/i16806.scala +++ b/tests/run/i16806.scala @@ -1,3 +1,4 @@ +//scalajs: --skip import java.util.concurrent.Semaphore object Repro { From a43787af3eba1d155f1b107972658daedf6e68d8 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 27 Feb 2023 12:40:41 +0100 Subject: [PATCH 200/657] Add comment describing why LazyValControlState extends Serializable --- library/src/scala/runtime/LazyVals.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index 416ffc91d34a..d8c89c7abf28 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -45,6 +45,7 @@ object LazyVals { /* ------------- Start of public API ------------- */ + // This trait extends Serializable to fix #16806 that caused a race condition sealed trait LazyValControlState extends Serializable /** From 02ee89b695242d513c428ec269a0c9156862f44f Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 27 Feb 2023 14:51:57 +0100 Subject: [PATCH 201/657] Add padding and width to the mobile menu #16834 --- scaladoc/resources/dotty_res/styles/theme/layout/header.css | 1 + scaladoc/resources/dotty_res/styles/theme/layout/mobileMenu.css | 2 ++ 2 files changed, 3 insertions(+) diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/header.css b/scaladoc/resources/dotty_res/styles/theme/layout/header.css index 034f9ed43087..2e33023d0701 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/header.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/header.css @@ -89,6 +89,7 @@ #mobile-menu-toggle { margin-left: calc(3 * var(--base-spacing)); display: block; + padding: 16px; } .header-container-right .text-button { diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/mobileMenu.css b/scaladoc/resources/dotty_res/styles/theme/layout/mobileMenu.css index a7c08eedb4be..6fa692ab4662 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/mobileMenu.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/mobileMenu.css @@ -154,6 +154,8 @@ #mobile-menu-close { margin-left: auto; + width: 48px; + height: 48px; } #mobile-menu-close:disabled { From 1f51d1cd32a92cd16669a8b75b12d25a0236489d Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 27 Feb 2023 15:04:59 +0100 Subject: [PATCH 202/657] Reduction of the padding top in mobile version #16833 --- scaladoc/resources/dotty_res/styles/theme/layout/content.css | 1 + 1 file changed, 1 insertion(+) diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/content.css b/scaladoc/resources/dotty_res/styles/theme/layout/content.css index e09aa2697762..1c1554c8a75d 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/content.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/content.css @@ -91,6 +91,7 @@ @media (max-width: 768px) { #content { + padding-top: calc(5 * var(--base-spacing)); padding-bottom: calc(6 * var(--base-spacing)); } From 0d663477f0f08afadf4fa427c018b2354ac16f04 Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 27 Feb 2023 15:49:25 +0100 Subject: [PATCH 203/657] Correction of the value --- scaladoc/resources/dotty_res/styles/theme/layout/content.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/content.css b/scaladoc/resources/dotty_res/styles/theme/layout/content.css index 1c1554c8a75d..e46e66cba9e5 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/content.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/content.css @@ -91,7 +91,7 @@ @media (max-width: 768px) { #content { - padding-top: calc(5 * var(--base-spacing)); + padding-top: calc(10 * var(--base-spacing)); padding-bottom: calc(6 * var(--base-spacing)); } From 67df3e5efeea2e592a85dd62b94041061b32020e Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 27 Feb 2023 16:03:10 +0100 Subject: [PATCH 204/657] WUnused: Fix unused warnining in synthetic symbols --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 8 ++++++-- tests/neg-custom-args/fatal-warnings/i16925.scala | 8 ++++++++ tests/neg-custom-args/fatal-warnings/i16926.scala | 7 +++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i16925.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i16926.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 49ce64b00b88..9f3f5aded50c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -7,13 +7,13 @@ import dotty.tools.dotc.ast.untpd.ImportSelector import dotty.tools.dotc.config.ScalaSettings import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Decorators.{em, i} -import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.report import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo -import dotty.tools.dotc.util.Property +import dotty.tools.dotc.util.{Property, SourcePosition, SrcPos} import dotty.tools.dotc.core.Mode import dotty.tools.dotc.core.Types.TypeTraverser import dotty.tools.dotc.core.Types.Type @@ -302,6 +302,7 @@ object CheckUnused: * See the `isAccessibleAsIdent` extension method below in the file */ private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name])]()) + private val usedInPosition = MutSet[(SrcPos, Name)]() /* unused import collected during traversal */ private val unusedImport = MutSet[ImportSelector]() @@ -351,6 +352,7 @@ object CheckUnused: usedInScope.top += ((sym, sym.isAccessibleAsIdent, name)) usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name)) usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name)) + name.map(n => usedInPosition += ((sym.sourcePos, n))) /** Register a symbol that should be ignored */ def addIgnoredUsage(sym: Symbol)(using Context): Unit = @@ -455,6 +457,7 @@ object CheckUnused: if ctx.settings.WunusedHas.locals then localDefInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .map(d => d.namePos -> WarnTypes.LocalDefs).toList else Nil @@ -483,6 +486,7 @@ object CheckUnused: if ctx.settings.WunusedHas.patvars then patVarsInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .map(d => d.namePos -> WarnTypes.PatVars).toList else Nil diff --git a/tests/neg-custom-args/fatal-warnings/i16925.scala b/tests/neg-custom-args/fatal-warnings/i16925.scala new file mode 100644 index 000000000000..5cc94f53cdd4 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16925.scala @@ -0,0 +1,8 @@ +// scalac: -Wunused:all + +def hello = + for { + i <- 1 to 2 if true + _ = println(i) // OK + } yield () + diff --git a/tests/neg-custom-args/fatal-warnings/i16926.scala b/tests/neg-custom-args/fatal-warnings/i16926.scala new file mode 100644 index 000000000000..23f167f4ce30 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16926.scala @@ -0,0 +1,7 @@ +// scalac: -Wunused:all + +def hello(): Unit = + for { + i <- (0 to 10).toList + (a, b) = "hello" -> "world" // OK + } yield println(s"$a $b") From 9886ef36c93bb4cbfffb119bcf75d93d2cfb0b48 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Feb 2023 12:55:33 +0100 Subject: [PATCH 205/657] Move tests --- tests/neg-custom-args/fatal-warnings/i15503i.scala | 14 ++++++++++++++ tests/neg-custom-args/fatal-warnings/i16925.scala | 8 -------- tests/neg-custom-args/fatal-warnings/i16926.scala | 7 ------- 3 files changed, 14 insertions(+), 15 deletions(-) delete mode 100644 tests/neg-custom-args/fatal-warnings/i16925.scala delete mode 100644 tests/neg-custom-args/fatal-warnings/i16926.scala diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 82fb9acf7ace..ab83e1dafb3b 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -194,3 +194,17 @@ package foo.test.i16877: @ExampleAnnotation(new HashMap()) // OK class Test //OK + +package foo.test.i16926: + def hello(): Unit = + for { + i <- (0 to 10).toList + (a, b) = "hello" -> "world" // OK + } yield println(s"$a $b") + +package foo.test.i16925: + def hello = + for { + i <- 1 to 2 if true + _ = println(i) // OK + } yield () \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i16925.scala b/tests/neg-custom-args/fatal-warnings/i16925.scala deleted file mode 100644 index 5cc94f53cdd4..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i16925.scala +++ /dev/null @@ -1,8 +0,0 @@ -// scalac: -Wunused:all - -def hello = - for { - i <- 1 to 2 if true - _ = println(i) // OK - } yield () - diff --git a/tests/neg-custom-args/fatal-warnings/i16926.scala b/tests/neg-custom-args/fatal-warnings/i16926.scala deleted file mode 100644 index 23f167f4ce30..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i16926.scala +++ /dev/null @@ -1,7 +0,0 @@ -// scalac: -Wunused:all - -def hello(): Unit = - for { - i <- (0 to 10).toList - (a, b) = "hello" -> "world" // OK - } yield println(s"$a $b") From 6b36cd97bf276b57c392cf63e281f283345610cc Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Fri, 17 Feb 2023 11:46:53 +0000 Subject: [PATCH 206/657] Make unchecked cases non-`@unchecked` and non-unreachable --- .../src/dotty/tools/dotc/core/TypeOps.scala | 8 ++- .../tools/dotc/transform/ExpandSAMs.scala | 16 +++-- .../tools/dotc/transform/patmat/Space.scala | 59 +++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 4 +- .../fatal-warnings/i15662.scala | 1 - .../suppressed-type-test-warnings.scala | 2 - .../isInstanceOf/enum-approx2.scala | 4 +- .../neg-custom-args/isInstanceOf/i11178.scala | 3 +- .../neg-custom-args/isInstanceOf/i8932.scala | 3 +- tests/neg/i16451.check | 24 ++++++++ tests/neg/i16451.scala | 44 ++++++++++++++ tests/pos/i16451.CanForward.scala | 34 +++++++++++ tests/pos/i16451.DiffUtil.scala | 15 +++++ tests/pos/i16451.default.scala | 22 +++++++ tests/run-macros/i7898.check | 2 +- 15 files changed, 187 insertions(+), 54 deletions(-) create mode 100644 tests/neg/i16451.check create mode 100644 tests/neg/i16451.scala create mode 100644 tests/pos/i16451.CanForward.scala create mode 100644 tests/pos/i16451.DiffUtil.scala create mode 100644 tests/pos/i16451.default.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index c91412988e82..6809e4b9083c 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -765,7 +765,7 @@ object TypeOps: * * Otherwise, return NoType. */ - private def instantiateToSubType(tp1: NamedType, tp2: Type, mixins: List[Type])(using Context): Type = { + private def instantiateToSubType(tp1: NamedType, tp2: Type, mixins: List[Type])(using Context): Type = trace(i"instantiateToSubType($tp1, $tp2, $mixins)", typr) { // In order for a child type S to qualify as a valid subtype of the parent // T, we need to test whether it is possible S <: T. // @@ -854,6 +854,12 @@ object TypeOps: case tp: TypeRef if tp.symbol.isAbstractOrParamType => gadtSyms += tp.symbol traverseChildren(tp) + val owners = Iterator.iterate(tp.symbol)(_.maybeOwner).takeWhile(_.exists) + for sym <- owners do + // add ThisType's for the classes symbols in the ownership of `tp` + // for example, i16451.CanForward.scala, add `Namer.this`, as one of the owners of the type parameter `A1` + if sym.isClass && !sym.isAnonymousClass && !sym.isStaticOwner then + traverse(sym.thisType) case _ => traverseChildren(tp) } diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala index 0552fe31f8a2..0bfc444e0997 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -145,15 +145,13 @@ class ExpandSAMs extends MiniPhase: def translateMatch(tree: Match, pfParam: Symbol, cases: List[CaseDef], defaultValue: Tree)(using Context) = { val selector = tree.selector - val selectorTpe = selector.tpe.widen - val defaultSym = newSymbol(pfParam.owner, nme.WILDCARD, SyntheticCase, selectorTpe) - val defaultCase = - CaseDef( - Bind(defaultSym, Underscore(selectorTpe)), - EmptyTree, - defaultValue) - val unchecked = selector.annotated(New(ref(defn.UncheckedAnnot.typeRef))) - cpy.Match(tree)(unchecked, cases :+ defaultCase) + val cases1 = if cases.exists(isDefaultCase) then cases + else + val selectorTpe = selector.tpe.widen + val defaultSym = newSymbol(pfParam.owner, nme.WILDCARD, SyntheticCase, selectorTpe) + val defaultCase = CaseDef(Bind(defaultSym, Underscore(selectorTpe)), EmptyTree, defaultValue) + cases :+ defaultCase + cpy.Match(tree)(selector, cases1) .subst(param.symbol :: Nil, pfParam :: Nil) // Needed because a partial function can be written as: // param => param match { case "foo" if foo(param) => param } diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index d69371520413..3e05310d7249 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -9,7 +9,7 @@ import TypeUtils._ import Contexts._ import Flags._ import ast._ -import Decorators._ +import Decorators.{ show => _, * } import Symbols._ import StdNames._ import NameOps._ @@ -26,6 +26,8 @@ import util.{SrcPos, NoSourcePosition} import scala.annotation.internal.sharable import scala.collection.mutable +import SpaceEngine.* + /* Space logic for checking exhaustivity and unreachability of pattern matching * * Space can be thought of as a set of possible values. A type or a pattern @@ -57,7 +59,6 @@ import scala.collection.mutable /** space definition */ sealed trait Space: - import SpaceEngine.* @sharable private val isSubspaceCache = mutable.HashMap.empty[Space, Boolean] @@ -95,14 +96,14 @@ case object Empty extends Space case class Typ(tp: Type, decomposed: Boolean = true) extends Space: private var myDecompose: List[Typ] | Null = null - def canDecompose(using Context): Boolean = decompose != SpaceEngine.ListOfTypNoType + def canDecompose(using Context): Boolean = decompose != ListOfTypNoType def decompose(using Context): List[Typ] = val decompose = myDecompose if decompose == null then val decompose = tp match - case SpaceEngine.Parts(parts) => parts.map(Typ(_, decomposed = true)) - case _ => SpaceEngine.ListOfTypNoType + case Parts(parts) => parts.map(Typ(_, decomposed = true)) + case _ => ListOfTypNoType myDecompose = decompose decompose else decompose @@ -346,7 +347,7 @@ object SpaceEngine { } /** Return the space that represents the pattern `pat` */ - def project(pat: Tree)(using Context): Space = pat match { + def project(pat: Tree)(using Context): Space = trace(i"project($pat ${pat.className} ${pat.tpe})", debug, show)(pat match { case Literal(c) => if (c.value.isInstanceOf[Symbol]) Typ(c.value.asInstanceOf[Symbol].termRef, decomposed = false) @@ -407,7 +408,7 @@ object SpaceEngine { case _ => // Pattern is an arbitrary expression; assume a skolem (i.e. an unknown value) of the pattern type Typ(pat.tpe.narrow, decomposed = false) - } + }) private def project(tp: Type)(using Context): Space = tp match { case OrType(tp1, tp2) => Or(project(tp1) :: project(tp2) :: Nil) @@ -461,16 +462,23 @@ object SpaceEngine { * If `isValue` is true, then pattern-bound symbols are erased to its upper bound. * This is needed to avoid spurious unreachable warnings. See tests/patmat/i6197.scala. */ - private def erase(tp: Type, inArray: Boolean = false, isValue: Boolean = false)(using Context): Type = trace(i"$tp erased to", debug) { - - tp match { + private def erase(tp: Type, inArray: Boolean = false, isValue: Boolean = false)(using Context): Type = + trace(i"erase($tp${if inArray then " inArray" else ""}${if isValue then " isValue" else ""})", debug)(tp match { case tp @ AppliedType(tycon, args) if tycon.typeSymbol.isPatternBound => WildcardType case tp @ AppliedType(tycon, args) => val args2 = - if (tycon.isRef(defn.ArrayClass)) args.map(arg => erase(arg, inArray = true, isValue = false)) - else args.map(arg => erase(arg, inArray = false, isValue = false)) + if tycon.isRef(defn.ArrayClass) then + args.map(arg => erase(arg, inArray = true, isValue = false)) + else tycon.typeParams.lazyZip(args).map { (tparam, arg) => + if isValue && tparam.paramVarianceSign == 0 then + // when matching against a value, + // any type argument for an invariant type parameter will be unchecked, + // meaning it won't fail to match against anything; thus the wildcard replacement + WildcardType + else erase(arg, inArray = false, isValue = false) + } tp.derivedAppliedType(erase(tycon, inArray, isValue = false), args2) case tp @ OrType(tp1, tp2) => @@ -488,8 +496,7 @@ object SpaceEngine { else WildcardType case _ => tp - } - } + }) /** Space of the pattern: unapplySeq(a, b, c: _*) */ @@ -873,16 +880,11 @@ object SpaceEngine { case _ => tp }) - def checkExhaustivity(_match: Match)(using Context): Unit = { - val Match(sel, cases) = _match - debug.println(i"checking exhaustivity of ${_match}") - - if (!exhaustivityCheckable(sel)) return - - val selTyp = toUnderlying(sel.tpe).dealias + def checkExhaustivity(m: Match)(using Context): Unit = if exhaustivityCheckable(m.selector) then trace(i"checkExhaustivity($m)", debug) { + val selTyp = toUnderlying(m.selector.tpe).dealias debug.println(i"selTyp = $selTyp") - val patternSpace = Or(cases.foldLeft(List.empty[Space]) { (acc, x) => + val patternSpace = Or(m.cases.foldLeft(List.empty[Space]) { (acc, x) => val space = if (x.guard.isEmpty) project(x.pat) else Empty debug.println(s"${x.pat.show} ====> ${show(space)}") space :: acc @@ -899,7 +901,7 @@ object SpaceEngine { if uncovered.nonEmpty then val hasMore = uncovered.lengthCompare(6) > 0 val deduped = dedup(uncovered.take(6)) - report.warning(PatternMatchExhaustivity(showSpaces(deduped), hasMore), sel.srcPos) + report.warning(PatternMatchExhaustivity(showSpaces(deduped), hasMore), m.selector) } private def redundancyCheckable(sel: Tree)(using Context): Boolean = @@ -912,14 +914,10 @@ object SpaceEngine { && !sel.tpe.widen.isRef(defn.QuotedExprClass) && !sel.tpe.widen.isRef(defn.QuotedTypeClass) - def checkRedundancy(_match: Match)(using Context): Unit = { - val Match(sel, _) = _match - val cases = _match.cases.toIndexedSeq - debug.println(i"checking redundancy in $_match") - - if (!redundancyCheckable(sel)) return + def checkRedundancy(m: Match)(using Context): Unit = if redundancyCheckable(m.selector) then trace(i"checkRedundancy($m)", debug) { + val cases = m.cases.toIndexedSeq - val selTyp = toUnderlying(sel.tpe).dealias + val selTyp = toUnderlying(m.selector.tpe).dealias debug.println(i"selTyp = $selTyp") val isNullable = selTyp.classSymbol.isNullableClass @@ -953,6 +951,7 @@ object SpaceEngine { for (pat <- deferred.reverseIterator) report.warning(MatchCaseUnreachable(), pat.srcPos) if pat != EmptyTree // rethrow case of catch uses EmptyTree + && !pat.symbol.isAllOf(SyntheticCase, butNot=Method) // ExpandSAMs default cases use SyntheticCase && isSubspace(covered, prev) then { val nullOnly = isNullable && i == len - 1 && isWildcardArg(pat) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9d8fdcc006c9..b39116f971f8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1618,9 +1618,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } else { val (protoFormals, _) = decomposeProtoFunction(pt, 1, tree.srcPos) - val checkMode = - if (pt.isRef(defn.PartialFunctionClass)) desugar.MatchCheck.None - else desugar.MatchCheck.Exhaustive + val checkMode = desugar.MatchCheck.Exhaustive typed(desugar.makeCaseLambda(tree.cases, checkMode, protoFormals.length).withSpan(tree.span), pt) } case _ => diff --git a/tests/neg-custom-args/fatal-warnings/i15662.scala b/tests/neg-custom-args/fatal-warnings/i15662.scala index 1d5ff21eb3ba..afe505922603 100644 --- a/tests/neg-custom-args/fatal-warnings/i15662.scala +++ b/tests/neg-custom-args/fatal-warnings/i15662.scala @@ -3,7 +3,6 @@ case class Composite[T](v: T) def m(composite: Composite[_]): Unit = composite match { case Composite[Int](v) => println(v) // error: cannot be checked at runtime - case _ => println("OTHER") } def m2(composite: Composite[_]): Unit = diff --git a/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala b/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala index 92d86b3307e5..175096fc6b21 100644 --- a/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala +++ b/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala @@ -18,12 +18,10 @@ object Test { def err2[A, B](value: Foo[A, B], a: A => Int): B = value match { case b: Bar[B] => // spurious // error b.x - case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } def fail[A, B](value: Foo[A, B], a: A => Int): B = value match { case b: Bar[Int] => // error b.x - case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } } diff --git a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala index 516b765ec64b..5e3bdef7553d 100644 --- a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala +++ b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala @@ -4,7 +4,5 @@ case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B] class Test { def eval(e: Fun[Int, Int]) = e match { case Fun(x: Fun[Int, Double]) => ??? // error - case Fun(x: Exp[Int => String]) => ??? // error - case _ => } -} \ No newline at end of file +} diff --git a/tests/neg-custom-args/isInstanceOf/i11178.scala b/tests/neg-custom-args/isInstanceOf/i11178.scala index 0d6867eba75f..71bc346e5743 100644 --- a/tests/neg-custom-args/isInstanceOf/i11178.scala +++ b/tests/neg-custom-args/isInstanceOf/i11178.scala @@ -12,7 +12,6 @@ object Test1 { def test[A](bar: Bar[A]) = bar match { case _: Bar[Boolean] => ??? // error - case _ => ??? } } @@ -36,4 +35,4 @@ object Test3 { case _: Bar[Boolean] => ??? // error case _ => ??? } -} \ No newline at end of file +} diff --git a/tests/neg-custom-args/isInstanceOf/i8932.scala b/tests/neg-custom-args/isInstanceOf/i8932.scala index f77c28c7b0a7..e070fdae518c 100644 --- a/tests/neg-custom-args/isInstanceOf/i8932.scala +++ b/tests/neg-custom-args/isInstanceOf/i8932.scala @@ -6,7 +6,6 @@ class Dummy extends Bar[Nothing] with Foo[String] def bugReport[A](foo: Foo[A]): Foo[A] = foo match { case bar: Bar[A] => bar // error - case dummy: Dummy => ??? } -def test = bugReport(new Dummy: Foo[String]) \ No newline at end of file +def test = bugReport(new Dummy: Foo[String]) diff --git a/tests/neg/i16451.check b/tests/neg/i16451.check new file mode 100644 index 000000000000..e53085e8eafa --- /dev/null +++ b/tests/neg/i16451.check @@ -0,0 +1,24 @@ +-- Error: tests/neg/i16451.scala:13:9 ---------------------------------------------------------------------------------- +13 | case x: Wrapper[Color.Red.type] => Some(x) // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] +-- Error: tests/neg/i16451.scala:21:9 ---------------------------------------------------------------------------------- +21 | case x: Wrapper[Color.Red.type] => Some(x) // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Any +-- Error: tests/neg/i16451.scala:25:9 ---------------------------------------------------------------------------------- +25 | case x: Wrapper[Color.Red.type] => Some(x) // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] +-- Error: tests/neg/i16451.scala:29:9 ---------------------------------------------------------------------------------- +29 | case x: Wrapper[Color.Red.type] => Some(x) // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from A1 +-- Error: tests/neg/i16451.scala:34:11 --------------------------------------------------------------------------------- +34 | case x: Wrapper[Color.Red.type] => x // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] +-- Error: tests/neg/i16451.scala:39:11 --------------------------------------------------------------------------------- +39 | case x: Wrapper[Color.Red.type] => x // error + | ^ + |the type test for Wrapper[(Color.Red : Color)] cannot be checked at runtime because its type arguments can't be determined from Wrapper[Color] diff --git a/tests/neg/i16451.scala b/tests/neg/i16451.scala new file mode 100644 index 000000000000..685b79477bbe --- /dev/null +++ b/tests/neg/i16451.scala @@ -0,0 +1,44 @@ +// scalac: -Werror +enum Color: + case Red, Green +//sealed trait Color +//object Color: +// case object Red extends Color +// case object Green extends Color + +case class Wrapper[A](value: A) + +object Test: + def test_correct(x: Wrapper[Color]): Option[Wrapper[Color.Red.type]] = x match + case x: Wrapper[Color.Red.type] => Some(x) // error + case null => None + + def test_different(x: Wrapper[Color]): Option[Wrapper[Color]] = x match + case x @ Wrapper(_: Color.Red.type) => Some(x) + case x @ Wrapper(_: Color.Green.type) => None + + def test_any(x: Any): Option[Wrapper[Color.Red.type]] = x match + case x: Wrapper[Color.Red.type] => Some(x) // error + case _ => None + + def test_wrong(x: Wrapper[Color]): Option[Wrapper[Color.Red.type]] = x match + case x: Wrapper[Color.Red.type] => Some(x) // error + case null => None + + def t2[A1 <: Wrapper[Color]](x: A1): Option[Wrapper[Color.Red.type]] = x match + case x: Wrapper[Color.Red.type] => Some(x) // error + case null => None + + def test_wrong_seq(xs: Seq[Wrapper[Color]]): Seq[Wrapper[Color.Red.type]] = + xs.collect { + case x: Wrapper[Color.Red.type] => x // error + } + + def test_wrong_seq2(xs: Seq[Wrapper[Color]]): Seq[Wrapper[Color.Red.type]] = + xs.collect { x => x match + case x: Wrapper[Color.Red.type] => x // error + } + + def main(args: Array[String]): Unit = + println(test_wrong_seq(Seq(Wrapper(Color.Red), Wrapper(Color.Green)))) + // outputs: List(Wrapper(Red), Wrapper(Green)) diff --git a/tests/pos/i16451.CanForward.scala b/tests/pos/i16451.CanForward.scala new file mode 100644 index 000000000000..a09a26f22acc --- /dev/null +++ b/tests/pos/i16451.CanForward.scala @@ -0,0 +1,34 @@ +// scalac: -Werror +abstract class Namer: + private enum CanForward: + case Yes + case No(whyNot: String) + case Skip // for members that have never forwarders + + class Mbr + private def canForward(mbr: Mbr): CanForward = CanForward.Yes + + private def applyOrElse[A1 <: CanForward, B1 >: String](x: A1, default: A1 => B1): B1 = x match + case CanForward.No(whyNot @ _) => whyNot + case _ => "" + + def addForwardersNamed(mbrs: List[Mbr]) = + val reason = mbrs.map(canForward).collect { + case CanForward.No(whyNot) => whyNot + }.headOption.getOrElse("") + + class ClassCompleter: + def addForwardersNamed(mbrs: List[Mbr]) = + val reason = mbrs.map(canForward).collect { + case CanForward.No(whyNot) => whyNot + }.headOption.getOrElse("") + + private def exportForwarders = + def addForwardersNamed(mbrs: List[Mbr]) = + val reason = mbrs.map(canForward).collect { + case CanForward.No(whyNot) => whyNot + }.headOption.getOrElse("") + if mbrs.size == 4 then + val reason = mbrs.map(canForward).collect { + case CanForward.No(whyNot) => whyNot + }.headOption.getOrElse("") diff --git a/tests/pos/i16451.DiffUtil.scala b/tests/pos/i16451.DiffUtil.scala new file mode 100644 index 000000000000..3ade8bb73aa7 --- /dev/null +++ b/tests/pos/i16451.DiffUtil.scala @@ -0,0 +1,15 @@ +// scalac: -Werror +object DiffUtil: + private sealed trait Patch + private final case class Unmodified(str: String) extends Patch + private final case class Modified(original: String, str: String) extends Patch + private final case class Deleted(str: String) extends Patch + private final case class Inserted(str: String) extends Patch + + private def test(diff: Array[Patch]) = + diff.collect { + case Unmodified(str) => str + case Inserted(str) => s"+$str" + case Modified(orig, str) => s"{$orig,$str}" + case Deleted(str) => s"-$str" + }.mkString diff --git a/tests/pos/i16451.default.scala b/tests/pos/i16451.default.scala new file mode 100644 index 000000000000..2751f4901b5f --- /dev/null +++ b/tests/pos/i16451.default.scala @@ -0,0 +1,22 @@ +// scalac: -Werror + +import java.lang.reflect.* +import scala.annotation.tailrec + +class Test: + @tailrec private def unwrapThrowable(x: Throwable): Throwable = x match { + case _: InvocationTargetException | // thrown by reflectively invoked method or constructor + _: ExceptionInInitializerError | // thrown when running a static initializer (e.g. a scala module constructor) + _: UndeclaredThrowableException | // invocation on a proxy instance if its invocation handler's `invoke` throws an exception + _: ClassNotFoundException | // no definition for a class instantiated by name + _: NoClassDefFoundError // the definition existed when the executing class was compiled, but can no longer be found + if x.getCause != null => + unwrapThrowable(x.getCause) + case _ => x + } + + private def unwrapHandler[T](pf: PartialFunction[Throwable, T]): PartialFunction[Throwable, T] = + pf.compose({ case ex => unwrapThrowable(ex) }) + + def test = + unwrapHandler({ case ex => throw ex }) diff --git a/tests/run-macros/i7898.check b/tests/run-macros/i7898.check index 64da4a308f63..044c101dc772 100644 --- a/tests/run-macros/i7898.check +++ b/tests/run-macros/i7898.check @@ -1,4 +1,4 @@ -scala.List.apply[scala.PartialFunction[scala.Int, scala.Predef.String]](((x$1: scala.Int) => (x$1: @scala.unchecked) match { +scala.List.apply[scala.PartialFunction[scala.Int, scala.Predef.String]](((x$1: scala.Int) => x$1 match { case 1 => "x" })) From 7de74bd2a2689af1a33fabc9bdd5e58d5f5d7668 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 28 Feb 2023 14:16:32 +0100 Subject: [PATCH 207/657] Use "explain=" to tag an -explain message in an implicitNotFound This approach mirrors the format used in `@noWarn`. There it is possible to tag encode the category and message in the string. ``` @nowarn("cat=deprecation&msg=Implementation ...") ``` The current implementation for `explain=` only allows this tag at the start of the string. Making the message either a old-style message or an message in the explain. We could extend this to support `msg=` to be able to have both a custom message and an explain. Furthermore we could insert some flags to explicitly state how to display the original message (for example: not-found=hidden, not-found=in-message, not-found=in-explain). --- compiler/src/dotty/tools/dotc/reporting/messages.scala | 9 +++++---- library/src/scala/util/boundary.scala | 2 +- tests/neg-custom-args/explain/labelNotFound.scala | 2 -- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index fb9eaed094e8..2652102eb02c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2640,17 +2640,18 @@ class MissingImplicitArgument( recur(pt) /** The implicitNotFound annotation on the parameter, or else on the type. - * implicitNotFound message strings starting with `...` are intended for - * additional explanations, not the message proper. The leading `...` is + * implicitNotFound message strings starting with `explain=` are intended for + * additional explanations, not the message proper. The leading `explain=` is * dropped in this case. * @param explain The message is used for an additional explanation, not * the message proper. */ def userDefinedImplicitNotFoundMessage(explain: Boolean)(using Context): Option[String] = + val explainTag = "explain=" def filter(msg: Option[String]) = msg match case Some(str) => - if str.startsWith("...") then - if explain then Some(str.drop(3)) else None + if str.startsWith(explainTag) then + if explain then Some(str.drop(explainTag.length)) else None else if explain then None else msg case None => None diff --git a/library/src/scala/util/boundary.scala b/library/src/scala/util/boundary.scala index ea21446d9682..2edd754bbb93 100644 --- a/library/src/scala/util/boundary.scala +++ b/library/src/scala/util/boundary.scala @@ -35,7 +35,7 @@ object boundary: /** Labels are targets indicating which boundary will be exited by a `break`. */ - @implicitNotFound("...A Label is generated from an enclosing `scala.util.boundary` call.\nMaybe that boundary is missing?") + @implicitNotFound("explain=A Label is generated from an enclosing `scala.util.boundary` call.\nMaybe that boundary is missing?") final class Label[-T] /** Abort current computation and instead return `value` as the value of diff --git a/tests/neg-custom-args/explain/labelNotFound.scala b/tests/neg-custom-args/explain/labelNotFound.scala index 8eeaed90f331..2618600702da 100644 --- a/tests/neg-custom-args/explain/labelNotFound.scala +++ b/tests/neg-custom-args/explain/labelNotFound.scala @@ -1,4 +1,2 @@ object Test: scala.util.boundary.break(1) // error - - From 821023547864dfba0662965dd1f895edf08728f4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 28 Feb 2023 14:32:56 +0100 Subject: [PATCH 208/657] Helpful implicit not found message for `Quotes` Fixes #16888 --- library/src/scala/quoted/Quotes.scala | 19 +++++++++++++++++++ tests/neg-custom-args/explain/i16888.check | 14 ++++++++++++++ tests/neg-custom-args/explain/i16888.scala | 1 + tests/neg-macros/i6436.check | 2 ++ 4 files changed, 36 insertions(+) create mode 100644 tests/neg-custom-args/explain/i16888.check create mode 100644 tests/neg-custom-args/explain/i16888.scala diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 22ec107aeae8..05c253a76fd3 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -1,6 +1,7 @@ package scala.quoted import scala.annotation.experimental +import scala.annotation.implicitNotFound import scala.reflect.TypeTest /** Current Quotes in scope @@ -21,7 +22,25 @@ transparent inline def quotes(using q: Quotes): q.type = q * * It contains the low-level Typed AST API metaprogramming API. * This API does not have the static type guarantees that `Expr` and `Type` provide. + * `Quotes` are generated from an enclosing `${ ... }` or `scala.staging.run`. For example: + * ```scala sc:nocompile + * import scala.quoted._ + * inline def myMacro: Expr[T] = + * ${ /* (quotes: Quotes) ?=> */ myExpr } + * def myExpr(using Quotes): Expr[T] = + * '{ f(${ /* (quotes: Quotes) ?=> */ myOtherExpr }) } + * } + * def myOtherExpr(using Quotes): Expr[U] = '{ ... } + * ``` */ + +@implicitNotFound("""explain=Maybe this methods is missing a `(using Quotes)` parameter. + +Maybe that splice `$ { ... }` is missing? +Given instances of `Quotes` are generated from an enclosing splice `$ { ... }` (or `scala.staging.run` call). +A splice can be thought as a method with the following signature. + def $[T](body: Quotes ?=> Expr[T]): T +""") trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => // Extension methods for `Expr[T]` diff --git a/tests/neg-custom-args/explain/i16888.check b/tests/neg-custom-args/explain/i16888.check new file mode 100644 index 000000000000..e184ea418a94 --- /dev/null +++ b/tests/neg-custom-args/explain/i16888.check @@ -0,0 +1,14 @@ +-- [E172] Type Error: tests/neg-custom-args/explain/i16888.scala:1:38 -------------------------------------------------- +1 |def test = summon[scala.quoted.Quotes] // error + | ^ + | No given instance of type quoted.Quotes was found for parameter x of method summon in object Predef + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Maybe this methods is missing a `(using Quotes)` parameter. + | + | Maybe that splice `$ { ... }` is missing? + | Given instances of `Quotes` are generated from an enclosing splice `$ { ... }` (or `scala.staging.run` call). + | A splice can be thought as a method with the following signature. + | def $[T](body: Quotes ?=> Expr[T]): T + --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg-custom-args/explain/i16888.scala b/tests/neg-custom-args/explain/i16888.scala new file mode 100644 index 000000000000..9d3fd0f2f57e --- /dev/null +++ b/tests/neg-custom-args/explain/i16888.scala @@ -0,0 +1 @@ +def test = summon[scala.quoted.Quotes] // error diff --git a/tests/neg-macros/i6436.check b/tests/neg-macros/i6436.check index 43e93b2e64e5..d563abb5424c 100644 --- a/tests/neg-macros/i6436.check +++ b/tests/neg-macros/i6436.check @@ -2,6 +2,8 @@ 5 | case '{ StringContext(${Varargs(parts)}*) } => // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | No given instance of type scala.quoted.Quotes was found + | + | longer explanation available when compiling with `-explain` -- [E006] Not Found Error: tests/neg-macros/i6436.scala:6:34 ----------------------------------------------------------- 6 | val ps: Seq[Expr[String]] = parts // error | ^^^^^ From dbdca17b01eef9e9d3ee0d46f36ffe97883c4a65 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Feb 2023 22:38:25 +0100 Subject: [PATCH 209/657] Simplify QuoteMatcher using optional --- .../src/dotty/tools/dotc/util/optional.scala | 19 +++++++ .../quoted/runtime/impl/QuoteMatcher.scala | 55 +++++++------------ .../quoted/runtime/impl/QuotesImpl.scala | 22 ++++---- 3 files changed, 49 insertions(+), 47 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/util/optional.scala diff --git a/compiler/src/dotty/tools/dotc/util/optional.scala b/compiler/src/dotty/tools/dotc/util/optional.scala new file mode 100644 index 000000000000..cb62315d3c98 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/util/optional.scala @@ -0,0 +1,19 @@ +package dotty.tools.dotc.util + +import scala.util.boundary + +/** Return type that indicates that the method returns a T or aborts to the enclosing boundary with a `None` */ +type optional[T] = boundary.Label[None.type] ?=> T + +/** A prompt for `Option`, which establishes a boundary which `_.?` on `Option` can return */ +object optional: + inline def apply[T](inline body: optional[T]): Option[T] = + boundary(Some(body)) + + extension [T](r: Option[T]) + inline def ? (using label: boundary.Label[None.type]): T = r match + case Some(x) => x + case None => boundary.break(None) + + inline def break()(using label: boundary.Label[None.type]): Nothing = + boundary.break(None) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 7c952dbbe142..9a77cba97400 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -1,7 +1,6 @@ package scala.quoted package runtime.impl - import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Flags.* @@ -9,6 +8,7 @@ import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.Symbols.* +import dotty.tools.dotc.util.optional /** Matches a quoted tree against a quoted pattern tree. * A quoted pattern tree may have type and term holes in addition to normal terms. @@ -103,12 +103,13 @@ import dotty.tools.dotc.core.Symbols.* object QuoteMatcher { import tpd.* - // TODO improve performance - // TODO use flag from Context. Maybe -debug or add -debug-macros private inline val debug = false - import Matching._ + /** Sequence of matched expressions. + * These expressions are part of the scrutinee and will be bound to the quote pattern term splices. + */ + type MatchingExprs = Seq[Expr[Any]] /** A map relating equivalent symbols from the scrutinee and the pattern * For example in @@ -121,19 +122,20 @@ object QuoteMatcher { private def withEnv[T](env: Env)(body: Env ?=> T): T = body(using env) - def treeMatch(scrutineeTree: Tree, patternTree: Tree)(using Context): Option[Tuple] = + def treeMatch(scrutineeTree: Tree, patternTree: Tree)(using Context): Option[MatchingExprs] = given Env = Map.empty - scrutineeTree =?= patternTree + optional: + scrutineeTree =?= patternTree /** Check that all trees match with `mtch` and concatenate the results with &&& */ - private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => Matching): Matching = (l1, l2) match { + private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => MatchingExprs): optional[MatchingExprs] = (l1, l2) match { case (x :: xs, y :: ys) => mtch(x, y) &&& matchLists(xs, ys)(mtch) case (Nil, Nil) => matched case _ => notMatched } extension (scrutinees: List[Tree]) - private def =?= (patterns: List[Tree])(using Env, Context): Matching = + private def =?= (patterns: List[Tree])(using Env, Context): optional[MatchingExprs] = matchLists(scrutinees, patterns)(_ =?= _) extension (scrutinee0: Tree) @@ -144,9 +146,9 @@ object QuoteMatcher { * @param scrutinee The tree being matched * @param pattern The pattern tree that the scrutinee should match. Contains `patternHole` holes. * @param `summon[Env]` Set of tuples containing pairs of symbols (s, p) where s defines a symbol in `scrutinee` which corresponds to symbol p in `pattern`. - * @return `None` if it did not match or `Some(tup: Tuple)` if it matched where `tup` contains the contents of the holes. + * @return `None` if it did not match or `Some(tup: MatchingExprs)` if it matched where `tup` contains the contents of the holes. */ - private def =?= (pattern0: Tree)(using Env, Context): Matching = + private def =?= (pattern0: Tree)(using Env, Context): optional[MatchingExprs] = /* Match block flattening */ // TODO move to cases /** Normalize the tree */ @@ -431,7 +433,6 @@ object QuoteMatcher { case _ => scrutinee val pattern = patternTree.symbol - devirtualizedScrutinee == pattern || summon[Env].get(devirtualizedScrutinee).contains(pattern) || devirtualizedScrutinee.allOverriddenSymbols.contains(pattern) @@ -452,32 +453,16 @@ object QuoteMatcher { accumulator.apply(Set.empty, term) } - /** Result of matching a part of an expression */ - private type Matching = Option[Tuple] - - private object Matching { - - def notMatched: Matching = None + private inline def notMatched: optional[MatchingExprs] = + optional.break() - val matched: Matching = Some(Tuple()) + private inline def matched: MatchingExprs = + Seq.empty - def matched(tree: Tree)(using Context): Matching = - Some(Tuple1(new ExprImpl(tree, SpliceScope.getCurrent))) + private inline def matched(tree: Tree)(using Context): MatchingExprs = + Seq(new ExprImpl(tree, SpliceScope.getCurrent)) - extension (self: Matching) - def asOptionOfTuple: Option[Tuple] = self - - /** Concatenates the contents of two successful matchings or return a `notMatched` */ - def &&& (that: => Matching): Matching = self match { - case Some(x) => - that match { - case Some(y) => Some(x ++ y) - case _ => None - } - case _ => None - } - end extension - - } + extension (self: MatchingExprs) + private inline def &&& (that: MatchingExprs): MatchingExprs = self ++ that } diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 965a3e907878..378f3f6a5c40 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -3139,18 +3139,16 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler val matchings = QuoteMatcher.treeMatch(scrutinee, pat1)(using ctx1) - if typeHoles.isEmpty then matchings - else { - // After matching and doing all subtype checks, we have to approximate all the type bindings - // that we have found, seal them in a quoted.Type and add them to the result - def typeHoleApproximation(sym: Symbol) = - val fromAboveAnnot = sym.hasAnnotation(dotc.core.Symbols.defn.QuotedRuntimePatterns_fromAboveAnnot) - val fullBounds = ctx1.gadt.fullBounds(sym) - val tp = if fromAboveAnnot then fullBounds.hi else fullBounds.lo - reflect.TypeReprMethods.asType(tp) - matchings.map { tup => - Tuple.fromIArray(typeHoles.map(typeHoleApproximation).toArray.asInstanceOf[IArray[Object]]) ++ tup - } + // After matching and doing all subtype checks, we have to approximate all the type bindings + // that we have found, seal them in a quoted.Type and add them to the result + def typeHoleApproximation(sym: Symbol) = + val fromAboveAnnot = sym.hasAnnotation(dotc.core.Symbols.defn.QuotedRuntimePatterns_fromAboveAnnot) + val fullBounds = ctx1.gadt.fullBounds(sym) + val tp = if fromAboveAnnot then fullBounds.hi else fullBounds.lo + reflect.TypeReprMethods.asType(tp) + matchings.map { tup => + val results = typeHoles.map(typeHoleApproximation) ++ tup + Tuple.fromIArray(results.toArray.asInstanceOf[IArray[Object]]) } } From fa5df056172e2a25f72103720218daa94a19e115 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Feb 2023 17:54:21 +0100 Subject: [PATCH 210/657] Remove unused import --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 9f3f5aded50c..e7e6e1c4952c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -13,7 +13,7 @@ import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.report import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo -import dotty.tools.dotc.util.{Property, SourcePosition, SrcPos} +import dotty.tools.dotc.util.{Property, SrcPos} import dotty.tools.dotc.core.Mode import dotty.tools.dotc.core.Types.TypeTraverser import dotty.tools.dotc.core.Types.Type From e7e0df0e1ef6242a12a98544c5b034c7cafed678 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 27 Feb 2023 13:47:08 +0100 Subject: [PATCH 211/657] Generate kind-correct wildcards when selecting from a wildcard Previously, derivedSelect would incorrectly approximate `fn.R` by `? >: Nothing <: Any` in `i16997.min.scala`, which subsequently lead to a kind-incorrect type application `Any[Y]` that broke type inference. #16999 proposed to fix this by changing type inference, but it seems like we don't need to do that if we make sure our wildcards are always kind-correct. Note that the wildcard logic in lookupRefined could be removed: it seems like it was supposed to be used by `reduceProjection` according to its documentation, but the constructor of `NamedType` disallows wildcards as prefixes so it didn't actually do anything there. Co-Authored-By: Dale Wijnand --- .../src/dotty/tools/dotc/core/Types.scala | 16 ++--- .../test/dotc/pos-test-pickling.blacklist | 1 + tests/pos/i16997.min.scala | 12 ++++ tests/pos/i16997.scala | 63 +++++++++++++++++++ 4 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 tests/pos/i16997.min.scala create mode 100644 tests/pos/i16997.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index d005cabebcfb..840f49a19cc2 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1574,8 +1574,6 @@ object Types { else NoType case SkolemType(tp) => loop(tp) - case pre: WildcardType => - WildcardType case pre: TypeRef => pre.info match { case TypeAlias(alias) => loop(alias) @@ -2554,10 +2552,7 @@ object Types { case _ => true } - /** (1) Reduce a type-ref `W # X` or `W { ... } # U`, where `W` is a wildcard type - * to an (unbounded) wildcard type. - * - * (2) Reduce a type-ref `T { X = U; ... } # X` to `U` + /** Reduce a type-ref `T { X = U; ... } # X` to `U` * provided `U` does not refer with a RecThis to the * refinement type `T { X = U; ... }` */ @@ -2679,7 +2674,7 @@ object Types { case _ => } } - if (prefix.isInstanceOf[WildcardType]) WildcardType + if (prefix.isInstanceOf[WildcardType]) WildcardType.sameKindAs(this) else withPrefix(prefix) } @@ -5253,6 +5248,10 @@ object Types { else result def emptyPolyKind(using Context): TypeBounds = apply(defn.NothingType, defn.AnyKindType) + /** An interval covering all types of the same kind as `tp`. */ + def emptySameKindAs(tp: Type)(using Context): TypeBounds = + val top = tp.topType + if top.isExactlyAny then empty else apply(defn.NothingType, top) def upper(hi: Type)(using Context): TypeBounds = apply(defn.NothingType, hi) def lower(lo: Type)(using Context): TypeBounds = apply(lo, defn.AnyType) } @@ -5428,6 +5427,9 @@ object Types { else result else unique(CachedWildcardType(bounds)) + /** A wildcard matching any type of the same kind as `tp`. */ + def sameKindAs(tp: Type)(using Context): WildcardType = + apply(TypeBounds.emptySameKindAs(tp)) } /** An extractor for single abstract method types. diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 5f7221ee0ce9..d1dd83f36ff7 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -20,6 +20,7 @@ i13871.scala i15181.scala i15922.scala t5031_2.scala +i16997.scala # Tree is huge and blows stack for printing Text i7034.scala diff --git a/tests/pos/i16997.min.scala b/tests/pos/i16997.min.scala new file mode 100644 index 000000000000..abac648bdfd5 --- /dev/null +++ b/tests/pos/i16997.min.scala @@ -0,0 +1,12 @@ +class Fn: + class R[Y] + +case class Foo[F[_]](nest: Foo[F]): + case class Bar[G[_], R[_]](value: Foo[G]) + + def bar[G[_]](using fn: Fn): Bar[G, fn.R] = ??? + + def part[G[_]](using fn: Fn): Bar[G, fn.R] = + (bar[G], ()) match + case (Bar(value), ()) => + Bar(Foo(value)) diff --git a/tests/pos/i16997.scala b/tests/pos/i16997.scala new file mode 100644 index 000000000000..cd7b1ac8ab91 --- /dev/null +++ b/tests/pos/i16997.scala @@ -0,0 +1,63 @@ +class Funs { + sealed trait ->[A, B] +} + +/** + * Binary tree with leafs holding values of types `F[X]`, `F[Y]`, ... + * The complete structure of the tree is expressed by the type `A`, using the tags for branches and leafs. + * + * @tparam <*> tag for branches + * @tparam T tag for leafs. + * @tparam F value type of leafs. Each leaf holds a value of type `F[T]`, for some type `T`. + * @tparam A captures the complete structure of the tree + */ +enum Tree[<*>[_, _], T[_], F[_], A] { + case Branch[<*>[_, _], T[_], F[_], A, B]( + l: Tree[<*>, T, F, A], + r: Tree[<*>, T, F, B], + ) extends Tree[<*>, T, F, A <*> B] + + case Leaf[<*>[_, _], T[_], F[_], A]( + value: F[A], + ) extends Tree[<*>, T, F, T[A]] + + def <*>[B](that: Tree[<*>, T, F, B]): Tree[<*>, T, F, A <*> B] = + Branch(this, that) + + def partition[G[_], H[_]]( + f: [x] => F[x] => Either[G[x], H[x]], + )(using + funs: Funs, + ): Partitioned[G, H, funs.->] = + this match { + case Leaf(a) => + f(a) match + case Left(a) => Partitioned.Left(Leaf(a)) + case Right(a) => Partitioned.Right(Leaf(a)) + case Branch(l, r) => + import Partitioned.{Both, Left, Right} + import l.Partitioned.{Both => LBoth, Left => LLeft, Right => LRight} + import r.Partitioned.{Both => RBoth, Left => RLeft, Right => RRight} + + (l.partition(f), r.partition(f)) match + case (LLeft(lg), RLeft(rg)) => Left(lg <*> rg) + case (LLeft(lg), RRight(rh)) => Both(lg, rh) + case (LLeft(lg), RBoth(rg, rh)) => Both(lg <*> rg, rh) + case (LRight(lh), RLeft(rg)) => Both(rg, lh) + case (LRight(lh), RRight(rh)) => Right(lh <*> rh) + case (LRight(lh), RBoth(rg, rh)) => Both(rg, lh <*> rh) + case (LBoth(lg, lh), RLeft(rg)) => Both(lg <*> rg, lh) + case (LBoth(lg, lh), RRight(rh)) => Both(lg, lh <*> rh) + case (LBoth(lg, lh), RBoth(rg, rh)) => Both(lg <*> rg, lh <*> rh) + } + + // note that `->` is never even used, to keep this reproduction case small + enum Partitioned[G[_], H[_], ->[_, _]] { + case Left(value: Tree[<*>, T, G, A]) + case Right(value: Tree[<*>, T, H, A]) + case Both[G[_], H[_], X, Y, ->[_, _]]( + l: Tree[<*>, T, G, X], + r: Tree[<*>, T, H, Y], + ) extends Partitioned[G, H, ->] + } +} From 0f9a6724736acb3c19edf15b9615d189f3dfdfbf Mon Sep 17 00:00:00 2001 From: David Hua Date: Tue, 28 Feb 2023 22:41:07 -0500 Subject: [PATCH 212/657] Add missing cacheResult flags. --- compiler/src/dotty/tools/dotc/transform/init/Semantic.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 286e3a124d12..11376f427158 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -893,7 +893,7 @@ object Semantic: case Cold => Cold - case ref: Ref => eval(vdef.rhs, ref, enclosingClass) + case ref: Ref => eval(vdef.rhs, ref, enclosingClass, cacheResult = true) case _ => report.error("[Internal error] unexpected this value when accessing local variable, sym = " + sym.show + ", thisValue = " + thisValue2.show + Trace.show, Trace.position) @@ -989,7 +989,7 @@ object Semantic: val errors = Reporter.stopEarly { val res = { given Trace = Trace.empty - eval(body, thisV, klass) + eval(body, thisV, klass, cacheResult = true) } given Trace = Trace.empty.add(body) res.promote("The function return value is not hot. Found = " + res.show + ".") From 294fae8969fcb8e6bc46132234900c60822604b3 Mon Sep 17 00:00:00 2001 From: David Hua Date: Tue, 28 Feb 2023 23:42:51 -0500 Subject: [PATCH 213/657] Added method calls to clause positive clause interleaving init tests --- tests/init/pos/interleaving-overload.scala | 4 ++++ tests/init/pos/interleaving-params.scala | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/tests/init/pos/interleaving-overload.scala b/tests/init/pos/interleaving-overload.scala index 4d9214bf4d09..260b3538214a 100755 --- a/tests/init/pos/interleaving-overload.scala +++ b/tests/init/pos/interleaving-overload.scala @@ -7,6 +7,8 @@ class A{ f1(1) f1("hello") + f1[Boolean]("a")[Int] + f1[Boolean](1)[Int] case class B[U](x: Int) def b[U](x: Int) = B[U](x) @@ -17,4 +19,6 @@ class A{ f2[Any](1) f2[Any][Any](1) + b[Int](5) + } \ No newline at end of file diff --git a/tests/init/pos/interleaving-params.scala b/tests/init/pos/interleaving-params.scala index 08531a84e696..9f98b5f35d5b 100755 --- a/tests/init/pos/interleaving-params.scala +++ b/tests/init/pos/interleaving-params.scala @@ -1,3 +1,5 @@ +import scala.collection.mutable.AbstractSet +import scala.collection.mutable.BitSet import scala.language.experimental.clauseInterleaving class Params{ @@ -5,4 +7,13 @@ class Params{ def foo[T](x: T)[U >: x.type <: T](using U)[L <: List[U]](l: L): L = ??? def aaa(x: U): U = ??? def bbb[T <: U](x: U)[U]: U = ??? + + foo[AbstractSet[Int]](BitSet())[AbstractSet[Int]](using BitSet())[List[AbstractSet[Int]]](List[AbstractSet[Int]]()) +} + +class Param2 extends Params { + type U = AbstractSet[Int] + + aaa(BitSet()) + bbb[BitSet](BitSet())[AbstractSet[Int]] } \ No newline at end of file From 0f7c3abc3706b2054c48f3b16991741edb3a4610 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Tue, 20 Sep 2022 10:17:13 +0200 Subject: [PATCH 214/657] Implement individual erased parameters Breaking change for erasedDefinitions: this effectively makes the current `erased` marker in parameter list apply to only the first parameter. def f(erased a: int, b: int) should now be written as def f(erased a: int, erased b: int) type Function1 = (x: Int, erased y: Int) => Int type Function2 = (Int, erased Int) => Int Use refined traits for erased functions - Function types with erased parameters are now always `ErasedFunction` refined with the correct `apply` definition, for example: scala.runtime.ErasedFunction { def apply(x1: Int, erased x2: Int): Int } where ErasedFunction is an @experimental empty trait. - Polymorphic functions cannot take erased parameters. - By-name parameters cannot be erased. - Internally, use the @ErasedParam annotation as a marker for an erased parameter. - Parameters that are erased classes are now marked `erased` at Typer phase (with an annotation), and in later phases, they are not taken into account when considering erasedness. - Erased parameters/functions quotes API are changed: - `isErased` => `erasedArgs`/`erasedParams` and `hasErasedArgs`/`hasErasedParams` - `FunctionClass` now fails when `isErased = true`. Add `ErasedFunctionClass`. - Added tests and test-fixes for `erasedDefinitions` feature - Updated specs and internal syntax - Aside, reject normal tuples with ValDefs in them. This comes up when trying to parse parameters out of tuples. In practice they don't show up. Co-authored-by: Nicolas Stucki --- .../src/dotty/tools/dotc/ast/Desugar.scala | 29 +++- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 2 +- compiler/src/dotty/tools/dotc/ast/tpd.scala | 10 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 10 +- .../src/dotty/tools/dotc/cc/CaptureOps.scala | 1 - .../dotty/tools/dotc/cc/CheckCaptures.scala | 20 +-- compiler/src/dotty/tools/dotc/cc/Setup.scala | 16 +- .../dotty/tools/dotc/core/Definitions.scala | 124 ++++++++------- .../dotty/tools/dotc/core/Denotations.scala | 3 +- .../src/dotty/tools/dotc/core/NameOps.scala | 10 +- .../src/dotty/tools/dotc/core/NamerOps.scala | 8 +- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../tools/dotc/core/TypeApplications.scala | 10 ++ .../dotty/tools/dotc/core/TypeComparer.scala | 5 +- .../dotty/tools/dotc/core/TypeErasure.scala | 21 ++- .../src/dotty/tools/dotc/core/Types.scala | 62 ++++---- .../tools/dotc/core/tasty/TreePickler.scala | 1 - .../tools/dotc/core/tasty/TreeUnpickler.scala | 3 - .../dotty/tools/dotc/parsing/Parsers.scala | 147 ++++++++++++------ .../tools/dotc/printing/PlainPrinter.scala | 12 +- .../tools/dotc/printing/RefinedPrinter.scala | 30 ++-- .../dotty/tools/dotc/quoted/Interpreter.scala | 4 +- .../dotty/tools/dotc/transform/Bridges.scala | 7 +- .../transform/ContextFunctionResults.scala | 20 +-- .../tools/dotc/transform/ElimByName.scala | 10 +- .../dotty/tools/dotc/transform/Erasure.scala | 9 +- .../dotc/transform/GenericSignatures.scala | 4 +- .../tools/dotc/transform/PickleQuotes.scala | 2 +- .../tools/dotc/transform/PostTyper.scala | 24 +-- .../dotc/transform/PruneErasedDefs.scala | 8 +- .../dotc/transform/SpecializeFunctions.scala | 2 +- .../tools/dotc/transform/TreeChecker.scala | 4 +- .../dotty/tools/dotc/typer/Applications.scala | 6 +- .../dotty/tools/dotc/typer/EtaExpansion.scala | 5 +- .../dotty/tools/dotc/typer/ProtoTypes.scala | 4 +- .../dotty/tools/dotc/typer/Synthesizer.scala | 8 +- .../src/dotty/tools/dotc/typer/Typer.scala | 146 ++++++++++------- .../quoted/runtime/impl/QuotesImpl.scala | 18 ++- docs/_docs/internals/syntax.md | 16 +- .../experimental/erased-defs-spec.md | 20 +-- .../reference/experimental/erased-defs.md | 18 +-- library/src/scala/quoted/Quotes.scala | 25 ++- .../src/scala/runtime/ErasedFunction.scala | 11 ++ tests/neg-custom-args/erased/by-name.scala | 4 + .../erased/erased-in-tuples.scala | 16 ++ .../neg-custom-args/erased/lambda-infer.scala | 23 +++ .../erased/multiple-args-consume.scala | 13 ++ .../erased/multiple-args.scala | 11 ++ .../erased/poly-functions.scala | 16 ++ tests/neg/safeThrowsStrawman2.scala | 2 +- .../erased/erased-class-as-args.scala | 22 +++ .../erased/erased-soft-keyword.scala | 18 +++ tests/run-custom-args/erased/erased-15.scala | 4 +- tests/run-custom-args/erased/erased-27.check | 1 - tests/run-custom-args/erased/erased-28.check | 2 - .../erased/erased-class-are-erased.check | 1 + .../erased/erased-class-are-erased.scala | 14 ++ tests/run-custom-args/erased/lambdas.scala | 38 +++++ .../erased/quotes-add-erased.check | 1 + .../erased/quotes-add-erased/Macro_1.scala | 26 ++++ .../erased/quotes-add-erased/Test_2.scala | 12 ++ .../erased/quotes-reflection.check | 10 ++ .../erased/quotes-reflection/Macros_1.scala | 35 +++++ .../erased/quotes-reflection/Test_2.scala | 20 +++ .../run-macros-erased/macro-erased/1.scala | 4 +- .../stdlibExperimentalDefinitions.scala | 16 ++ tests/run-macros/i12021.check | 8 +- tests/run-macros/i12021/Macro_1.scala | 2 +- tests/run-macros/i12021/Test_2.scala | 8 +- tests/run-macros/tasty-definitions-1.check | 52 +------ .../tasty-definitions-1/quoted_1.scala | 7 +- 71 files changed, 879 insertions(+), 403 deletions(-) create mode 100644 library/src/scala/runtime/ErasedFunction.scala create mode 100644 tests/neg-custom-args/erased/by-name.scala create mode 100644 tests/neg-custom-args/erased/erased-in-tuples.scala create mode 100644 tests/neg-custom-args/erased/lambda-infer.scala create mode 100644 tests/neg-custom-args/erased/multiple-args-consume.scala create mode 100644 tests/neg-custom-args/erased/multiple-args.scala create mode 100644 tests/neg-custom-args/erased/poly-functions.scala create mode 100644 tests/pos-custom-args/erased/erased-class-as-args.scala create mode 100644 tests/pos-custom-args/erased/erased-soft-keyword.scala create mode 100644 tests/run-custom-args/erased/erased-class-are-erased.check create mode 100644 tests/run-custom-args/erased/erased-class-are-erased.scala create mode 100644 tests/run-custom-args/erased/lambdas.scala create mode 100644 tests/run-custom-args/erased/quotes-add-erased.check create mode 100644 tests/run-custom-args/erased/quotes-add-erased/Macro_1.scala create mode 100644 tests/run-custom-args/erased/quotes-add-erased/Test_2.scala create mode 100644 tests/run-custom-args/erased/quotes-reflection.check create mode 100644 tests/run-custom-args/erased/quotes-reflection/Macros_1.scala create mode 100644 tests/run-custom-args/erased/quotes-reflection/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 85dd5e6665c6..c1dd78451bae 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1498,10 +1498,10 @@ object desugar { case vd: ValDef => vd } - def makeContextualFunction(formals: List[Tree], body: Tree, isErased: Boolean)(using Context): Function = { - val mods = if (isErased) Given | Erased else Given + def makeContextualFunction(formals: List[Tree], body: Tree, erasedParams: List[Boolean])(using Context): Function = { + val mods = Given val params = makeImplicitParameters(formals, mods) - FunctionWithMods(params, body, Modifiers(mods)) + FunctionWithMods(params, body, Modifiers(mods), erasedParams) } private def derivedValDef(original: Tree, named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers)(using Context) = { @@ -1834,6 +1834,7 @@ object desugar { cpy.ByNameTypeTree(parent)(annotate(tpnme.retainsByName, restpt)) case _ => annotate(tpnme.retains, parent) + case f: FunctionWithMods if f.hasErasedParams => makeFunctionWithValDefs(f, pt) } desugared.withSpan(tree.span) } @@ -1909,6 +1910,28 @@ object desugar { TypeDef(tpnme.REFINE_CLASS, impl).withFlags(Trait) } + /** Ensure the given function tree use only ValDefs for parameters. + * For example, + * FunctionWithMods(List(TypeTree(A), TypeTree(B)), body, mods, erasedParams) + * gets converted to + * FunctionWithMods(List(ValDef(x$1, A), ValDef(x$2, B)), body, mods, erasedParams) + */ + def makeFunctionWithValDefs(tree: Function, pt: Type)(using Context): Function = { + val Function(args, result) = tree + args match { + case (_ : ValDef) :: _ => tree // ValDef case can be easily handled + case _ if !ctx.mode.is(Mode.Type) => tree + case _ => + val applyVParams = args.zipWithIndex.map { + case (p, n) => makeSyntheticParameter(n + 1, p) + } + tree match + case tree: FunctionWithMods => + untpd.FunctionWithMods(applyVParams, tree.body, tree.mods, tree.erasedParams) + case _ => untpd.Function(applyVParams, result) + } + } + /** Returns list of all pattern variables, possibly with their types, * without duplicates */ diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 9b55db600d3d..73f45bd7369b 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -960,7 +960,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => && tree.isTerm && { val qualType = tree.qualifier.tpe - hasRefinement(qualType) && !qualType.derivesFrom(defn.PolyFunctionClass) + hasRefinement(qualType) && !defn.isRefinedFunctionType(qualType) } def loop(tree: Tree): Boolean = tree match case TypeApply(fun, _) => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 5b0061b5d036..d1b1cdf607b5 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -260,12 +260,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { // If `isParamDependent == false`, the value of `previousParamRefs` is not used. if isParamDependent then mutable.ListBuffer[TermRef]() else (null: ListBuffer[TermRef] | Null).uncheckedNN - def valueParam(name: TermName, origInfo: Type): TermSymbol = + def valueParam(name: TermName, origInfo: Type, isErased: Boolean): TermSymbol = val maybeImplicit = if tp.isContextualMethod then Given else if tp.isImplicitMethod then Implicit else EmptyFlags - val maybeErased = if tp.isErasedMethod then Erased else EmptyFlags + val maybeErased = if isErased then Erased else EmptyFlags def makeSym(info: Type) = newSymbol(sym, name, TermParam | maybeImplicit | maybeErased, info, coord = sym.coord) @@ -283,7 +283,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { assert(vparams.hasSameLengthAs(tp.paramNames) && vparams.head.isTerm) (vparams.asInstanceOf[List[TermSymbol]], remaining1) case nil => - (tp.paramNames.lazyZip(tp.paramInfos).map(valueParam), Nil) + (tp.paramNames.lazyZip(tp.paramInfos).lazyZip(tp.erasedParams).map(valueParam), Nil) val (rtp, paramss) = recur(tp.instantiate(vparams.map(_.termRef)), remaining1) (rtp, vparams :: paramss) case _ => @@ -1140,10 +1140,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def etaExpandCFT(using Context): Tree = def expand(target: Tree, tp: Type)(using Context): Tree = tp match - case defn.ContextFunctionType(argTypes, resType, isErased) => + case defn.ContextFunctionType(argTypes, resType, _) => val anonFun = newAnonFun( ctx.owner, - MethodType.companion(isContextual = true, isErased = isErased)(argTypes, resType), + MethodType.companion(isContextual = true)(argTypes, resType), coord = ctx.owner.coord) def lambdaBody(refss: List[List[Tree]]) = expand(target.select(nme.apply).appliedToArgss(refss), resType)( diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index aeebb1f203e8..a262c3658399 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -76,9 +76,13 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def isType: Boolean = body.isType } - /** 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) + /** A function type or closure with `implicit` or `given` modifiers and information on which parameters are `erased` */ + class FunctionWithMods(args: List[Tree], body: Tree, val mods: Modifiers, val erasedParams: List[Boolean])(implicit @constructorOnly src: SourceFile) + extends Function(args, body) { + assert(args.length == erasedParams.length) + + def hasErasedParams = erasedParams.contains(true) + } /** A polymorphic function type */ case class PolyFunction(targs: List[Tree], body: Tree)(implicit @constructorOnly src: SourceFile) extends Tree { diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index e4533aa73ce0..decd428f5365 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -146,7 +146,6 @@ extension (tp: Type) defn.FunctionType( fname.functionArity, isContextual = fname.isContextFunction, - isErased = fname.isErasedFunction, isImpure = true).appliedTo(args) case _ => tp diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 77363a165f64..f9401a0c509f 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -336,8 +336,8 @@ class CheckCaptures extends Recheck, SymTransformer: mapArgUsing(_.forceBoxStatus(false)) else if meth == defn.Caps_unsafeBoxFunArg then mapArgUsing { - case defn.FunctionOf(paramtpe :: Nil, restpe, isContectual, isErased) => - defn.FunctionOf(paramtpe.forceBoxStatus(true) :: Nil, restpe, isContectual, isErased) + case defn.FunctionOf(paramtpe :: Nil, restpe, isContectual) => + defn.FunctionOf(paramtpe.forceBoxStatus(true) :: Nil, restpe, isContectual) } else super.recheckApply(tree, pt) match @@ -430,7 +430,7 @@ class CheckCaptures extends Recheck, SymTransformer: block match case closureDef(mdef) => pt.dealias match - case defn.FunctionOf(ptformals, _, _, _) + case defn.FunctionOf(ptformals, _, _) if ptformals.nonEmpty && ptformals.forall(_.captureSet.isAlwaysEmpty) => // Redo setup of the anonymous function so that formal parameters don't // get capture sets. This is important to avoid false widenings to `*` @@ -598,8 +598,8 @@ class CheckCaptures extends Recheck, SymTransformer: //println(i"check conforms $actual1 <<< $expected1") super.checkConformsExpr(actual1, expected1, tree) - private def toDepFun(args: List[Type], resultType: Type, isContextual: Boolean, isErased: Boolean)(using Context): Type = - MethodType.companion(isContextual = isContextual, isErased = isErased)(args, resultType) + private def toDepFun(args: List[Type], resultType: Type, isContextual: Boolean)(using Context): Type = + MethodType.companion(isContextual = isContextual)(args, resultType) .toFunctionType(isJava = false, alwaysDependent = true) /** Turn `expected` into a dependent function when `actual` is dependent. */ @@ -607,9 +607,9 @@ class CheckCaptures extends Recheck, SymTransformer: def recur(expected: Type): Type = expected.dealias match case expected @ CapturingType(eparent, refs) => CapturingType(recur(eparent), refs, boxed = expected.isBoxed) - case expected @ defn.FunctionOf(args, resultType, isContextual, isErased) + case expected @ defn.FunctionOf(args, resultType, isContextual) if defn.isNonRefinedFunction(expected) && defn.isFunctionType(actual) && !defn.isNonRefinedFunction(actual) => - val expected1 = toDepFun(args, resultType, isContextual, isErased) + val expected1 = toDepFun(args, resultType, isContextual) expected1 case _ => expected @@ -675,7 +675,7 @@ class CheckCaptures extends Recheck, SymTransformer: try val (eargs, eres) = expected.dealias.stripCapturing match - case defn.FunctionOf(eargs, eres, _, _) => (eargs, eres) + case defn.FunctionOf(eargs, eres, _) => (eargs, eres) case expected: MethodType => (expected.paramInfos, expected.resType) case expected @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(expected) => (rinfo.paramInfos, rinfo.resType) case _ => (aargs.map(_ => WildcardType), WildcardType) @@ -739,7 +739,7 @@ class CheckCaptures extends Recheck, SymTransformer: case actual @ AppliedType(tycon, args) if defn.isNonRefinedFunction(actual) => adaptFun(actual, args.init, args.last, expected, covariant, insertBox, (aargs1, ares1) => actual.derivedAppliedType(tycon, aargs1 :+ ares1)) - case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionType(actual) => + case actual @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionOrPolyType(actual) => // TODO Find a way to combine handling of generic and dependent function types (here and elsewhere) adaptFun(actual, rinfo.paramInfos, rinfo.resType, expected, covariant, insertBox, (aargs1, ares1) => @@ -962,7 +962,7 @@ class CheckCaptures extends Recheck, SymTransformer: case CapturingType(parent, refs) => healCaptureSet(refs) traverse(parent) - case tp @ RefinedType(parent, rname, rinfo: MethodType) if defn.isFunctionType(tp) => + case tp @ RefinedType(parent, rname, rinfo: MethodType) if defn.isFunctionOrPolyType(tp) => traverse(rinfo) case tp: TermLambda => val saved = allowed diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 461c18ea0980..5642ea99de1a 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -12,6 +12,7 @@ import transform.Recheck.* import CaptureSet.IdentityCaptRefMap import Synthetics.isExcluded import util.Property +import dotty.tools.dotc.core.Annotations.Annotation /** A tree traverser that prepares a compilation unit to be capture checked. * It does the following: @@ -38,7 +39,6 @@ extends tpd.TreeTraverser: private def depFun(tycon: Type, argTypes: List[Type], resType: Type)(using Context): Type = MethodType.companion( isContextual = defn.isContextFunctionClass(tycon.classSymbol), - isErased = defn.isErasedFunctionClass(tycon.classSymbol) )(argTypes, resType) .toFunctionType(isJava = false, alwaysDependent = true) @@ -54,7 +54,7 @@ extends tpd.TreeTraverser: val boxedRes = recur(res) if boxedRes eq res then tp else tp1.derivedAppliedType(tycon, args.init :+ boxedRes) - case tp1 @ RefinedType(_, _, rinfo) if defn.isFunctionType(tp1) => + case tp1 @ RefinedType(_, _, rinfo: MethodType) if defn.isFunctionOrPolyType(tp1) => val boxedRinfo = recur(rinfo) if boxedRinfo eq rinfo then tp else boxedRinfo.toFunctionType(isJava = false, alwaysDependent = true) @@ -231,7 +231,7 @@ extends tpd.TreeTraverser: tp.derivedAppliedType(tycon1, args1 :+ res1) else tp.derivedAppliedType(tycon1, args.mapConserve(arg => this(arg))) - case tp @ RefinedType(core, rname, rinfo) if defn.isFunctionType(tp) => + case tp @ RefinedType(core, rname, rinfo: MethodType) if defn.isFunctionOrPolyType(tp) => val rinfo1 = apply(rinfo) if rinfo1 ne rinfo then rinfo1.toFunctionType(isJava = false, alwaysDependent = true) else tp @@ -260,7 +260,13 @@ extends tpd.TreeTraverser: private def expandThrowsAlias(tp: Type)(using Context) = tp match case AppliedType(tycon, res :: exc :: Nil) if tycon.typeSymbol == defn.throwsAlias => // hard-coded expansion since $throws aliases in stdlib are defined with `?=>` rather than `?->` - defn.FunctionOf(defn.CanThrowClass.typeRef.appliedTo(exc) :: Nil, res, isContextual = true, isErased = true) + defn.FunctionOf( + AnnotatedType( + defn.CanThrowClass.typeRef.appliedTo(exc), + Annotation(defn.ErasedParamAnnot, defn.CanThrowClass.span)) :: Nil, + res, + isContextual = true + ) case _ => tp private def expandThrowsAliases(using Context) = new TypeMap: @@ -323,7 +329,7 @@ extends tpd.TreeTraverser: args.last, CaptureSet.empty, currentCs ++ outerCs) tp.derivedAppliedType(tycon1, args1 :+ resType1) tp1.capturing(outerCs) - case tp @ RefinedType(parent, nme.apply, rinfo: MethodType) if defn.isFunctionType(tp) => + case tp @ RefinedType(parent, nme.apply, rinfo: MethodType) if defn.isFunctionOrPolyType(tp) => propagateDepFunctionResult(mapOver(tp), currentCs ++ outerCs) .capturing(outerCs) case _ => diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 8c3f2ad89ca1..20a1d59316eb 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -86,7 +86,7 @@ class Definitions { newPermanentClassSymbol(ScalaPackageClass, name, Artifact, completer).entered } - /** The trait FunctionN, ContextFunctionN, ErasedFunctionN or ErasedContextFunction, for some N + /** The trait FunctionN and ContextFunctionN for some N * @param name The name of the trait to be created * * FunctionN traits follow this template: @@ -104,21 +104,6 @@ class Definitions { * trait ContextFunctionN[-T0,...,-T{N-1}, +R] extends Object { * def apply(using $x0: T0, ..., $x{N_1}: T{N-1}): R * } - * - * ErasedFunctionN traits follow this template: - * - * trait ErasedFunctionN[-T0,...,-T{N-1}, +R] extends Object { - * def apply(erased $x0: T0, ..., $x{N_1}: T{N-1}): R - * } - * - * ErasedContextFunctionN traits follow this template: - * - * trait ErasedContextFunctionN[-T0,...,-T{N-1}, +R] extends Object { - * def apply(using erased $x0: T0, ..., $x{N_1}: T{N-1}): R - * } - * - * ErasedFunctionN and ErasedContextFunctionN erase to Function0. - * * ImpureXYZFunctionN follow this template: * * type ImpureXYZFunctionN[-T0,...,-T{N-1}, +R] = {*} XYZFunctionN[T0,...,T{N-1}, R] @@ -149,8 +134,7 @@ class Definitions { val resParamRef = enterTypeParam(cls, paramNamePrefix ++ "R", Covariant, decls).typeRef val methodType = MethodType.companion( isContextual = name.isContextFunction, - isImplicit = false, - isErased = name.isErasedFunction) + isImplicit = false) decls.enter(newMethod(cls, nme.apply, methodType(argParamRefs, resParamRef), Deferred)) denot.info = ClassInfo(ScalaPackageClass.thisType, cls, ObjectType :: Nil, decls) @@ -1109,15 +1093,23 @@ class Definitions { sym.owner.linkedClass.typeRef object FunctionOf { - def apply(args: List[Type], resultType: Type, isContextual: Boolean = false, isErased: Boolean = false)(using Context): Type = - FunctionType(args.length, isContextual, isErased).appliedTo(args ::: resultType :: Nil) - def unapply(ft: Type)(using Context): Option[(List[Type], Type, Boolean, Boolean)] = { - val tsym = ft.typeSymbol - if isFunctionClass(tsym) && ft.isRef(tsym) then - val targs = ft.dealias.argInfos - if (targs.isEmpty) None - else Some(targs.init, targs.last, tsym.name.isContextFunction, tsym.name.isErasedFunction) - else None + def apply(args: List[Type], resultType: Type, isContextual: Boolean = false)(using Context): Type = + val mt = MethodType.companion(isContextual, false)(args, resultType) + if mt.hasErasedParams then + RefinedType(ErasedFunctionClass.typeRef, nme.apply, mt) + else + FunctionType(args.length, isContextual).appliedTo(args ::: resultType :: Nil) + def unapply(ft: Type)(using Context): Option[(List[Type], Type, Boolean)] = { + ft.dealias match + case RefinedType(parent, nme.apply, mt: MethodType) if isErasedFunctionType(parent) => + Some(mt.paramInfos, mt.resType, mt.isContextualMethod) + case _ => + val tsym = ft.dealias.typeSymbol + if isFunctionSymbol(tsym) && ft.isRef(tsym) then + val targs = ft.dealias.argInfos + if (targs.isEmpty) None + else Some(targs.init, targs.last, tsym.name.isContextFunction) + else None } } @@ -1436,24 +1428,22 @@ class Definitions { classRefs(n).nn end FunType - private def funTypeIdx(isContextual: Boolean, isErased: Boolean, isImpure: Boolean): Int = + private def funTypeIdx(isContextual: Boolean, isImpure: Boolean): Int = (if isContextual then 1 else 0) - + (if isErased then 2 else 0) - + (if isImpure then 4 else 0) + + (if isImpure then 2 else 0) private val funTypeArray: IArray[FunType] = val arr = Array.ofDim[FunType](8) val choices = List(false, true) - for contxt <- choices; erasd <- choices; impure <- choices do + for contxt <- choices; impure <- choices do var str = "Function" if contxt then str = "Context" + str - if erasd then str = "Erased" + str if impure then str = "Impure" + str - arr(funTypeIdx(contxt, erasd, impure)) = FunType(str) + arr(funTypeIdx(contxt, impure)) = FunType(str) IArray.unsafeFromArray(arr) - def FunctionSymbol(n: Int, isContextual: Boolean = false, isErased: Boolean = false, isImpure: Boolean = false)(using Context): Symbol = - funTypeArray(funTypeIdx(isContextual, isErased, isImpure))(n).symbol + def FunctionSymbol(n: Int, isContextual: Boolean = false, isImpure: Boolean = false)(using Context): Symbol = + funTypeArray(funTypeIdx(isContextual, isImpure))(n).symbol @tu lazy val Function0_apply: Symbol = Function0.requiredMethod(nme.apply) @tu lazy val ContextFunction0_apply: Symbol = ContextFunction0.requiredMethod(nme.apply) @@ -1463,12 +1453,14 @@ class Definitions { @tu lazy val Function2: Symbol = FunctionSymbol(2) @tu lazy val ContextFunction0: Symbol = FunctionSymbol(0, isContextual = true) - def FunctionType(n: Int, isContextual: Boolean = false, isErased: Boolean = false, isImpure: Boolean = false)(using Context): TypeRef = - FunctionSymbol(n, isContextual && !ctx.erasedTypes, isErased, isImpure).typeRef + def FunctionType(n: Int, isContextual: Boolean = false, isImpure: Boolean = false)(using Context): TypeRef = + FunctionSymbol(n, isContextual && !ctx.erasedTypes, isImpure).typeRef lazy val PolyFunctionClass = requiredClass("scala.PolyFunction") def PolyFunctionType = PolyFunctionClass.typeRef + lazy val ErasedFunctionClass = requiredClass("scala.runtime.ErasedFunction") + /** If `cls` is a class in the scala package, its name, otherwise EmptyTypeName */ def scalaClassName(cls: Symbol)(using Context): TypeName = cls.denot match case clsd: ClassDenotation if clsd.owner eq ScalaPackageClass => @@ -1501,8 +1493,6 @@ class Definitions { * - FunctionXXL * - FunctionN for N >= 0 * - ContextFunctionN for N >= 0 - * - ErasedFunctionN for N > 0 - * - ErasedContextFunctionN for N > 0 */ def isFunctionClass(cls: Symbol): Boolean = scalaClassName(cls).isFunction @@ -1521,12 +1511,6 @@ class Definitions { */ def isContextFunctionClass(cls: Symbol): Boolean = scalaClassName(cls).isContextFunction - /** Is an erased function class. - * - ErasedFunctionN for N > 0 - * - ErasedContextFunctionN for N > 0 - */ - def isErasedFunctionClass(cls: Symbol): Boolean = scalaClassName(cls).isErasedFunction - /** Is either FunctionXXL or a class that will be erased to FunctionXXL * - FunctionXXL * - FunctionN for N >= 22 @@ -1563,8 +1547,7 @@ class Definitions { */ def functionTypeErasure(cls: Symbol): Type = val arity = scalaClassName(cls).functionArity - if cls.name.isErasedFunction then FunctionType(0) - else if arity > 22 then FunctionXXLClass.typeRef + if arity > 22 then FunctionXXLClass.typeRef else if arity >= 0 then FunctionType(arity) else NoType @@ -1704,16 +1687,29 @@ class Definitions { arity >= 0 && isFunctionClass(sym) && tp.isRef( - FunctionType(arity, sym.name.isContextFunction, sym.name.isErasedFunction).typeSymbol, + FunctionType(arity, sym.name.isContextFunction).typeSymbol, skipRefined = false) end isNonRefinedFunction - /** Is `tp` a representation of a (possibly dependent) function type or an alias of such? */ + /** Returns whether `tp` is an instance or a refined instance of: + * - scala.FunctionN + * - scala.ContextFunctionN + */ def isFunctionType(tp: Type)(using Context): Boolean = isNonRefinedFunction(tp.dropDependentRefinement) + /** Is `tp` a specialized, refined function type? Either an `ErasedFunction` or a `PolyFunction`. */ + def isRefinedFunctionType(tp: Type)(using Context): Boolean = + tp.derivesFrom(defn.PolyFunctionClass) || isErasedFunctionType(tp) + + /** Returns whether `tp` is an instance or a refined instance of: + * - scala.FunctionN + * - scala.ContextFunctionN + * - ErasedFunction + * - PolyFunction + */ def isFunctionOrPolyType(tp: Type)(using Context): Boolean = - isFunctionType(tp) || (tp.typeSymbol eq defn.PolyFunctionClass) + isFunctionType(tp) || isRefinedFunctionType(tp) private def withSpecMethods(cls: ClassSymbol, bases: List[Name], paramTypes: Set[TypeRef]) = for base <- bases; tp <- paramTypes do @@ -1802,7 +1798,7 @@ class Definitions { @tu lazy val FunctionSpecializedApplyNames: collection.Set[Name] = Function0SpecializedApplyNames ++ Function1SpecializedApplyNames ++ Function2SpecializedApplyNames - def functionArity(tp: Type)(using Context): Int = tp.dropDependentRefinement.dealias.argInfos.length - 1 + def functionArity(tp: Type)(using Context): Int = tp.functionArgInfos.length - 1 /** Return underlying context function type (i.e. instance of an ContextFunctionN class) * or NoType if none exists. The following types are considered as underlying types: @@ -1814,6 +1810,8 @@ class Definitions { tp.stripTypeVar.dealias match case tp1: TypeParamRef if ctx.typerState.constraint.contains(tp1) => asContextFunctionType(TypeComparer.bounds(tp1).hiBound) + case tp1 @ RefinedType(parent, nme.apply, mt: MethodType) if isErasedFunctionType(parent) && mt.isContextualMethod => + tp1 case tp1 => if tp1.typeSymbol.name.isContextFunction && isFunctionType(tp1) then tp1 else NoType @@ -1827,18 +1825,28 @@ class Definitions { * types `As`, the result type `B` and a whether the type is an erased context function. */ object ContextFunctionType: - def unapply(tp: Type)(using Context): Option[(List[Type], Type, Boolean)] = + def unapply(tp: Type)(using Context): Option[(List[Type], Type, List[Boolean])] = if ctx.erasedTypes then atPhase(erasurePhase)(unapply(tp)) else - val tp1 = asContextFunctionType(tp) - if tp1.exists then - val args = tp1.dropDependentRefinement.argInfos - Some((args.init, args.last, tp1.typeSymbol.name.isErasedFunction)) - else None + asContextFunctionType(tp) match + case RefinedType(parent, nme.apply, mt: MethodType) if isErasedFunctionType(parent) => + Some((mt.paramInfos, mt.resType, mt.erasedParams)) + case tp1 if tp1.exists => + val args = tp1.functionArgInfos + val erasedParams = erasedFunctionParameters(tp1) + Some((args.init, args.last, erasedParams)) + case _ => None + + /* Returns a list of erased booleans marking whether parameters are erased, for a function type. */ + def erasedFunctionParameters(tp: Type)(using Context): List[Boolean] = tp.dealias match { + case RefinedType(parent, nme.apply, mt: MethodType) => mt.erasedParams + case tp if isFunctionType(tp) => List.fill(functionArity(tp)) { false } + case _ => Nil + } def isErasedFunctionType(tp: Type)(using Context): Boolean = - tp.dealias.typeSymbol.name.isErasedFunction && isFunctionType(tp) + tp.derivesFrom(defn.ErasedFunctionClass) /** A whitelist of Scala-2 classes that are known to be pure */ def isAssuredNoInits(sym: Symbol): Boolean = diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 723f9408d805..82368fd4dbf5 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -545,8 +545,7 @@ object Denotations { tp2 match case tp2: MethodType if TypeComparer.matchingMethodParams(tp1, tp2) - && tp1.isImplicitMethod == tp2.isImplicitMethod - && tp1.isErasedMethod == tp2.isErasedMethod => + && tp1.isImplicitMethod == tp2.isImplicitMethod => val resType = infoMeet(tp1.resType, tp2.resType.subst(tp2, tp1), safeIntersection) if resType.exists then tp1.derivedLambdaType(mergeParamNames(tp1, tp2), tp1.paramInfos, resType) diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index 4e075953d7fa..04440c9e9b39 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -214,7 +214,7 @@ object NameOps { if str == mustHave then found = true idx + str.length else idx - skip(skip(skip(0, "Impure"), "Erased"), "Context") == suffixStart + skip(skip(0, "Impure"), "Context") == suffixStart && found } @@ -225,10 +225,11 @@ object NameOps { private def checkedFunArity(suffixStart: Int)(using Context): Int = if isFunctionPrefix(suffixStart) then funArity(suffixStart) else -1 - /** Is a function name, i.e one of FunctionXXL, FunctionN, ContextFunctionN, ErasedFunctionN, ErasedContextFunctionN for N >= 0 + /** Is a function name, i.e one of FunctionXXL, FunctionN, ContextFunctionN, ImpureFunctionN, ImpureContextFunctionN for N >= 0 */ def isFunction(using Context): Boolean = - (name eq tpnme.FunctionXXL) || checkedFunArity(functionSuffixStart) >= 0 + (name eq tpnme.FunctionXXL) + || checkedFunArity(functionSuffixStart) >= 0 /** Is a function name * - FunctionN for N >= 0 @@ -241,14 +242,11 @@ object NameOps { isFunctionPrefix(suffixStart, mustHave) && funArity(suffixStart) >= 0 def isContextFunction(using Context): Boolean = isSpecificFunction("Context") - def isErasedFunction(using Context): Boolean = isSpecificFunction("Erased") def isImpureFunction(using Context): Boolean = isSpecificFunction("Impure") /** Is a synthetic function name, i.e. one of * - FunctionN for N > 22 * - ContextFunctionN for N >= 0 - * - ErasedFunctionN for N >= 0 - * - ErasedContextFunctionN for N >= 0 */ def isSyntheticFunction(using Context): Boolean = val suffixStart = functionSuffixStart diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index db6f72590818..dc09edd79781 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -42,10 +42,10 @@ object NamerOps: case Nil => resultType case TermSymbols(params) :: paramss1 => - val (isContextual, isImplicit, isErased) = - if params.isEmpty then (false, false, false) - else (params.head.is(Given), params.head.is(Implicit), params.head.is(Erased)) - val make = MethodType.companion(isContextual = isContextual, isImplicit = isImplicit, isErased = isErased) + val (isContextual, isImplicit) = + if params.isEmpty then (false, false) + else (params.head.is(Given), params.head.is(Implicit)) + val make = MethodType.companion(isContextual = isContextual, isImplicit = isImplicit) if isJava then for param <- params do if param.info.isDirectRef(defn.ObjectClass) then param.info = defn.AnyType diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 92f2e55a49bf..27e97a92b48e 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -212,6 +212,7 @@ object StdNames { final val Throwable: N = "Throwable" final val IOOBException: N = "IndexOutOfBoundsException" final val FunctionXXL: N = "FunctionXXL" + final val ErasedFunction: N = "ErasedFunction" final val Abs: N = "Abs" final val And: N = "&&" diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 7c25ecd21ebf..2e8aee4df96c 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -9,9 +9,11 @@ import SymDenotations.LazyType import Decorators._ import util.Stats._ import Names._ +import StdNames.nme import Flags.{Module, Provisional} import dotty.tools.dotc.config.Config import cc.boxedUnlessFun +import dotty.tools.dotc.transform.TypeUtils.isErasedValueType object TypeApplications { @@ -503,6 +505,14 @@ class TypeApplications(val self: Type) extends AnyVal { case AppliedType(tycon, args) => args.boxedUnlessFun(tycon) case _ => Nil + /** If this is an encoding of a function type, return its arguments, otherwise return Nil. + * Handles `ErasedFunction`s and poly functions gracefully. + */ + final def functionArgInfos(using Context): List[Type] = self.dealias match + case RefinedType(parent, nme.apply, mt: MethodType) if defn.isErasedFunctionType(parent) => (mt.paramInfos :+ mt.resultType) + case RefinedType(parent, nme.apply, mt: MethodType) if parent.typeSymbol eq defn.PolyFunctionClass => (mt.paramInfos :+ mt.resultType) + case _ => self.dropDependentRefinement.dealias.argInfos + /** Argument types where existential types in arguments are disallowed */ def argTypes(using Context): List[Type] = argInfos mapConserve noBounds diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 9e8d18765352..f097bd160fdd 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2119,7 +2119,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling case nil => formals2.isEmpty } - loop(tp1.paramInfos, tp2.paramInfos) + // If methods have erased parameters, then the erased parameters must match + val erasedValid = (!tp1.hasErasedParams && !tp2.hasErasedParams) || (tp1.erasedParams == tp2.erasedParams) + + erasedValid && loop(tp1.paramInfos, tp2.paramInfos) } /** Do the parameter types of `tp1` and `tp2` match in a way that allows `tp1` diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 67839d10c8cd..9bcb3eca36bb 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -536,7 +536,14 @@ object TypeErasure { val paramss = res.paramNamess assert(paramss.length == 1) erasure(defn.FunctionType(paramss.head.length, - isContextual = res.isImplicitMethod, isErased = res.isErasedMethod)) + isContextual = res.isImplicitMethod)) + + def eraseErasedFunctionApply(erasedFn: MethodType)(using Context): Type = + val fnType = defn.FunctionType( + n = erasedFn.erasedParams.count(_ == false), + isContextual = erasedFn.isContextualMethod, + ) + erasure(fnType) } import TypeErasure._ @@ -613,6 +620,8 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst defn.FunctionType(0) case RefinedType(parent, nme.apply, refinedInfo) if parent.typeSymbol eq defn.PolyFunctionClass => erasePolyFunctionApply(refinedInfo) + case RefinedType(parent, nme.apply, refinedInfo: MethodType) if defn.isErasedFunctionType(parent) => + eraseErasedFunctionApply(refinedInfo) case tp: TypeProxy => this(tp.underlying) case tp @ AndType(tp1, tp2) => @@ -639,7 +648,13 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst case tp: MethodType => def paramErasure(tpToErase: Type) = erasureFn(sourceLanguage, semiEraseVCs, isConstructor, isSymbol, wildcardOK)(tpToErase) - val (names, formals0) = if (tp.isErasedMethod) (Nil, Nil) else (tp.paramNames, tp.paramInfos) + val (names, formals0) = if tp.hasErasedParams then + tp.paramNames + .zip(tp.paramInfos) + .zip(tp.erasedParams) + .collect{ case (param, isErased) if !isErased => param } + .unzip + else (tp.paramNames, tp.paramInfos) val formals = formals0.mapConserve(paramErasure) eraseResult(tp.resultType) match { case rt: MethodType => @@ -871,6 +886,8 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst // because RefinedTypes <: TypeProxy and it would be caught by // the case immediately below sigName(this(tp)) + case tp @ RefinedType(parent, nme.apply, refinedInfo) if defn.isErasedFunctionType(parent) => + sigName(this(tp)) case tp: TypeProxy => sigName(tp.underlying) case tp: WildcardType => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c05bb164834d..c660ef657b13 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -43,6 +43,7 @@ import scala.annotation.internal.sharable import scala.annotation.threadUnsafe import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.transform.TypeUtils.isErasedClass object Types { @@ -425,7 +426,7 @@ object Types { def isContextualMethod: Boolean = false /** Is this a MethodType for which the parameters will not be used? */ - def isErasedMethod: Boolean = false + def hasErasedParams(using Context): Boolean = false /** Is this a match type or a higher-kinded abstraction of one? */ @@ -1180,7 +1181,8 @@ object Types { /** Remove all AnnotatedTypes wrapping this type. */ - def stripAnnots(using Context): Type = this + def stripAnnots(keep: Annotation => Context ?=> Boolean)(using Context): Type = this + final def stripAnnots(using Context): Type = stripAnnots(_ => false) /** Strip TypeVars and Annotation and CapturingType wrappers */ def stripped(using Context): Type = this @@ -1470,7 +1472,7 @@ object Types { /** Dealias, and if result is a dependent function type, drop the `apply` refinement. */ final def dropDependentRefinement(using Context): Type = dealias match { - case RefinedType(parent, nme.apply, _) => parent + case RefinedType(parent, nme.apply, mt) if defn.isNonRefinedFunction(parent) => parent case tp => tp } @@ -1712,6 +1714,8 @@ object Types { else NoType case t if defn.isNonRefinedFunction(t) => t + case t if defn.isErasedFunctionType(t) => + t case t @ SAMType(_) => t case _ => @@ -1839,15 +1843,15 @@ object Types { case mt: MethodType if !mt.isParamDependent => val formals1 = if (dropLast == 0) mt.paramInfos else mt.paramInfos dropRight dropLast val isContextual = mt.isContextualMethod && !ctx.erasedTypes - val isErased = mt.isErasedMethod && !ctx.erasedTypes val result1 = mt.nonDependentResultApprox match { case res: MethodType => res.toFunctionType(isJava) case res => res } val funType = defn.FunctionOf( formals1 mapConserve (_.translateFromRepeated(toArray = isJava)), - result1, isContextual, isErased) - if alwaysDependent || mt.isResultDependent then RefinedType(funType, nme.apply, mt) + result1, isContextual) + if alwaysDependent || mt.isResultDependent then + RefinedType(funType, nme.apply, mt) else funType } @@ -3648,6 +3652,8 @@ object Types { def companion: LambdaTypeCompanion[ThisName, PInfo, This] + def erasedParams(using Context) = List.fill(paramInfos.size)(false) + /** The type `[tparams := paramRefs] tp`, where `tparams` can be * either a list of type parameter symbols or a list of lambda parameters * @@ -3725,7 +3731,11 @@ object Types { else Signature(tp, sourceLanguage) this match case tp: MethodType => - val params = if (isErasedMethod) Nil else tp.paramInfos + val params = if (hasErasedParams) + tp.paramInfos + .zip(tp.erasedParams) + .collect { case (param, isErased) if !isErased => param } + else tp.paramInfos resultSignature.prependTermParams(params, sourceLanguage) case tp: PolyType => resultSignature.prependTypeParams(tp.paramNames.length) @@ -3932,16 +3942,14 @@ object Types { def companion: MethodTypeCompanion final override def isImplicitMethod: Boolean = - companion.eq(ImplicitMethodType) || - companion.eq(ErasedImplicitMethodType) || - isContextualMethod - final override def isErasedMethod: Boolean = - companion.eq(ErasedMethodType) || - companion.eq(ErasedImplicitMethodType) || - companion.eq(ErasedContextualMethodType) + companion.eq(ImplicitMethodType) || isContextualMethod + final override def hasErasedParams(using Context): Boolean = + erasedParams.contains(true) final override def isContextualMethod: Boolean = - companion.eq(ContextualMethodType) || - companion.eq(ErasedContextualMethodType) + companion.eq(ContextualMethodType) + + override def erasedParams(using Context): List[Boolean] = + paramInfos.map(p => p.hasAnnotation(defn.ErasedParamAnnot)) protected def prefixString: String = companion.prefixString } @@ -4038,7 +4046,7 @@ object Types { tl => tl.integrate(params, resultType)) end fromSymbols - final def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(using Context): MethodType = + def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(using Context): MethodType = checkValid(unique(new CachedMethodType(paramNames)(paramInfosExp, resultTypeExp, self))) def checkValid(mt: MethodType)(using Context): mt.type = { @@ -4053,19 +4061,14 @@ object Types { } object MethodType extends MethodTypeCompanion("MethodType") { - def companion(isContextual: Boolean = false, isImplicit: Boolean = false, isErased: Boolean = false): MethodTypeCompanion = - if (isContextual) - if (isErased) ErasedContextualMethodType else ContextualMethodType - else if (isImplicit) - if (isErased) ErasedImplicitMethodType else ImplicitMethodType - else - if (isErased) ErasedMethodType else MethodType + def companion(isContextual: Boolean = false, isImplicit: Boolean = false): MethodTypeCompanion = + if (isContextual) ContextualMethodType + else if (isImplicit) ImplicitMethodType + else MethodType } - object ErasedMethodType extends MethodTypeCompanion("ErasedMethodType") + object ContextualMethodType extends MethodTypeCompanion("ContextualMethodType") - object ErasedContextualMethodType extends MethodTypeCompanion("ErasedContextualMethodType") object ImplicitMethodType extends MethodTypeCompanion("ImplicitMethodType") - object ErasedImplicitMethodType extends MethodTypeCompanion("ErasedImplicitMethodType") /** A ternary extractor for MethodType */ object MethodTpe { @@ -5280,7 +5283,10 @@ object Types { override def stripTypeVar(using Context): Type = derivedAnnotatedType(parent.stripTypeVar, annot) - override def stripAnnots(using Context): Type = parent.stripAnnots + override def stripAnnots(keep: Annotation => (Context) ?=> Boolean)(using Context): Type = + val p = parent.stripAnnots(keep) + if keep(annot) then derivedAnnotatedType(p, annot) + else p override def stripped(using Context): Type = parent.stripped diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index bef28545592a..8a396921f32b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -287,7 +287,6 @@ class TreePickler(pickler: TastyPickler) { var mods = EmptyFlags if tpe.isContextualMethod then mods |= Given else if tpe.isImplicitMethod then mods |= Implicit - if tpe.isErasedMethod then mods |= Erased pickleMethodic(METHODtype, tpe, mods) case tpe: ParamRef => assert(pickleParamRef(tpe), s"orphan parameter reference: $tpe") diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index dfe04dbe6d2b..9078a8959112 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -249,7 +249,6 @@ class TreeUnpickler(reader: TastyReader, while currentAddr != end do // avoid boxing the mods readByte() match case IMPLICIT => mods |= Implicit - case ERASED => mods |= Erased case GIVEN => mods |= Given (names, mods) @@ -406,9 +405,7 @@ class TreeUnpickler(reader: TastyReader, case METHODtype => def methodTypeCompanion(mods: FlagSet): MethodTypeCompanion = if mods.is(Implicit) then ImplicitMethodType - else if mods.isAllOf(Erased | Given) then ErasedContextualMethodType else if mods.is(Given) then ContextualMethodType - else if mods.is(Erased) then ErasedMethodType else MethodType readMethodic(methodTypeCompanion, _.toTermName) case TYPELAMBDAtype => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 479ae1fa9095..d1b0c6cba097 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -190,6 +190,8 @@ object Parsers { def isPureArrow(name: Name): Boolean = isIdent(name) && Feature.pureFunsEnabled def isPureArrow: Boolean = isPureArrow(nme.PUREARROW) || isPureArrow(nme.PURECTXARROW) def isErased = isIdent(nme.erased) && in.erasedEnabled + // Are we seeing an `erased` soft keyword that will not be an identifier? + def isErasedKw = isErased && in.isSoftModifierInParamModifierPosition def isSimpleLiteral = simpleLiteralTokens.contains(in.token) || isIdent(nme.raw.MINUS) && numericLitTokens.contains(in.lookahead.token) @@ -463,6 +465,15 @@ object Parsers { case _ => fail() + /** Checks that tuples don't contain a parameter. */ + def checkNonParamTuple(t: Tree) = t match + case Tuple(ts) => ts.collectFirst { + case param: ValDef => + syntaxError(em"invalid parameter definition syntax in tuple value", param.span) + } + case _ => + + /** Convert (qual)ident to type identifier */ def convertToTypeId(tree: Tree): Tree = tree match { @@ -1425,13 +1436,30 @@ object Parsers { */ def toplevelTyp(): Tree = rejectWildcardType(typ()) - private def isFunction(tree: Tree): Boolean = tree match { - case Parens(tree1) => isFunction(tree1) - case Block(Nil, tree1) => isFunction(tree1) - case _: Function => true - case _ => false + private def getFunction(tree: Tree): Option[Function] = tree match { + case Parens(tree1) => getFunction(tree1) + case Block(Nil, tree1) => getFunction(tree1) + case t: Function => Some(t) + case _ => None } + private def checkFunctionNotErased(f: Function, context: String) = + def fail(span: Span) = + syntaxError(em"Implementation restriction: erased parameters are not supported in $context", span) + // erased parameter in type + val hasErasedParam = f match + case f: FunctionWithMods => f.hasErasedParams + case _ => false + if hasErasedParam then + fail(f.span) + // erased parameter in term + val hasErasedMods = f.args.collectFirst { + case v: ValDef if v.mods.is(Flags.Erased) => v + } + hasErasedMods match + case Some(param) => fail(param.span) + case _ => + /** CaptureRef ::= ident | `this` */ def captureRef(): Tree = @@ -1464,6 +1492,7 @@ object Parsers { def typ(): Tree = val start = in.offset var imods = Modifiers() + var erasedArgs: ListBuffer[Boolean] = ListBuffer() def functionRest(params: List[Tree]): Tree = val paramSpan = Span(start, in.lastOffset) atSpan(start, in.offset) { @@ -1495,10 +1524,10 @@ object Parsers { if isByNameType(tpt) then syntaxError(em"parameter of type lambda may not be call-by-name", tpt.span) TermLambdaTypeTree(params.asInstanceOf[List[ValDef]], resultType) - else if imods.isOneOf(Given | Erased | Impure) then + else if imods.isOneOf(Given | Impure) || erasedArgs.contains(true) then if imods.is(Given) && params.isEmpty then syntaxError(em"context function types require at least one parameter", paramSpan) - FunctionWithMods(params, resultType, imods) + FunctionWithMods(params, resultType, imods, erasedArgs.toList) else if !ctx.settings.YkindProjector.isDefault then val (newParams :+ newResultType, tparams) = replaceKindProjectorPlaceholders(params :+ resultType): @unchecked lambdaAbstract(tparams, Function(newParams, newResultType)) @@ -1516,17 +1545,30 @@ object Parsers { functionRest(Nil) } else { - if isErased then imods = addModifier(imods) val paramStart = in.offset + def addErased() = + erasedArgs.addOne(isErasedKw) + if isErasedKw then { in.skipToken(); } + addErased() val ts = in.currentRegion.withCommasExpected { funArgType() match case Ident(name) if name != tpnme.WILDCARD && in.isColon => isValParamList = true + def funParam(start: Offset, mods: Modifiers) = { + atSpan(start) { + addErased() + typedFunParam(in.offset, ident(), imods) + } + } commaSeparatedRest( typedFunParam(paramStart, name.toTermName, imods), - () => typedFunParam(in.offset, ident(), imods)) + () => funParam(in.offset, imods)) case t => - commaSeparatedRest(t, funArgType) + def funParam() = { + addErased() + funArgType() + } + commaSeparatedRest(t, funParam) } accept(RPAREN) if isValParamList || in.isArrow || isPureArrow then @@ -1557,11 +1599,13 @@ object Parsers { val arrowOffset = in.skipToken() val body = toplevelTyp() atSpan(start, arrowOffset) { - if (isFunction(body)) - PolyFunction(tparams, body) - else { - syntaxError(em"Implementation restriction: polymorphic function types must have a value parameter", arrowOffset) - Ident(nme.ERROR.toTypeName) + getFunction(body) match { + case Some(f) => + checkFunctionNotErased(f, "poly function") + PolyFunction(tparams, body) + case None => + syntaxError(em"Implementation restriction: polymorphic function types must have a value parameter", arrowOffset) + Ident(nme.ERROR.toTypeName) } } } @@ -1573,14 +1617,17 @@ object Parsers { else infixType() in.token match - case ARROW | CTXARROW => functionRest(t :: Nil) + case ARROW | CTXARROW => + erasedArgs.addOne(false) + functionRest(t :: Nil) case MATCH => matchType(t) case FORSOME => syntaxError(ExistentialTypesNoLongerSupported()); t case _ => if isPureArrow then + erasedArgs.addOne(false) functionRest(t :: Nil) else - if (imods.is(Erased) && !t.isInstanceOf[FunctionWithMods]) + if (erasedArgs.contains(true) && !t.isInstanceOf[FunctionWithMods]) syntaxError(ErasedTypesCanOnlyBeFunctionTypes(), implicitKwPos(start)) t end typ @@ -2078,24 +2125,22 @@ object Parsers { def expr(location: Location): Tree = { val start = in.offset - def isSpecialClosureStart = in.lookahead.isIdent(nme.erased) && in.erasedEnabled in.token match case IMPLICIT => closure(start, location, modifiers(BitSet(IMPLICIT))) - case LPAREN if isSpecialClosureStart => - closure(start, location, Modifiers()) case LBRACKET => val start = in.offset val tparams = typeParamClause(ParamOwner.TypeParam) val arrowOffset = accept(ARROW) val body = expr(location) atSpan(start, arrowOffset) { - if (isFunction(body)) - PolyFunction(tparams, body) - else { - syntaxError(em"Implementation restriction: polymorphic function literals must have a value parameter", arrowOffset) - errorTermTree(arrowOffset) - } + getFunction(body) match + case Some(f) => + checkFunctionNotErased(f, "poly function") + PolyFunction(tparams, f) + case None => + syntaxError(em"Implementation restriction: polymorphic function literals must have a value parameter", arrowOffset) + errorTermTree(arrowOffset) } case _ => val saved = placeholderParams @@ -2113,7 +2158,9 @@ object Parsers { else if isWildcard(t) then placeholderParams = placeholderParams ::: saved t - else wrapPlaceholders(t) + else + checkNonParamTuple(t) + wrapPlaceholders(t) } def expr1(location: Location = Location.ElseWhere): Tree = in.token match @@ -2305,10 +2352,8 @@ object Parsers { if in.token == RPAREN then Nil else - var mods1 = mods - if isErased then mods1 = addModifier(mods1) try - commaSeparated(() => binding(mods1)) + commaSeparated(() => binding(mods)) finally accept(RPAREN) else { @@ -2332,10 +2377,13 @@ object Parsers { (atSpan(start) { makeParameter(name, t, mods) }) :: Nil } - /** Binding ::= (id | `_') [`:' Type] + /** Binding ::= [`erased`] (id | `_') [`:' Type] */ def binding(mods: Modifiers): Tree = - atSpan(in.offset) { makeParameter(bindingName(), typedOpt(), mods) } + atSpan(in.offset) { + val mods1 = if isErasedKw then addModifier(mods) else mods + makeParameter(bindingName(), typedOpt(), mods1) + } def bindingName(): TermName = if (in.token == USCORE) { @@ -2532,6 +2580,7 @@ object Parsers { else in.currentRegion.withCommasExpected { var isFormalParams = false def exprOrBinding() = + if isErasedKw then isFormalParams = true if isFormalParams then binding(Modifiers()) else val t = exprInParens() @@ -3175,7 +3224,7 @@ object Parsers { val paramFlags = if ofClass then LocalParamAccessor else Param tps.map(makeSyntheticParameter(nextIdx, _, paramFlags | Synthetic | impliedMods.flags)) - /** ClsTermParamClause ::= ‘(’ [‘erased’] ClsParams ‘)’ | UsingClsTermParamClause + /** ClsTermParamClause ::= ‘(’ ClsParams ‘)’ | UsingClsTermParamClause * UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ * ClsParams ::= ClsParam {‘,’ ClsParam} * ClsParam ::= {Annotation} @@ -3184,10 +3233,10 @@ object Parsers { * | UsingParamClause * * DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ - * UsingParamClause ::= ‘(’ ‘using’ [‘erased’] (DefTermParams | ContextTypes) ‘)’ + * UsingParamClause ::= ‘(’ ‘using’ (DefTermParams | ContextTypes) ‘)’ * DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ * DefTermParams ::= DefTermParam {‘,’ DefTermParam} - * DefTermParam ::= {Annotation} [‘inline’] Param + * DefTermParam ::= {Annotation} [‘erased’] [‘inline’] Param * * Param ::= id `:' ParamType [`=' Expr] * @@ -3211,12 +3260,12 @@ object Parsers { else if isIdent(nme.using) then addParamMod(() => Mod.Given()) - if isErased then - addParamMod(() => Mod.Erased()) def param(): ValDef = { val start = in.offset var mods = impliedMods.withAnnotations(annotations()) + if isErasedKw then + mods = addModifier(mods) if (ofClass) { mods = addFlag(modifiers(start = mods), ParamAccessor) mods = @@ -3227,7 +3276,7 @@ object Parsers { val mod = atSpan(in.skipToken()) { Mod.Var() } addMod(mods, mod) else - if (!(mods.flags &~ (ParamAccessor | Inline | impliedMods.flags)).isEmpty) + if (!(mods.flags &~ (ParamAccessor | Inline | Erased | impliedMods.flags)).isEmpty) syntaxError(em"`val` or `var` expected") if (firstClause && ofCaseClass) mods else mods | PrivateLocal @@ -3275,12 +3324,22 @@ object Parsers { paramMods() if givenOnly && !impliedMods.is(Given) then syntaxError(em"`using` expected") - val isParams = - !impliedMods.is(Given) - || startParamTokens.contains(in.token) - || isIdent && (in.name == nme.inline || in.lookahead.isColon) - if isParams then commaSeparated(() => param()) - else contextTypes(ofClass, numLeadParams, impliedMods) + val (firstParamMod, isParams) = + var mods = EmptyModifiers + if in.lookahead.isColon then + (mods, true) + else + if isErased then mods = addModifier(mods) + val isParams = + !impliedMods.is(Given) + || startParamTokens.contains(in.token) + || isIdent && (in.name == nme.inline || in.lookahead.isColon) + (mods, isParams) + (if isParams then commaSeparated(() => param()) + else contextTypes(ofClass, numLeadParams, impliedMods)) match { + case Nil => Nil + case (h :: t) => h.withAddedFlags(firstParamMod.flags) :: t + } checkVarArgsRules(clause) clause } diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 0da1993310c6..ee0062f77dcd 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -120,10 +120,10 @@ class PlainPrinter(_ctx: Context) extends Printer { } (keyword ~ refinementNameString(rt) ~ toTextRHS(rt.refinedInfo)).close - protected def argText(arg: Type): Text = homogenizeArg(arg) match { + protected def argText(arg: Type, isErased: Boolean = false): Text = keywordText("erased ").provided(isErased) ~ (homogenizeArg(arg) match { case arg: TypeBounds => "?" ~ toText(arg) case arg => toText(arg) - } + }) /** Pretty-print comma-separated type arguments for a constructor to be inserted among parentheses or brackets * (hence with `GlobalPrec` precedence). @@ -235,7 +235,6 @@ class PlainPrinter(_ctx: Context) extends Printer { changePrec(GlobalPrec) { "(" ~ keywordText("using ").provided(tp.isContextualMethod) - ~ keywordText("erased ").provided(tp.isErasedMethod) ~ keywordText("implicit ").provided(tp.isImplicitMethod && !tp.isContextualMethod) ~ paramsText(tp) ~ ")" @@ -296,9 +295,10 @@ class PlainPrinter(_ctx: Context) extends Printer { "(" ~ toTextRef(tp) ~ " : " ~ toTextGlobal(tp.underlying) ~ ")" protected def paramsText(lam: LambdaType): Text = { - def paramText(name: Name, tp: Type) = - toText(name) ~ lambdaHash(lam) ~ toTextRHS(tp, isParameter = true) - Text(lam.paramNames.lazyZip(lam.paramInfos).map(paramText), ", ") + val erasedParams = lam.erasedParams + def paramText(name: Name, tp: Type, erased: Boolean) = + keywordText("erased ").provided(erased) ~ toText(name) ~ lambdaHash(lam) ~ toTextRHS(tp, isParameter = true) + Text(lam.paramNames.lazyZip(lam.paramInfos).lazyZip(erasedParams).map(paramText), ", ") } protected def ParamRefNameString(name: Name): String = nameString(name) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 8ffb99f073fb..014e5ddf0d66 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -148,17 +148,16 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def toTextTuple(args: List[Type]): Text = "(" ~ argsText(args) ~ ")" - def toTextFunction(args: List[Type], isGiven: Boolean, isErased: Boolean, isPure: Boolean): Text = + def toTextFunction(args: List[Type], isGiven: Boolean, isPure: Boolean): Text = changePrec(GlobalPrec) { val argStr: Text = if args.length == 2 && !defn.isTupleNType(args.head) - && !isGiven && !isErased + && !isGiven then atPrec(InfixPrec) { argText(args.head) } else "(" - ~ keywordText("erased ").provided(isErased) ~ argsText(args.init) ~ ")" argStr ~ " " ~ arrow(isGiven, isPure) ~ " " ~ argText(args.last) @@ -168,7 +167,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case info: MethodType => changePrec(GlobalPrec) { "(" - ~ keywordText("erased ").provided(info.isErasedMethod) ~ paramsText(info) ~ ") " ~ arrow(info.isImplicitMethod, isPure) @@ -226,7 +224,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if tycon.isRepeatedParam then toTextLocal(args.head) ~ "*" else if tp.isConvertibleParam then "into " ~ toText(args.head) else if defn.isFunctionSymbol(tsym) then - toTextFunction(args, tsym.name.isContextFunction, tsym.name.isErasedFunction, + toTextFunction(args, tsym.name.isContextFunction, isPure = Feature.pureFunsEnabled && !tsym.name.isImpureFunction) else if isInfixType(tp) then val l :: r :: Nil = args: @unchecked @@ -289,7 +287,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case tp @ FunProto(args, resultType) => "[applied to (" ~ keywordText("using ").provided(tp.isContextualMethod) - ~ keywordText("erased ").provided(tp.isErasedMethod) ~ argsTreeText(args) ~ ") returning " ~ toText(resultType) @@ -650,27 +647,29 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case str: Literal => strText(str) } toText(id) ~ "\"" ~ Text(segments map segmentText, "") ~ "\"" - case Function(args, body) => + case fn @ Function(args, body) => var implicitSeen: Boolean = false var isGiven: Boolean = false - var isErased: Boolean = false - def argToText(arg: Tree) = arg match { + val erasedParams = fn match { + case fn: FunctionWithMods => fn.erasedParams + case _ => fn.args.map(_ => false) + } + def argToText(arg: Tree, isErased: Boolean) = arg match { case arg @ ValDef(name, tpt, _) => val implicitText = if ((arg.mods.is(Given))) { isGiven = true; "" } - else if ((arg.mods.is(Erased))) { isErased = true; "" } else if ((arg.mods.is(Implicit)) && !implicitSeen) { implicitSeen = true; keywordStr("implicit ") } else "" - implicitText ~ toText(name) ~ optAscription(tpt) + val erasedText = if isErased then keywordStr("erased ") else "" + implicitText ~ erasedText ~ toText(name) ~ optAscription(tpt) case _ => toText(arg) } val argsText = args match { - case (arg @ ValDef(_, tpt, _)) :: Nil if tpt.isEmpty => argToText(arg) + case (arg @ ValDef(_, tpt, _)) :: Nil if tpt.isEmpty => argToText(arg, erasedParams(0)) case _ => "(" - ~ keywordText("erased ").provided(isErased) - ~ Text(args.map(argToText), ", ") + ~ Text(args.zip(erasedParams).map(argToText), ", ") ~ ")" } val isPure = @@ -870,7 +869,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "()" case untpd.ValDefs(vparams @ (vparam :: _)) => "(" ~ keywordText("using ").provided(vparam.mods.is(Given)) - ~ keywordText("erased ").provided(vparam.mods.is(Erased)) ~ toText(vparams, ", ") ~ ")" case untpd.TypeDefs(tparams) => "[" ~ toText(tparams, ", ") ~ "]" @@ -1032,7 +1030,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { else PrintableFlags(isType) if (homogenizedView && mods.flags.isTypeFlags) flagMask &~= GivenOrImplicit // drop implicit/given from classes val rawFlags = if (sym.exists) sym.flagsUNSAFE else mods.flags - if (rawFlags.is(Param)) flagMask = flagMask &~ Given &~ Erased + if (rawFlags.is(Param)) flagMask = flagMask &~ Given val flags = rawFlags & flagMask var flagsText = toTextFlags(sym, flags) val annotTexts = diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index 851e3c422460..aadfedd2417c 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -128,7 +128,7 @@ class Interpreter(pos: SrcPos, classLoader0: ClassLoader)(using Context): view.toList fnType.dealias match - case fnType: MethodType if fnType.isErasedMethod => interpretArgs(argss, fnType.resType) + case fnType: MethodType if fnType.hasErasedParams => interpretArgs(argss, fnType.resType) case fnType: MethodType => val argTypes = fnType.paramInfos assert(argss.head.size == argTypes.size) @@ -342,7 +342,7 @@ object Interpreter: case fn: Ident => Some((tpd.desugarIdent(fn).withSpan(fn.span), Nil)) case fn: Select => Some((fn, Nil)) case Apply(f @ Call0(fn, args1), args2) => - if (f.tpe.widenDealias.isErasedMethod) Some((fn, args1)) + if (f.tpe.widenDealias.hasErasedParams) Some((fn, args1)) else Some((fn, args2 :: args1)) case TypeApply(Call0(fn, args), _) => Some((fn, args)) case _ => None diff --git a/compiler/src/dotty/tools/dotc/transform/Bridges.scala b/compiler/src/dotty/tools/dotc/transform/Bridges.scala index e302170991f9..569b16681cde 100644 --- a/compiler/src/dotty/tools/dotc/transform/Bridges.scala +++ b/compiler/src/dotty/tools/dotc/transform/Bridges.scala @@ -129,9 +129,12 @@ class Bridges(root: ClassSymbol, thisPhase: DenotTransformer)(using Context) { assert(ctx.typer.isInstanceOf[Erasure.Typer]) ctx.typer.typed(untpd.cpy.Apply(ref)(ref, args), member.info.finalResultType) else - val defn.ContextFunctionType(argTypes, resType, isErased) = tp: @unchecked + val defn.ContextFunctionType(argTypes, resType, erasedParams) = tp: @unchecked val anonFun = newAnonFun(ctx.owner, - MethodType(if isErased then Nil else argTypes, resType), + MethodType( + argTypes.zip(erasedParams.padTo(argTypes.length, false)) + .flatMap((t, e) => if e then None else Some(t)), + resType), coord = ctx.owner.coord) anonFun.info = transformInfo(anonFun, anonFun.info) diff --git a/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala b/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala index 2ab910f6d06e..5863c360e728 100644 --- a/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala +++ b/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala @@ -20,7 +20,7 @@ object ContextFunctionResults: */ def annotateContextResults(mdef: DefDef)(using Context): Unit = def contextResultCount(rhs: Tree, tp: Type): Int = tp match - case defn.ContextFunctionType(_, resTpe, _) => + case defn.ContextFunctionType(_, resTpe, erasedParams) if !erasedParams.contains(true) /* Only enable for non-erased functions */ => rhs match case closureDef(meth) => 1 + contextResultCount(meth.rhs, resTpe) case _ => 0 @@ -58,7 +58,7 @@ object ContextFunctionResults: */ def contextResultsAreErased(sym: Symbol)(using Context): Boolean = def allErased(tp: Type): Boolean = tp.dealias match - case defn.ContextFunctionType(_, resTpe, isErased) => isErased && allErased(resTpe) + case defn.ContextFunctionType(_, resTpe, erasedParams) => !erasedParams.contains(false) && allErased(resTpe) case _ => true contextResultCount(sym) > 0 && allErased(sym.info.finalResultType) @@ -72,10 +72,8 @@ object ContextFunctionResults: integrateContextResults(rt, crCount) case tp: MethodOrPoly => tp.derivedLambdaType(resType = integrateContextResults(tp.resType, crCount)) - case defn.ContextFunctionType(argTypes, resType, isErased) => - val methodType: MethodTypeCompanion = - if isErased then ErasedMethodType else MethodType - methodType(argTypes, integrateContextResults(resType, crCount - 1)) + case defn.ContextFunctionType(argTypes, resType, erasedParams) => + MethodType(argTypes, integrateContextResults(resType, crCount - 1)) /** The total number of parameters of method `sym`, not counting * erased parameters, but including context result parameters. @@ -85,14 +83,16 @@ object ContextFunctionResults: def contextParamCount(tp: Type, crCount: Int): Int = if crCount == 0 then 0 else - val defn.ContextFunctionType(params, resTpe, isErased) = tp: @unchecked + val defn.ContextFunctionType(params, resTpe, erasedParams) = tp: @unchecked val rest = contextParamCount(resTpe, crCount - 1) - if isErased then rest else params.length + rest + if erasedParams.contains(true) then erasedParams.count(_ == false) + rest else params.length + rest def normalParamCount(tp: Type): Int = tp.widenExpr.stripPoly match case mt @ MethodType(pnames) => val rest = normalParamCount(mt.resType) - if mt.isErasedMethod then rest else pnames.length + rest + if mt.hasErasedParams then + mt.erasedParams.count(_ == false) + rest + else pnames.length + rest case _ => contextParamCount(tp, contextResultCount(sym)) normalParamCount(sym.info) @@ -133,4 +133,4 @@ object ContextFunctionResults: case _ => false -end ContextFunctionResults \ No newline at end of file +end ContextFunctionResults diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 479a455b4aea..151e841f0e48 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -15,6 +15,7 @@ import MegaPhase.* import Decorators.* import typer.RefChecks import reporting.trace +import dotty.tools.dotc.core.Names.Name /** This phase implements the following transformations: * @@ -79,11 +80,14 @@ class ElimByName extends MiniPhase, InfoTransformer: case ExprType(rt) if exprBecomesFunction(sym) => defn.ByNameFunction(rt) case tp: MethodType => - def exprToFun(tp: Type) = tp match - case ExprType(rt) => defn.ByNameFunction(rt) + def exprToFun(tp: Type, name: Name) = tp match + case ExprType(rt) => + if rt.hasAnnotation(defn.ErasedParamAnnot) then + report.error(em"By-name parameter cannot be erased: $name", sym.srcPos) + defn.ByNameFunction(rt) case tp => tp tp.derivedLambdaType( - paramInfos = tp.paramInfos.mapConserve(exprToFun), + paramInfos = tp.paramInfos.zipWithConserve(tp.paramNames)(exprToFun), resType = transformInfo(tp.resType, sym)) case tp: PolyType => tp.derivedLambdaType(resType = transformInfo(tp.resType, sym)) diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 60affae38ad5..981dd5f60aea 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -500,7 +500,7 @@ object Erasure { if isFunction && !ctx.settings.scalajs.value then val arity = implParamTypes.length val specializedFunctionalInterface = - if defn.isSpecializableFunctionSAM(implParamTypes, implResultType) then + if !implType.hasErasedParams && defn.isSpecializableFunctionSAM(implParamTypes, implResultType) then // Using these subclasses is critical to avoid boxing since their // SAM is a specialized method `apply$mc*$sp` whose default // implementation in FunctionN boxes. @@ -679,6 +679,8 @@ object Erasure { val qualTp = tree.qualifier.typeOpt.widen if qualTp.derivesFrom(defn.PolyFunctionClass) then erasePolyFunctionApply(qualTp.select(nme.apply).widen).classSymbol + else if defn.isErasedFunctionType(qualTp) then + eraseErasedFunctionApply(qualTp.select(nme.apply).widen.asInstanceOf[MethodType]).classSymbol else NoSymbol } @@ -827,7 +829,10 @@ object Erasure { val Apply(fun, args) = tree val origFun = fun.asInstanceOf[tpd.Tree] val origFunType = origFun.tpe.widen(using preErasureCtx) - val ownArgs = if origFunType.isErasedMethod then Nil else args + val ownArgs = origFunType match + case mt: MethodType if mt.hasErasedParams => + args.zip(mt.erasedParams).collect { case (arg, false) => arg } + case _ => args val fun1 = typedExpr(fun, AnyFunctionProto) fun1.tpe.widen match case mt: MethodType => diff --git a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala index 050abf7f3cb7..a1baeac272b9 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala +++ b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala @@ -311,7 +311,9 @@ object GenericSignatures { case mtpe: MethodType => // erased method parameters do not make it to the bytecode. def effectiveParamInfoss(t: Type)(using Context): List[List[Type]] = t match { - case t: MethodType if t.isErasedMethod => effectiveParamInfoss(t.resType) + case t: MethodType if t.hasErasedParams => + t.paramInfos.zip(t.erasedParams).collect{ case (i, false) => i } + :: effectiveParamInfoss(t.resType) case t: MethodType => t.paramInfos :: effectiveParamInfoss(t.resType) case _ => Nil } diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index c56bac4d66af..87c6e294c104 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -315,7 +315,7 @@ object PickleQuotes { defn.QuotedExprClass.typeRef.appliedTo(defn.AnyType)), args => val cases = termSplices.map { case (splice, idx) => - val defn.FunctionOf(argTypes, defn.FunctionOf(quotesType :: _, _, _, _), _, _) = splice.tpe: @unchecked + val defn.FunctionOf(argTypes, defn.FunctionOf(quotesType :: _, _, _), _) = splice.tpe: @unchecked val rhs = { val spliceArgs = argTypes.zipWithIndex.map { (argType, i) => args(1).select(nme.apply).appliedTo(Literal(Constant(i))).asInstance(argType) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 2039a8f19558..574db18c9c7f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -296,19 +296,21 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case tree: Apply => val methType = tree.fun.tpe.widen.asInstanceOf[MethodType] val app = - if (methType.isErasedMethod) + if (methType.hasErasedParams) tpd.cpy.Apply(tree)( tree.fun, - tree.args.mapConserve(arg => - if methType.isResultDependent then - Checking.checkRealizable(arg.tpe, arg.srcPos, "erased argument") - if (methType.isImplicitMethod && arg.span.isSynthetic) - arg match - case _: RefTree | _: Apply | _: TypeApply if arg.symbol.is(Erased) => - dropInlines.transform(arg) - case _ => - PruneErasedDefs.trivialErasedTree(arg) - else dropInlines.transform(arg))) + tree.args.zip(methType.erasedParams).map((arg, isErased) => + if !isErased then arg + else + if methType.isResultDependent then + Checking.checkRealizable(arg.tpe, arg.srcPos, "erased argument") + if (methType.isImplicitMethod && arg.span.isSynthetic) + arg match + case _: RefTree | _: Apply | _: TypeApply if arg.symbol.is(Erased) => + dropInlines.transform(arg) + case _ => + PruneErasedDefs.trivialErasedTree(arg) + else dropInlines.transform(arg))) else tree def app1 = diff --git a/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala b/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala index 568512207fde..17f2d11ccfec 100644 --- a/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala @@ -13,6 +13,7 @@ import ast.tpd import SymUtils._ import config.Feature import Decorators.* +import dotty.tools.dotc.core.Types.MethodType /** This phase makes all erased term members of classes private so that they cannot * conflict with non-erased members. This is needed so that subsequent phases like @@ -38,8 +39,11 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform => else sym.copySymDenotation(initFlags = sym.flags | Private) override def transformApply(tree: Apply)(using Context): Tree = - if !tree.fun.tpe.widen.isErasedMethod then tree - else cpy.Apply(tree)(tree.fun, tree.args.map(trivialErasedTree)) + tree.fun.tpe.widen match + case mt: MethodType if mt.hasErasedParams => + cpy.Apply(tree)(tree.fun, tree.args.zip(mt.erasedParams).map((a, e) => if e then trivialErasedTree(a) else a)) + case _ => + tree override def transformValDef(tree: ValDef)(using Context): Tree = checkErasedInExperimental(tree.symbol) diff --git a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala index c1f891d6293a..2248fbc8d570 100644 --- a/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala +++ b/compiler/src/dotty/tools/dotc/transform/SpecializeFunctions.scala @@ -70,7 +70,7 @@ class SpecializeFunctions extends MiniPhase { /** Dispatch to specialized `apply`s in user code when available */ override def transformApply(tree: Apply)(using Context) = tree match { - case Apply(fun: NameTree, args) if fun.name == nme.apply && args.size <= 3 && fun.symbol.owner.isType => + case Apply(fun: NameTree, args) if fun.name == nme.apply && args.size <= 3 && fun.symbol.maybeOwner.isType => val argTypes = fun.tpe.widen.firstParamTypes.map(_.widenSingleton.dealias) val retType = tree.tpe.widenSingleton.dealias val isSpecializable = diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 133651fe08b2..52942ea719f9 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -445,10 +445,12 @@ object TreeChecker { // Polymorphic apply methods stay structural until Erasure val isPolyFunctionApply = (tree.name eq nme.apply) && tree.qualifier.typeOpt.derivesFrom(defn.PolyFunctionClass) + // Erased functions stay structural until Erasure + val isErasedFunctionApply = (tree.name eq nme.apply) && tree.qualifier.typeOpt.derivesFrom(defn.ErasedFunctionClass) // Outer selects are pickled specially so don't require a symbol val isOuterSelect = tree.name.is(OuterSelectName) val isPrimitiveArrayOp = ctx.erasedTypes && nme.isPrimitiveName(tree.name) - if !(tree.isType || isPolyFunctionApply || isOuterSelect || isPrimitiveArrayOp) then + if !(tree.isType || isPolyFunctionApply || isErasedFunctionApply || isOuterSelect || isPrimitiveArrayOp) then val denot = tree.denot assert(denot.exists, i"Selection $tree with type $tpe does not have a denotation") assert(denot.symbol.exists, i"Denotation $denot of selection $tree with type $tpe does not have a symbol, qualifier type = ${tree.qualifier.typeOpt}") diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index a8abd868fdef..345a8693063b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1978,7 +1978,7 @@ trait Applications extends Compatibility { val formals = ref.widen.firstParamTypes if formals.length > idx then formals(idx) match - case defn.FunctionOf(args, _, _, _) => args.length + case defn.FunctionOf(args, _, _) => args.length case _ => -1 else -1 @@ -2062,7 +2062,7 @@ trait Applications extends Compatibility { if isDetermined(alts2) then alts2 else resolveMapped(alts1, _.widen.appliedTo(targs1.tpes), pt1) - case defn.FunctionOf(args, resultType, _, _) => + case defn.FunctionOf(args, resultType, _) => narrowByTypes(alts, args, resultType) case pt => @@ -2225,7 +2225,7 @@ trait Applications extends Compatibility { val formalsForArg: List[Type] = altFormals.map(_.head) def argTypesOfFormal(formal: Type): List[Type] = formal.dealias match { - case defn.FunctionOf(args, result, isImplicit, isErased) => args + case defn.FunctionOf(args, result, isImplicit) => args case defn.PartialFunctionOf(arg, result) => arg :: Nil case _ => Nil } diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index 46725f0fa6b2..b1513df777ec 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -285,8 +285,9 @@ object EtaExpansion extends LiftImpure { val body = Apply(lifted, ids) if (mt.isContextualMethod) body.setApplyKind(ApplyKind.Using) val fn = - if (mt.isContextualMethod) new untpd.FunctionWithMods(params, body, Modifiers(Given)) - else if (mt.isImplicitMethod) new untpd.FunctionWithMods(params, body, Modifiers(Implicit)) + if (mt.isContextualMethod) new untpd.FunctionWithMods(params, body, Modifiers(Given), mt.erasedParams) + else if (mt.isImplicitMethod) new untpd.FunctionWithMods(params, body, Modifiers(Implicit), mt.erasedParams) + else if (mt.hasErasedParams) new untpd.FunctionWithMods(params, body, Modifiers(), mt.erasedParams) else untpd.Function(params, body) if (defs.nonEmpty) untpd.Block(defs.toList map (untpd.TypedSplice(_)), fn) else fn } diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index da785e32865a..bde279c582e6 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -372,7 +372,7 @@ object ProtoTypes { private def isUndefined(tp: Type): Boolean = tp match { case _: WildcardType => true - case defn.FunctionOf(args, result, _, _) => args.exists(isUndefined) || isUndefined(result) + case defn.FunctionOf(args, result, _) => args.exists(isUndefined) || isUndefined(result) case _ => false } @@ -842,7 +842,7 @@ object ProtoTypes { normalize(et.resultType, pt) case wtp => val iftp = defn.asContextFunctionType(wtp) - if iftp.exists && followIFT then normalize(iftp.dropDependentRefinement.argInfos.last, pt) + if iftp.exists && followIFT then normalize(iftp.functionArgInfos.last, pt) else tp } } diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 1a7a4b97855b..103961b68c29 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -103,12 +103,12 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): def functionTypeEqual(baseFun: Type, actualArgs: List[Type], actualRet: Type, expected: Type) = expected =:= defn.FunctionOf(actualArgs, actualRet, - defn.isContextFunctionType(baseFun), defn.isErasedFunctionType(baseFun)) + defn.isContextFunctionType(baseFun)) val arity: Int = - if defn.isErasedFunctionType(fun) || defn.isErasedFunctionType(fun) then -1 // TODO support? + if defn.isErasedFunctionType(fun) then -1 // TODO support? else if defn.isFunctionType(fun) then // TupledFunction[(...) => R, ?] - fun.dropDependentRefinement.dealias.argInfos match + fun.functionArgInfos match case funArgs :+ funRet if functionTypeEqual(fun, defn.tupleType(funArgs) :: Nil, funRet, tupled) => // TupledFunction[(...funArgs...) => funRet, ?] @@ -116,7 +116,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): case _ => -1 else if defn.isFunctionType(tupled) then // TupledFunction[?, (...) => R] - tupled.dropDependentRefinement.dealias.argInfos match + tupled.functionArgInfos match case tupledArgs :: funRet :: Nil => defn.tupleTypes(tupledArgs.dealias) match case Some(funArgs) if functionTypeEqual(tupled, funArgs, funRet, fun) => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9d8fdcc006c9..4c6ec98ba9ba 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1262,7 +1262,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer (pt1.argInfos.init, typeTree(interpolateWildcards(pt1.argInfos.last.hiBound))) case RefinedType(parent, nme.apply, mt @ MethodTpe(_, formals, restpe)) - if defn.isNonRefinedFunction(parent) && formals.length == defaultArity => + if (defn.isNonRefinedFunction(parent) || defn.isErasedFunctionType(parent)) && formals.length == defaultArity => (formals, untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef)))) case SAMType(mt @ MethodTpe(_, formals, restpe)) => (formals, @@ -1293,16 +1293,18 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * If both attempts fail, return `NoType`. */ def inferredFromTarget( - param: untpd.ValDef, formal: Type, calleeType: Type, paramIndex: Name => Int)(using Context): Type = + param: untpd.ValDef, formal: Type, calleeType: Type, isErased: Boolean, paramIndex: Name => Int)(using Context): Type = val target = calleeType.widen match case mtpe: MethodType => val pos = paramIndex(param.name) if pos < mtpe.paramInfos.length then - mtpe.paramInfos(pos) + val tp = mtpe.paramInfos(pos) // This works only if vararg annotations match up. // See neg/i14367.scala for an example where the inferred type is mispredicted. // Nevertheless, the alternative would be to give up completely, so this is // defensible. + // Strip inferred erased annotation, to avoid accidentally inferring erasedness + if !isErased then tp.stripAnnots(_.symbol != defn.ErasedParamAnnot) else tp else NoType case _ => NoType if target.exists then formal <:< target @@ -1316,32 +1318,14 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedFunctionType(tree: untpd.Function, pt: Type)(using Context): Tree = { val untpd.Function(args, body) = tree - var funFlags = tree match { - case tree: untpd.FunctionWithMods => tree.mods.flags - case _ => EmptyFlags + var (funFlags, erasedParams) = tree match { + case tree: untpd.FunctionWithMods => (tree.mods.flags, tree.erasedParams) + case _ => (EmptyFlags, args.map(_ => false)) } - assert(!funFlags.is(Erased) || !args.isEmpty, "An empty function cannot not be erased") - val numArgs = args.length val isContextual = funFlags.is(Given) - val isErased = funFlags.is(Erased) val isImpure = funFlags.is(Impure) - val funSym = defn.FunctionSymbol(numArgs, isContextual, isErased, isImpure) - - /** If `app` is a function type with arguments that are all erased classes, - * turn it into an erased function type. - */ - def propagateErased(app: Tree): Tree = app match - case AppliedTypeTree(tycon: TypeTree, args) - if !isErased - && numArgs > 0 - && args.indexWhere(!_.tpe.isErasedClass) == numArgs => - val tycon1 = TypeTree(defn.FunctionSymbol(numArgs, isContextual, true, isImpure).typeRef) - .withSpan(tycon.span) - assignType(cpy.AppliedTypeTree(app)(tycon1, args), tycon1, args) - case _ => - app /** Typechecks dependent function type with given parameters `params` */ def typedDependent(params: List[untpd.ValDef])(using Context): Tree = @@ -1356,16 +1340,29 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if funFlags.is(Given) then params.map(_.withAddedFlags(Given)) else params val params2 = params1.map(fixThis.transformSub) - val appDef0 = untpd.DefDef(nme.apply, List(params2), body, EmptyTree).withSpan(tree.span) + val params3 = params2.zipWithConserve(erasedParams) { (arg, isErased) => + if isErased then arg.withAddedFlags(Erased) else arg + } + val appDef0 = untpd.DefDef(nme.apply, List(params3), body, EmptyTree).withSpan(tree.span) index(appDef0 :: Nil) val appDef = typed(appDef0).asInstanceOf[DefDef] val mt = appDef.symbol.info.asInstanceOf[MethodType] if (mt.isParamDependent) report.error(em"$mt is an illegal function type because it has inter-parameter dependencies", tree.srcPos) + // Restart typechecking if there are erased classes that we want to mark erased + if mt.erasedParams.zip(mt.paramInfos.map(_.isErasedClass)).exists((paramErased, classErased) => classErased && !paramErased) then + val newParams = params3.zipWithConserve(mt.paramInfos.map(_.isErasedClass)) { (arg, isErasedClass) => + if isErasedClass then arg.withAddedFlags(Erased) else arg + } + return typedDependent(newParams) val resTpt = TypeTree(mt.nonDependentResultApprox).withSpan(body.span) val typeArgs = appDef.termParamss.head.map(_.tpt) :+ resTpt - val tycon = TypeTree(funSym.typeRef) - val core = propagateErased(AppliedTypeTree(tycon, typeArgs)) + val core = + if mt.hasErasedParams then TypeTree(defn.ErasedFunctionClass.typeRef) + else + val funSym = defn.FunctionSymbol(numArgs, isContextual, isImpure) + val tycon = TypeTree(funSym.typeRef) + AppliedTypeTree(tycon, typeArgs) RefinedTypeTree(core, List(appDef), ctx.owner.asClass) end typedDependent @@ -1374,17 +1371,25 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer typedDependent(args.asInstanceOf[List[untpd.ValDef]])( using ctx.fresh.setOwner(newRefinedClassSymbol(tree.span)).setNewScope) case _ => - propagateErased( - typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funSym.typeRef), args :+ body), pt)) + if erasedParams.contains(true) then + typedFunctionType(desugar.makeFunctionWithValDefs(tree, pt), pt) + else + val funSym = defn.FunctionSymbol(numArgs, isContextual, isImpure) + val result = typed(cpy.AppliedTypeTree(tree)(untpd.TypeTree(funSym.typeRef), args :+ body), pt) + // if there are any erased classes, we need to re-do the typecheck. + result match + case r: AppliedTypeTree if r.args.exists(_.tpe.isErasedClass) => + typedFunctionType(desugar.makeFunctionWithValDefs(tree, pt), pt) + case _ => result } } def typedFunctionValue(tree: untpd.Function, pt: Type)(using Context): Tree = { val untpd.Function(params: List[untpd.ValDef] @unchecked, _) = tree: @unchecked - val isContextual = tree match { - case tree: untpd.FunctionWithMods => tree.mods.is(Given) - case _ => false + val (isContextual, isDefinedErased) = tree match { + case tree: untpd.FunctionWithMods => (tree.mods.is(Given), tree.erasedParams) + case _ => (false, tree.args.map(_ => false)) } /** The function body to be returned in the closure. Can become a TypedSplice @@ -1485,9 +1490,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val (protoFormals, resultTpt) = decomposeProtoFunction(pt, params.length, tree.srcPos) - def protoFormal(i: Int): Type = - if (protoFormals.length == params.length) protoFormals(i) - else errorType(WrongNumberOfParameters(protoFormals.length), tree.srcPos) + /** Returns the type and whether the parameter is erased */ + def protoFormal(i: Int): (Type, Boolean) = + if (protoFormals.length == params.length) (protoFormals(i), isDefinedErased(i)) + else (errorType(WrongNumberOfParameters(protoFormals.length), tree.srcPos), false) /** Is `formal` a product type which is elementwise compatible with `params`? */ def ptIsCorrectProduct(formal: Type) = @@ -1525,28 +1531,32 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if desugared.isEmpty then val inferredParams: List[untpd.ValDef] = for ((param, i) <- params.zipWithIndex) yield - if (!param.tpt.isEmpty) param - else - val formalBounds = protoFormal(i) - val formal = formalBounds.loBound - val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing - val knownFormal = isFullyDefined(formal, ForceDegree.failBottom) - // If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound, - // try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala) - val paramType = - if knownFormal && !isBottomFromWildcard then - formal - else - inferredFromTarget(param, formal, calleeType, paramIndex).orElse( - if knownFormal then formal - else errorType(AnonymousFunctionMissingParamType(param, tree, formal), param.srcPos) + val (formalBounds, isErased) = protoFormal(i) + val param0 = + if (!param.tpt.isEmpty) param + else + val formal = formalBounds.loBound + val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing + val knownFormal = isFullyDefined(formal, ForceDegree.failBottom) + // If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound, + // try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala) + val paramType = + // Strip inferred erased annotation, to avoid accidentally inferring erasedness + val formal0 = if !isErased then formal.stripAnnots(_.symbol != defn.ErasedParamAnnot) else formal + if knownFormal && !isBottomFromWildcard then + formal0 + else + inferredFromTarget(param, formal, calleeType, isErased, paramIndex).orElse( + if knownFormal then formal0 + else errorType(AnonymousFunctionMissingParamType(param, tree, formal), param.srcPos) + ) + val paramTpt = untpd.TypedSplice( + (if knownFormal then InferredTypeTree() else untpd.TypeTree()) + .withType(paramType.translateFromRepeated(toArray = false)) + .withSpan(param.span.endPos) ) - val paramTpt = untpd.TypedSplice( - (if knownFormal then InferredTypeTree() else untpd.TypeTree()) - .withType(paramType.translateFromRepeated(toArray = false)) - .withSpan(param.span.endPos) - ) - cpy.ValDef(param)(tpt = paramTpt) + cpy.ValDef(param)(tpt = paramTpt) + if isErased then param0.withAddedFlags(Flags.Erased) else param0 desugared = desugar.makeClosure(inferredParams, fnBody, resultTpt, isContextual, tree.span) typed(desugared, pt) @@ -1585,7 +1595,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer |because it has internal parameter dependencies""") else if ((tree.tpt `eq` untpd.ContextualEmptyTree) && mt.paramNames.isEmpty) // Note implicitness of function in target type since there are no method parameters that indicate it. - TypeTree(defn.FunctionOf(Nil, mt.resType, isContextual = true, isErased = false)) + TypeTree(defn.FunctionOf(Nil, mt.resType, isContextual = true)) else if hasCaptureConversionArg(mt.resType) then errorTree(tree, em"""cannot turn method type $mt into closure @@ -2605,6 +2615,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // check value class constraints checkDerivedValueClass(cls, body1) + // check PolyFunction constraints (no erased functions!) + if parents1.exists(_.tpe.classSymbol eq defn.PolyFunctionClass) then + body1.foreach { + case ddef: DefDef => + ddef.paramss.foreach { params => + val erasedParam = params.collectFirst { case vdef: ValDef if vdef.symbol.is(Erased) => vdef } + erasedParam.foreach { p => + report.error(em"Implementation restriction: erased classes are not allowed in a poly function definition", p.srcPos) + } + } + case _ => + } + val effectiveOwner = cls.owner.skipWeakOwner if !cls.isRefinementClass && !cls.isAllOf(PrivateLocal) @@ -3031,7 +3054,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer tree protected def makeContextualFunction(tree: untpd.Tree, pt: Type)(using Context): Tree = { - val defn.FunctionOf(formals, _, true, _) = pt.dropDependentRefinement: @unchecked + val defn.FunctionOf(formals, _, true) = pt.dropDependentRefinement: @unchecked // The getter of default parameters may reach here. // Given the code below @@ -3059,7 +3082,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else formals.map(untpd.TypeTree) } - val ifun = desugar.makeContextualFunction(paramTypes, tree, defn.isErasedFunctionType(pt)) + val erasedParams = pt.dealias match { + case RefinedType(parent, nme.apply, mt: MethodType) => mt.erasedParams + case _ => paramTypes.map(_ => false) + } + + val ifun = desugar.makeContextualFunction(paramTypes, tree, erasedParams) typr.println(i"make contextual function $tree / $pt ---> $ifun") typedFunctionValue(ifun, pt) } diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 6688d6e81a89..f43c051ebf60 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -1581,8 +1581,12 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler self.nonEmpty && self.head.symbol.is(dotc.core.Flags.Implicit) def isGiven: Boolean = self.nonEmpty && self.head.symbol.is(dotc.core.Flags.Given) - def isErased: Boolean = - self.nonEmpty && self.head.symbol.is(dotc.core.Flags.Erased) + def isErased: Boolean = false + + def erasedArgs: List[Boolean] = + self.map(_.symbol.is(dotc.core.Flags.Erased)) + def hasErasedArgs: Boolean = + self.exists(_.symbol.is(dotc.core.Flags.Erased)) end TermParamClauseMethods type TypeParamClause = List[tpd.TypeDef] @@ -2139,9 +2143,12 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler given MethodTypeMethods: MethodTypeMethods with extension (self: MethodType) - def isErased: Boolean = self.isErasedMethod + def isErased: Boolean = false def isImplicit: Boolean = self.isImplicitMethod def param(idx: Int): TypeRepr = self.newParamRef(idx) + + def erasedParams: List[Boolean] = self.erasedParams + def hasErasedParams: Boolean = self.hasErasedParams end extension end MethodTypeMethods @@ -2768,11 +2775,14 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def ProductClass: Symbol = dotc.core.Symbols.defn.ProductClass def FunctionClass(arity: Int, isImplicit: Boolean = false, isErased: Boolean = false): Symbol = if arity < 0 then throw IllegalArgumentException(s"arity: $arity") - dotc.core.Symbols.defn.FunctionSymbol(arity, isImplicit, isErased) + if isErased then + throw new Exception("Erased function classes are not supported. Use a refined `scala.runtime.ErasedFunction`") + else dotc.core.Symbols.defn.FunctionSymbol(arity, isImplicit) def FunctionClass(arity: Int): Symbol = FunctionClass(arity, false, false) def FunctionClass(arity: Int, isContextual: Boolean): Symbol = FunctionClass(arity, isContextual, false) + def ErasedFunctionClass = dotc.core.Symbols.defn.ErasedFunctionClass def TupleClass(arity: Int): Symbol = dotc.core.Symbols.defn.TupleType(arity).nn.classSymbol.asClass def isTupleClass(sym: Symbol): Boolean = diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 1fbb7a34b078..445e86ee2408 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -140,7 +140,7 @@ type val var while with yield ### Soft keywords ``` -as derives end extension infix inline opaque open throws transparent using | * + - +as derives end erased extension infix inline opaque open throws transparent using | * + - ``` See the [separate section on soft keywords](../reference/soft-modifier.md) for additional @@ -180,13 +180,13 @@ Type ::= FunType | FunParamClause ‘=>>’ Type TermLambdaTypeTree(ps, t) | MatchType | InfixType -FunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type Function(ts, t) +FunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type Function(ts, t) | FunctionWithMods(ts, t, mods, erasedParams) | HKTypeParamClause '=>' Type PolyFunction(ps, t) FunTypeArgs ::= InfixType | ‘(’ [ FunArgTypes ] ‘)’ | FunParamClause FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ -TypedFunParam ::= id ‘:’ Type +TypedFunParam ::= [`erased`] id ‘:’ Type MatchType ::= InfixType `match` <<< TypeCaseClauses >>> InfixType ::= RefinedType {id [nl] RefinedType} InfixOp(t1, op, t2) RefinedType ::= AnnotType {[nl] Refinement} RefinedTypeTree(t, ds) @@ -207,8 +207,8 @@ Singleton ::= SimpleRef | SimpleLiteral | Singleton ‘.’ id Singletons ::= Singleton { ‘,’ Singleton } -FunArgType ::= Type - | ‘=>’ Type PrefixOp(=>, t) +FunArgType ::= [`erased`] Type + | [`erased`] ‘=>’ Type PrefixOp(=>, t) FunArgTypes ::= FunArgType { ‘,’ FunArgType } ParamType ::= [‘=>’] ParamValueType ParamValueType ::= [‘into’] ExactParamType Into(t) @@ -229,7 +229,7 @@ BlockResult ::= FunParams (‘=>’ | ‘?=>’) Block | HkTypeParamClause ‘=>’ Block | Expr1 FunParams ::= Bindings - | id + | [`erased`] id | ‘_’ Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] If(Parens(cond), thenp, elsep?) | [‘inline’] ‘if’ Expr ‘then’ Expr [[semi] ‘else’ Expr] If(cond, thenp, elsep?) @@ -376,13 +376,13 @@ UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ DefTermParams ::= DefTermParam {‘,’ DefTermParam} -DefTermParam ::= {Annotation} [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. +DefTermParam ::= {Annotation} [`erased`] [‘inline’] Param ValDef(mods, id, tpe, expr) -- point of mods at id. Param ::= id ‘:’ ParamType [‘=’ Expr] ``` ### Bindings and Imports ```ebnf -Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’ +Bindings ::= ‘(’[`erased`] [Binding {‘,’ [`erased`] Binding}] ‘)’ Binding ::= (id | ‘_’) [‘:’ Type] ValDef(_, id, tpe, EmptyTree) Modifier ::= LocalModifier diff --git a/docs/_docs/reference/experimental/erased-defs-spec.md b/docs/_docs/reference/experimental/erased-defs-spec.md index 24ae89c7e28b..59dfed92da2a 100644 --- a/docs/_docs/reference/experimental/erased-defs-spec.md +++ b/docs/_docs/reference/experimental/erased-defs-spec.md @@ -19,8 +19,8 @@ TODO: complete def g(erased x: Int) = ... - (erased x: Int) => ... - def h(x: (erased Int) => Int) = ... + (erased x: Int, y: Int) => ... + def h(x: (Int, erased Int) => Int) = ... class K(erased x: Int) { ... } erased class E {} @@ -34,12 +34,12 @@ TODO: complete 3. Functions * `(erased x1: T1, x2: T2, ..., xN: TN) => y : (erased T1, T2, ..., TN) => R` - * `(given erased x1: T1, x2: T2, ..., xN: TN) => y: (given erased T1, T2, ..., TN) => R` + * `(given x1: T1, erased x2: T2, ..., xN: TN) => y: (given T1, erased T2, ..., TN) => R` * `(given erased T1) => R <:< erased T1 => R` - * `(given erased T1, T2) => R <:< (erased T1, T2) => R` + * `(given T1, erased T2) => R <:< (T1, erased T2) => R` * ... - Note that there is no subtype relation between `(erased T) => R` and `T => R` (or `(given erased T) => R` and `(given T) => R`) + Note that there is no subtype relation between `(erased T) => R` and `T => R` (or `(given erased T) => R` and `(given T) => R`). The `erased` parameters must match exactly in their respective positions. 4. Eta expansion @@ -51,7 +51,8 @@ TODO: complete * All `erased` parameters are removed from the function * All argument to `erased` parameters are not passed to the function * All `erased` definitions are removed - * All `(erased T1, T2, ..., TN) => R` and `(given erased T1, T2, ..., TN) => R` become `() => R` + * `(erased ET1, erased ET2, T1, ..., erased ETN, TM) => R` are erased to `(T1, ..., TM) => R`. + * `(given erased ET1, erased ET2, T1, ..., erased ETN, TM) => R` are erased to `(given T1, ..., TM) => R`. 6. Overloading @@ -60,11 +61,10 @@ TODO: complete 7. Overriding - * Member definitions overriding each other must both be `erased` or not be `erased` - * `def foo(x: T): U` cannot be overridden by `def foo(erased x: T): U` and vice-versa - * - + * Member definitions overriding each other must both be `erased` or not be `erased`. + * `def foo(x: T): U` cannot be overridden by `def foo(erased x: T): U` and vice-versa. 8. Type Restrictions * For dependent functions, `erased` parameters are limited to realizable types, that is, types that are inhabited by non-null values. This restriction stops us from using a bad bound introduced by an erased value, which leads to unsoundness (see #4060). + * Polymorphic functions with erased parameters are currently not supported, and will be rejected by the compiler. This is purely an implementation restriction, and might be lifted in the future. diff --git a/docs/_docs/reference/experimental/erased-defs.md b/docs/_docs/reference/experimental/erased-defs.md index 28455f26cdc0..ef4f02e33dd4 100644 --- a/docs/_docs/reference/experimental/erased-defs.md +++ b/docs/_docs/reference/experimental/erased-defs.md @@ -54,13 +54,13 @@ semantics and they are completely erased. ## How to define erased terms? Parameters of methods and functions can be declared as erased, placing `erased` -in front of a parameter list (like `given`). +in front of each erased parameter (like `inline`). ```scala -def methodWithErasedEv(erased ev: Ev): Int = 42 +def methodWithErasedEv(erased ev: Ev, x: Int): Int = x + 2 -val lambdaWithErasedEv: erased Ev => Int = - (erased ev: Ev) => 42 +val lambdaWithErasedEv: (erased Ev, Int) => Int = + (erased ev, x) => x + 2 ``` `erased` parameters will not be usable for computations, though they can be used @@ -80,7 +80,7 @@ parameters. ```scala erased val erasedEvidence: Ev = ... -methodWithErasedEv(erasedEvidence) +methodWithErasedEv(erasedEvidence, 40) // 42 ``` ## What happens with erased values at runtime? @@ -89,15 +89,15 @@ As `erased` are guaranteed not to be used in computations, they can and will be erased. ```scala -// becomes def methodWithErasedEv(): Int at runtime -def methodWithErasedEv(erased ev: Ev): Int = ... +// becomes def methodWithErasedEv(x: Int): Int at runtime +def methodWithErasedEv(x: Int, erased ev: Ev): Int = ... def evidence1: Ev = ... erased def erasedEvidence2: Ev = ... // does not exist at runtime erased val erasedEvidence3: Ev = ... // does not exist at runtime -// evidence1 is not evaluated and no value is passed to methodWithErasedEv -methodWithErasedEv(evidence1) +// evidence1 is not evaluated and only `x` is passed to methodWithErasedEv +methodWithErasedEv(x, evidence1) ``` ## State machine with erased evidence example diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 321725d4e84c..79a4ea039ab5 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -2374,7 +2374,16 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Is this a given parameter clause `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */ def isGiven: Boolean /** Is this a erased parameter clause `(erased x1: X1, ..., xn: Xn)` */ + // TODO:deprecate in 3.4 and stabilize `erasedArgs` and `hasErasedArgs`. + // @deprecated("Use `hasErasedArgs`","3.4") def isErased: Boolean + + /** List of `erased` flags for each parameter of the clause */ + @experimental + def erasedArgs: List[Boolean] + /** Whether the clause has any erased parameters */ + @experimental + def hasErasedArgs: Boolean end TermParamClauseMethods /** A type parameter clause `[X1, ..., Xn]` */ @@ -2650,7 +2659,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => */ def isContextFunctionType: Boolean - /** Is this type an erased function type? + /** Is this type a function type with erased parameters? * * @see `isFunctionType` */ @@ -3145,7 +3154,17 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => extension (self: MethodType) /** Is this the type of using parameter clause `(implicit X1, ..., Xn)`, `(using X1, ..., Xn)` or `(using x1: X1, ..., xn: Xn)` */ def isImplicit: Boolean + /** Is this the type of erased parameter clause `(erased x1: X1, ..., xn: Xn)` */ + // TODO:deprecate in 3.4 and stabilize `erasedParams` and `hasErasedParams`. + // @deprecated("Use `hasErasedParams`","3.4") def isErased: Boolean + + /** List of `erased` flags for each parameters of the clause */ + @experimental + def erasedParams: List[Boolean] + /** Whether the clause has any erased parameters */ + @experimental + def hasErasedParams: Boolean def param(idx: Int): TypeRepr end extension end MethodTypeMethods @@ -4275,6 +4294,10 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => @experimental def FunctionClass(arity: Int, isContextual: Boolean): Symbol + /** The `scala.runtime.ErasedFunction` built-in trait. */ + @experimental + def ErasedFunctionClass: Symbol + /** Function-like object that maps arity to symbols for classes `scala.TupleX`. * - 0th element is `NoSymbol` * - 1st element is `NoSymbol` diff --git a/library/src/scala/runtime/ErasedFunction.scala b/library/src/scala/runtime/ErasedFunction.scala new file mode 100644 index 000000000000..7e9211bba75a --- /dev/null +++ b/library/src/scala/runtime/ErasedFunction.scala @@ -0,0 +1,11 @@ +package scala.runtime + +import scala.annotation.experimental + +/** Marker trait for function types with erased parameters. + * + * This trait will be refined with an `apply` method with erased parameters: + * ErasedFunction { def apply([erased] x_1: P_1, ..., [erased] x_N: P_N): R } + * This type will be erased to FunctionL, where L = N - count(erased). + */ +@experimental trait ErasedFunction diff --git a/tests/neg-custom-args/erased/by-name.scala b/tests/neg-custom-args/erased/by-name.scala new file mode 100644 index 000000000000..707cfd96734b --- /dev/null +++ b/tests/neg-custom-args/erased/by-name.scala @@ -0,0 +1,4 @@ +def f(x: => Int, erased y: => Int) = x // error +def g(erased x: => Int, y: => Int) = y // error + +val h: (erased => Int, Int) => Int = (erased x, y) => y // error diff --git a/tests/neg-custom-args/erased/erased-in-tuples.scala b/tests/neg-custom-args/erased/erased-in-tuples.scala new file mode 100644 index 000000000000..11a251c3bd4d --- /dev/null +++ b/tests/neg-custom-args/erased/erased-in-tuples.scala @@ -0,0 +1,16 @@ +@main def Test() = + val x = 5 + val y = 7 + + val t1 = (x, erased y) // error + val t2 = (erased x, y) // error + val t1a = (x: Int, erased y: Int) // error + val t2a = (erased x: Int, y: Int) // error + + val nest = (x, (x, erased y)) // error + + def use(f: (Int, Int) => Any) = f(5, 6) + + use((_, erased _)) // error + + (x, erased y) // error diff --git a/tests/neg-custom-args/erased/lambda-infer.scala b/tests/neg-custom-args/erased/lambda-infer.scala new file mode 100644 index 000000000000..2eebf8186b0d --- /dev/null +++ b/tests/neg-custom-args/erased/lambda-infer.scala @@ -0,0 +1,23 @@ +type F = (Int, erased Int) => Int + +erased class A + +@main def Test() = + val a: F = (x, y) => x + 1 // error: Expected F got (Int, Int) => Int + val b: F = (x, erased y) => x + 1 // ok + val c: F = (_, _) => 5 // error: Expected F got (Int, Int) => Int + val d: F = (_, erased _) => 5 // ok + + def use(f: F) = f(5, 6) + + use { (x, y) => x } // error: Expected F got (Int, Int) => Int + + def singleParam(f: (erased Int) => Int) = f(5) + + singleParam(x => 5) // error: Expected (erased Int) => Int got Int => Int + singleParam((erased x) => 5) // ok + + def erasedClass(f: A => Int) = f(new A) + + erasedClass(_ => 5) // ok since A is implicitly erased + diff --git a/tests/neg-custom-args/erased/multiple-args-consume.scala b/tests/neg-custom-args/erased/multiple-args-consume.scala new file mode 100644 index 000000000000..e4aaacca8969 --- /dev/null +++ b/tests/neg-custom-args/erased/multiple-args-consume.scala @@ -0,0 +1,13 @@ +def foo(erased x: Int, y: Int) = y +def bar(x: Int, erased y: Int) = x + +def consumeFoo(f: (erased x: Int, y: Int) => Int) = f(0, 1) + +val fooF: (erased x: Int, y: Int) => Int = foo +val barF: (x: Int, erased y: Int) => Int = bar + +val a = consumeFoo(foo) // ok +val b = consumeFoo(bar) // error + +val c = consumeFoo(fooF) // ok +val d = consumeFoo(barF) // error diff --git a/tests/neg-custom-args/erased/multiple-args.scala b/tests/neg-custom-args/erased/multiple-args.scala new file mode 100644 index 000000000000..fb9bce8e4573 --- /dev/null +++ b/tests/neg-custom-args/erased/multiple-args.scala @@ -0,0 +1,11 @@ +def foo(x: Int, erased y: Int): Int = x +def bar(erased x: Int, y: Int): Int = y + +val fooF: (x: Int, erased y: Int) => Int = foo + +val fooG: (erased x: Int, y: Int) => Int = foo // error + +val barF: (x: Int, erased y: Int) => Int = bar // error + +val barG: (erased x: Int, y: Int) => Int = bar + diff --git a/tests/neg-custom-args/erased/poly-functions.scala b/tests/neg-custom-args/erased/poly-functions.scala new file mode 100644 index 000000000000..000a2ca49cc9 --- /dev/null +++ b/tests/neg-custom-args/erased/poly-functions.scala @@ -0,0 +1,16 @@ +object Test: + // Poly functions with erased parameters are disallowed as an implementation restriction + + type T1 = [X] => (erased x: X, y: Int) => Int // error + type T2 = [X] => (x: X, erased y: Int) => X // error + + val t1 = [X] => (erased x: X, y: Int) => y // error + val t2 = [X] => (x: X, erased y: Int) => x // error + + // Erased classes should be detected too + erased class A + + type T3 = [X] => (x: A, y: X) => X // error + + val t3 = [X] => (x: A, y: X) => y // error + diff --git a/tests/neg/safeThrowsStrawman2.scala b/tests/neg/safeThrowsStrawman2.scala index 7d87baad6fa4..8d95494e30e0 100644 --- a/tests/neg/safeThrowsStrawman2.scala +++ b/tests/neg/safeThrowsStrawman2.scala @@ -24,7 +24,7 @@ def bar(x: Boolean)(using CanThrow[Fail]): Int = val x = new CanThrow[Fail]() // OK, x is erased val y: Any = new CanThrow[Fail]() // error: illegal reference to erased class CanThrow val y2: Any = new CTF() // error: illegal reference to erased class CanThrow - println(foo(true, ctf)) // error: ctf is declared as erased, but is in fact used + println(foo(true, ctf)) // not error: ctf will be erased at erasure val a = (1, new CanThrow[Fail]()) // error: illegal reference to erased class CanThrow def b: (Int, CanThrow[Fail]) = ??? def c = b._2 // ok; we only check creation sites diff --git a/tests/pos-custom-args/erased/erased-class-as-args.scala b/tests/pos-custom-args/erased/erased-class-as-args.scala new file mode 100644 index 000000000000..74c827fbd54b --- /dev/null +++ b/tests/pos-custom-args/erased/erased-class-as-args.scala @@ -0,0 +1,22 @@ +erased class A + +erased class B(val x: Int) extends A + +type T = (x: A, y: Int) => Int + +type TSub[-T <: A] = (erased x: T, y: Int) => Int + +def useT(f: T) = f(new A, 5) + +def useTSub(f: TSub[B]) = f(new B(5), 5) + +@main def Test() = + val tInfer = (x: A, y: Int) => y + 1 + val tExpl: T = (x, y) => y + 1 + assert(useT((erased x, y) => y + 1) == 6) + assert(useT(tInfer) == 6) + assert(useT(tExpl) == 6) + + val tSub: TSub[A] = (x, y) => y + 1 + assert(useT(tSub) == 6) + assert(useTSub(tSub) == 6) diff --git a/tests/pos-custom-args/erased/erased-soft-keyword.scala b/tests/pos-custom-args/erased/erased-soft-keyword.scala new file mode 100644 index 000000000000..fdb884628c7d --- /dev/null +++ b/tests/pos-custom-args/erased/erased-soft-keyword.scala @@ -0,0 +1,18 @@ +def f1(x: Int, erased y: Int) = 0 +def f2(x: Int, erased: Int) = 0 +inline def f3(x: Int, inline erased: Int) = 0 +def f4(x: Int, erased inline: Int) = 0 +// inline def f5(x: Int, erased inline y: Int) = 0 // should parse but rejected later + +def f6(using erased y: Int) = 0 +def f7(using erased: Int) = 0 +inline def f8(using inline erased: Int) = 0 +def f9(using erased inline: Int) = 0 +// inline def f10(using erased inline x: Int) = 0 // should parse but rejected later +def f11(using erased Int) = 0 + +val v1 = (erased: Int) => 0 +val v2: Int => Int = erased => 0 +val v3 = (erased x: Int) => 0 +val v4: (erased Int) => Int = (erased x) => 0 +val v5: (erased: Int) => Int = x => 0 diff --git a/tests/run-custom-args/erased/erased-15.scala b/tests/run-custom-args/erased/erased-15.scala index b879ee4c54d8..02b70f9125d6 100644 --- a/tests/run-custom-args/erased/erased-15.scala +++ b/tests/run-custom-args/erased/erased-15.scala @@ -1,3 +1,5 @@ +import scala.runtime.ErasedFunction + object Test { def main(args: Array[String]): Unit = { @@ -10,7 +12,7 @@ object Test { } } -class Foo extends ErasedFunction1[Int, Int] { +class Foo extends ErasedFunction { def apply(erased x: Int): Int = { println("Foo.apply") 42 diff --git a/tests/run-custom-args/erased/erased-27.check b/tests/run-custom-args/erased/erased-27.check index 4413863feead..1c255dd5419f 100644 --- a/tests/run-custom-args/erased/erased-27.check +++ b/tests/run-custom-args/erased/erased-27.check @@ -1,3 +1,2 @@ block -x foo diff --git a/tests/run-custom-args/erased/erased-28.check b/tests/run-custom-args/erased/erased-28.check index 85733f6db2d7..3bd1f0e29744 100644 --- a/tests/run-custom-args/erased/erased-28.check +++ b/tests/run-custom-args/erased/erased-28.check @@ -1,4 +1,2 @@ -x foo -x bar diff --git a/tests/run-custom-args/erased/erased-class-are-erased.check b/tests/run-custom-args/erased/erased-class-are-erased.check new file mode 100644 index 000000000000..f64f5d8d85ac --- /dev/null +++ b/tests/run-custom-args/erased/erased-class-are-erased.check @@ -0,0 +1 @@ +27 diff --git a/tests/run-custom-args/erased/erased-class-are-erased.scala b/tests/run-custom-args/erased/erased-class-are-erased.scala new file mode 100644 index 000000000000..b48e0265c521 --- /dev/null +++ b/tests/run-custom-args/erased/erased-class-are-erased.scala @@ -0,0 +1,14 @@ +object Test { + erased class Erased() { + println("Oh no!!!") + } + + def f(x: Erased, y: Int = 0): Int = y + 5 + + def g() = Erased() + + def main(args: Array[String]) = + val y = Erased() + val z = 10 + println(f(Erased()) + z + f(g(), 7)) +} diff --git a/tests/run-custom-args/erased/lambdas.scala b/tests/run-custom-args/erased/lambdas.scala new file mode 100644 index 000000000000..4c1746283099 --- /dev/null +++ b/tests/run-custom-args/erased/lambdas.scala @@ -0,0 +1,38 @@ +// lambdas should parse and work + +type F = (erased Int, String) => String +type S = (Int, erased String) => Int + +def useF(f: F) = f(5, "a") +def useS(f: S) = f(5, "a") + +val ff: F = (erased x, y) => y + +val fs: S = (x, erased y) => x +val fsExpl = (x: Int, erased y: String) => x + +// contextual lambdas should work + +type FC = (Int, erased String) ?=> Int + +def useCtx(f: FC) = f(using 5, "a") + +val fCv: FC = (x, erased y) ?=> x +val fCvExpl = (x: Int, erased y: String) ?=> x + +// nested lambdas should work + +val nested: Int => (String, erased Int) => FC = a => (_, erased _) => (c, erased d) ?=> a + c + +@main def Test() = + assert("a" == useF(ff)) + + assert(5 == useS(fs)) + assert(5 == useS(fsExpl)) + assert(5 == useS { (x, erased y) => x }) + + assert(5 == useCtx(fCv)) + assert(5 == useCtx(fCvExpl)) + assert(5 == useCtx { (x, erased y) ?=> x }) + + assert(6 == useCtx(nested(1)("b", 2))) diff --git a/tests/run-custom-args/erased/quotes-add-erased.check b/tests/run-custom-args/erased/quotes-add-erased.check new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/tests/run-custom-args/erased/quotes-add-erased.check @@ -0,0 +1 @@ +1 diff --git a/tests/run-custom-args/erased/quotes-add-erased/Macro_1.scala b/tests/run-custom-args/erased/quotes-add-erased/Macro_1.scala new file mode 100644 index 000000000000..66f8475da96d --- /dev/null +++ b/tests/run-custom-args/erased/quotes-add-erased/Macro_1.scala @@ -0,0 +1,26 @@ +import scala.annotation.MacroAnnotation +import scala.annotation.internal.ErasedParam +import scala.quoted._ + +class NewAnnotation extends scala.annotation.Annotation + +class erasedParamsMethod extends MacroAnnotation: + def transform(using Quotes)(tree: quotes.reflect.Definition): List[quotes.reflect.Definition] = + import quotes.reflect._ + tree match + case ClassDef(name, ctr, parents, self, body) => + val erasedInt = AnnotatedType(TypeRepr.of[Int], '{ new ErasedParam }.asTerm) + val methType = MethodType(List("x", "y"))(_ => List(erasedInt, TypeRepr.of[Int]), _ => TypeRepr.of[Int]) + + assert(methType.hasErasedParams) + assert(methType.erasedParams == List(true, false)) + + val methSym = Symbol.newMethod(tree.symbol, "takesErased", methType, Flags.EmptyFlags, Symbol.noSymbol) + val methDef = DefDef(methSym, _ => Some(Literal(IntConstant(1)))) + + val clsDef = ClassDef.copy(tree)(name, ctr, parents, self, methDef :: body) + + List(clsDef) + case _ => + report.error("Annotation only supports `class`") + List(tree) diff --git a/tests/run-custom-args/erased/quotes-add-erased/Test_2.scala b/tests/run-custom-args/erased/quotes-add-erased/Test_2.scala new file mode 100644 index 000000000000..107fa0833e95 --- /dev/null +++ b/tests/run-custom-args/erased/quotes-add-erased/Test_2.scala @@ -0,0 +1,12 @@ +import scala.language.experimental.erasedDefinitions + +class TakesErased { + def takesErased(erased x: Int, y: Int): Int = ??? +} + +@erasedParamsMethod class Foo extends TakesErased + +@main def Test() = + val foo = Foo() + val v = foo.takesErased(1, 2) + println(v) diff --git a/tests/run-custom-args/erased/quotes-reflection.check b/tests/run-custom-args/erased/quotes-reflection.check new file mode 100644 index 000000000000..838479e0b7af --- /dev/null +++ b/tests/run-custom-args/erased/quotes-reflection.check @@ -0,0 +1,10 @@ +method : () isGiven=false isImplicit=false erasedArgs=List() +method m1: (i: scala.Int) isGiven=true isImplicit=false erasedArgs=List(false) +method m2: (i: scala.Int) isGiven=false isImplicit=false erasedArgs=List(true) +method m3: (i: scala.Int, j: scala.Int) isGiven=false isImplicit=false erasedArgs=List(false, true) +method m4: (i: EC) isGiven=false isImplicit=false erasedArgs=List(true) +val l1: scala.ContextFunction1[scala.Int, scala.Int] +val l2: scala.runtime.ErasedFunction with apply: (x$0: scala.Int @scala.annotation.internal.ErasedParam) isImplicit=false erasedParams=List(true) +val l3: scala.runtime.ErasedFunction with apply: (x$0: scala.Int @scala.annotation.internal.ErasedParam) isImplicit=true erasedParams=List(true) +val l4: scala.runtime.ErasedFunction with apply: (x$0: scala.Int, x$1: scala.Int @scala.annotation.internal.ErasedParam) isImplicit=false erasedParams=List(false, true) +val l5: scala.runtime.ErasedFunction with apply: (x$0: EC @scala.annotation.internal.ErasedParam) isImplicit=false erasedParams=List(true) diff --git a/tests/run-custom-args/erased/quotes-reflection/Macros_1.scala b/tests/run-custom-args/erased/quotes-reflection/Macros_1.scala new file mode 100644 index 000000000000..f7b1187433f0 --- /dev/null +++ b/tests/run-custom-args/erased/quotes-reflection/Macros_1.scala @@ -0,0 +1,35 @@ +import scala.quoted.* + +inline def inspect[A]: String = + ${ inspect2[A] } + +def inspect2[A: Type](using Quotes): Expr[String] = { + import quotes.reflect.* + + val methods = TypeRepr.of[A].typeSymbol.declarations + val names = methods.map { m => + m.tree match + case dd @ DefDef(name, params, r, body) => + val paramStr = + params.map { + case ps: TermParamClause => + val params = ps.params.map(p => s"${p.name}: ${p.tpt.show}").mkString("(", ", ", ")") + s"$params isGiven=${ps.isGiven} isImplicit=${ps.isImplicit} erasedArgs=${ps.erasedArgs}" + case ps: TypeParamClause => ps.params.map(_.show).mkString("[", ", ", "]") + }.mkString("") + s"method $name: $paramStr" + case vd @ ValDef(name, tpt, body) => + tpt.tpe match + case Refinement(parent, "apply", tpe: MethodType) if parent == defn.ErasedFunctionClass.typeRef => + assert(tpt.tpe.isErasedFunctionType) + + val params = tpe.paramNames.zip(tpe.paramTypes).map((n, t) => s"$n: ${t.show}").mkString("(", ", ", ")") + s"val $name: ${parent.show} with apply: ${params} isImplicit=${tpe.isImplicit} erasedParams=${tpe.erasedParams}" + case _ => + s"val $name: ${tpt.show}" + case td @ TypeDef(name, tpt) => s"type $name: ${tpt.show}" + case _ => s"something else: $m" + } + + Expr(names.mkString("\n")) +} diff --git a/tests/run-custom-args/erased/quotes-reflection/Test_2.scala b/tests/run-custom-args/erased/quotes-reflection/Test_2.scala new file mode 100644 index 000000000000..ce1cc8d3dff1 --- /dev/null +++ b/tests/run-custom-args/erased/quotes-reflection/Test_2.scala @@ -0,0 +1,20 @@ +import scala.language.experimental.erasedDefinitions + +erased class EC + +trait X { + def m1(using i: Int): Int + def m2(erased i: Int): Int + def m3(i: Int, erased j: Int): Int + def m4(i: EC): Int + + val l1 = (x: Int) ?=> 5 + val l2 = (erased x: Int) => 5 + val l3 = (erased x: Int) ?=> 5 + val l4 = (x: Int, erased y: Int) => 5 + val l5 = (x: EC) => 5 +} + +@main def Test = { + println(inspect[X]) +} diff --git a/tests/run-custom-args/run-macros-erased/macro-erased/1.scala b/tests/run-custom-args/run-macros-erased/macro-erased/1.scala index 567ef57b1c06..36f583a7dc91 100644 --- a/tests/run-custom-args/run-macros-erased/macro-erased/1.scala +++ b/tests/run-custom-args/run-macros-erased/macro-erased/1.scala @@ -13,8 +13,8 @@ object Macro { def case1(erased i: Expr[Int])(using Quotes): Expr[Int] = '{ 0 } def case2 (i: Int)(erased j: Expr[Int])(using Quotes): Expr[Int] = '{ 0 } def case3(erased i: Expr[Int]) (j: Int)(using Quotes): Expr[Int] = '{ 0 } - def case4 (h: Int)(erased i: Expr[Int], j: Expr[Int])(using Quotes): Expr[Int] = '{ 0 } - def case5(erased i: Expr[Int], j: Expr[Int]) (h: Int)(using Quotes): Expr[Int] = '{ 0 } + def case4 (h: Int)(erased i: Expr[Int], erased j: Expr[Int])(using Quotes): Expr[Int] = '{ 0 } + def case5(erased i: Expr[Int], erased j: Expr[Int]) (h: Int)(using Quotes): Expr[Int] = '{ 0 } def case6 (h: Int)(erased i: Expr[Int])(erased j: Expr[Int])(using Quotes): Expr[Int] = '{ 0 } def case7(erased i: Expr[Int]) (h: Int)(erased j: Expr[Int])(using Quotes): Expr[Int] = '{ 0 } def case8(erased i: Expr[Int])(erased j: Expr[Int]) (h: Int)(using Quotes): Expr[Int] = '{ 0 } diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index eff76720a7e2..2c49ca46349e 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -1,5 +1,6 @@ import scala.quoted.* import scala.tasty.inspector.* +import scala.language.experimental.erasedDefinitions val experimentalDefinitionInLibrary = Set( @@ -80,6 +81,21 @@ val experimentalDefinitionInLibrary = Set( "scala.quoted.Quotes.reflectModule.SymbolModule.newModule", "scala.quoted.Quotes.reflectModule.SymbolModule.freshName", "scala.quoted.Quotes.reflectModule.SymbolMethods.info", + // Quotes for functions with erased parameters. + "scala.quoted.Quotes.reflectModule.MethodTypeMethods.erasedParams", + "scala.quoted.Quotes.reflectModule.MethodTypeMethods.hasErasedParams", + "scala.quoted.Quotes.reflectModule.TermParamClauseMethods.erasedArgs", + "scala.quoted.Quotes.reflectModule.TermParamClauseMethods.hasErasedArgs", + "scala.quoted.Quotes.reflectModule.defnModule.ErasedFunctionClass", + + // New feature: functions with erased parameters. + // Need erasedDefinitions enabled. + "scala.runtime.ErasedFunction", + "scala.quoted.Quotes.reflectModule.MethodTypeMethods.erasedParams", + "scala.quoted.Quotes.reflectModule.MethodTypeMethods.hasErasedParams", + "scala.quoted.Quotes.reflectModule.TermParamClauseMethods.erasedArgs", + "scala.quoted.Quotes.reflectModule.TermParamClauseMethods.hasErasedArgs", + "scala.quoted.Quotes.reflectModule.defnModule.ErasedFunctionClass" ) diff --git a/tests/run-macros/i12021.check b/tests/run-macros/i12021.check index b244dce80b34..ef998c725209 100644 --- a/tests/run-macros/i12021.check +++ b/tests/run-macros/i12021.check @@ -1,3 +1,5 @@ -X1: (i: scala.Int) isImplicit=true, isGiven=false, isErased=false -X2: (i: scala.Int) isImplicit=false, isGiven=true, isErased=false -X3: (i: scala.Int) isImplicit=false, isGiven=false, isErased=true +X1: (i: scala.Int) isImplicit=true, isGiven=false, erasedArgs=List(false) +X2: (i: scala.Int) isImplicit=false, isGiven=true, erasedArgs=List(false) +X3: (i: scala.Int) isImplicit=false, isGiven=false, erasedArgs=List(true) +X4: (i: scala.Int, j: scala.Int) isImplicit=false, isGiven=false, erasedArgs=List(false, true) +X5: (i: EC) isImplicit=false, isGiven=false, erasedArgs=List(true) diff --git a/tests/run-macros/i12021/Macro_1.scala b/tests/run-macros/i12021/Macro_1.scala index 81703dfbab3d..25cab1786146 100644 --- a/tests/run-macros/i12021/Macro_1.scala +++ b/tests/run-macros/i12021/Macro_1.scala @@ -14,5 +14,5 @@ def inspect2[A: Type](using Quotes): Expr[String] = { val names = ps.params.map(p => s"${p.name}: ${p.tpt.show}").mkString("(", ", ", ")") - Expr(s"${Type.show[A]}: $names isImplicit=${ps.isImplicit}, isGiven=${ps.isGiven}, isErased=${ps.isErased}") + Expr(s"${Type.show[A]}: $names isImplicit=${ps.isImplicit}, isGiven=${ps.isGiven}, erasedArgs=${ps.erasedArgs}") } diff --git a/tests/run-macros/i12021/Test_2.scala b/tests/run-macros/i12021/Test_2.scala index 17f74792ece4..a542b14f1175 100644 --- a/tests/run-macros/i12021/Test_2.scala +++ b/tests/run-macros/i12021/Test_2.scala @@ -1,11 +1,17 @@ import scala.language.experimental.erasedDefinitions +erased class EC + class X1(implicit i: Int) class X2(using i: Int) class X3(erased i: Int) +class X4(i: Int, erased j: Int) +class X5(i: EC) @main def Test = { println(inspect[X1]) println(inspect[X2]) println(inspect[X3]) -} \ No newline at end of file + println(inspect[X4]) + println(inspect[X5]) +} diff --git a/tests/run-macros/tasty-definitions-1.check b/tests/run-macros/tasty-definitions-1.check index c2674c1b7a06..ce7251d7d3ee 100644 --- a/tests/run-macros/tasty-definitions-1.check +++ b/tests/run-macros/tasty-definitions-1.check @@ -108,56 +108,8 @@ ContextFunction24 ContextFunction24 ContextFunction25 ContextFunction25 -ErasedFunction1 -ErasedFunction2 -ErasedFunction3 -ErasedFunction4 -ErasedFunction5 -ErasedFunction6 -ErasedFunction7 -ErasedFunction8 -ErasedFunction9 -ErasedFunction10 -ErasedFunction11 -ErasedFunction12 -ErasedFunction13 -ErasedFunction14 -ErasedFunction15 -ErasedFunction16 -ErasedFunction17 -ErasedFunction18 -ErasedFunction19 -ErasedFunction20 -ErasedFunction21 -ErasedFunction22 -ErasedFunction23 -ErasedFunction24 -ErasedFunction25 -ErasedContextFunction1 -ErasedContextFunction2 -ErasedContextFunction3 -ErasedContextFunction4 -ErasedContextFunction5 -ErasedContextFunction6 -ErasedContextFunction7 -ErasedContextFunction8 -ErasedContextFunction9 -ErasedContextFunction10 -ErasedContextFunction11 -ErasedContextFunction12 -ErasedContextFunction13 -ErasedContextFunction14 -ErasedContextFunction15 -ErasedContextFunction16 -ErasedContextFunction17 -ErasedContextFunction18 -ErasedContextFunction19 -ErasedContextFunction20 -ErasedContextFunction21 -ErasedContextFunction22 -ErasedContextFunction23 -ErasedContextFunction24 -ErasedContextFunction25 +class java.lang.Exception: Erased function classes are not supported. Use a refined `scala.runtime.ErasedFunction` +ErasedFunction Tuple2 Tuple3 Tuple4 diff --git a/tests/run-macros/tasty-definitions-1/quoted_1.scala b/tests/run-macros/tasty-definitions-1/quoted_1.scala index baf0b929f202..bf9e28288486 100644 --- a/tests/run-macros/tasty-definitions-1/quoted_1.scala +++ b/tests/run-macros/tasty-definitions-1/quoted_1.scala @@ -63,11 +63,10 @@ object Macros { printout(defn.FunctionClass(i, isContextual = true).name) printout(defn.FunctionClass(i, isImplicit = true).name) - for (i <- 1 to 25) - printout(defn.FunctionClass(i, isErased = true).name) + // should fail + printout(defn.FunctionClass(1, isErased = true).name) - for (i <- 1 to 25) - printout(defn.FunctionClass(i, isImplicit = true, isErased = true).name) + printout(defn.ErasedFunctionClass.name) for (i <- 2 to 22) printout(defn.TupleClass(i).name) From da1b0289a68c7f3ccfb26494d5f107173fd96892 Mon Sep 17 00:00:00 2001 From: David Hua Date: Wed, 1 Mar 2023 04:55:00 -0500 Subject: [PATCH 215/657] Reword error messages to be more user friendly. --- .../tools/dotc/transform/init/Errors.scala | 8 ++--- .../tools/dotc/transform/init/Semantic.scala | 36 ++++++++++--------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Errors.scala b/compiler/src/dotty/tools/dotc/transform/init/Errors.scala index 762e029ba36f..366fd6be96a2 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Errors.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Errors.scala @@ -47,11 +47,11 @@ object Errors: case class AccessCold(field: Symbol)(val trace: Trace) extends Error: def show(using Context): String = - "Access field " + field.show + " on a cold object." + stacktrace + "Access field " + field.show + " on an uninitialized (Cold) object." + stacktrace case class CallCold(meth: Symbol)(val trace: Trace) extends Error: def show(using Context): String = - "Call method " + meth.show + " on a cold object." + stacktrace + "Call method " + meth.show + " on an uninitialized (Cold) object." + stacktrace case class CallUnknown(meth: Symbol)(val trace: Trace) extends Error: def show(using Context): String = @@ -62,7 +62,7 @@ object Errors: case class UnsafePromotion(msg: String, error: Error)(val trace: Trace) extends Error: def show(using Context): String = msg + stacktrace + "\n" + - "Promoting the value to hot (transitively initialized) failed due to the following problem:\n" + { + "Promoting the value to transitively initialized (Hot) failed due to the following problem:\n" + { val ctx2 = ctx.withProperty(IsFromPromotion, Some(true)) error.show(using ctx2) } @@ -96,5 +96,5 @@ object Errors: acc + text2 } val verb = if multiple then " are " else " is " - val adjective = "not hot (transitively initialized)." + val adjective = "not transitively initialized (Hot)." subject + verb + adjective diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 286e3a124d12..a53a032f9fce 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -64,16 +64,18 @@ object Semantic: sealed abstract class Value: def show(using Context): String = this match case ThisRef(klass) => - "ThisRef[" + klass.show + "]" + "the original object of type " + klass.show + " that started this trace" case Warm(klass, outer, ctor, args) => val argsText = if args.nonEmpty then ", args = " + args.map(_.show).mkString("(", ", ", ")") else "" - "Warm[" + klass.show + "] { outer = " + outer.show + argsText + " }" + "an initialized (Warm) object of type " + klass.show + "{ outer = " + outer.show + argsText + " }" case Fun(expr, thisV, klass) => - "Fun { this = " + thisV.show + ", owner = " + klass.show + " }" + "a function { this = " + thisV.show + ", owner = " + klass.show + " }" case RefSet(values) => values.map(_.show).mkString("Set { ", ", ", " }") - case _ => - this.toString() + case Hot => + "a transitively initialized (Hot) object" + case Cold => + "an uninitialized (Cold) object" def isHot = this == Hot def isCold = this == Cold @@ -470,7 +472,7 @@ object Semantic: def widenArg: Contextual[Value] = a match case _: Ref | _: Fun => - val hasError = Reporter.hasErrors { a.promote("Argument cannot be promoted to hot") } + val hasError = Reporter.hasErrors { a.promote("Argument cannot be promoted to transitively initialized (Hot)") } if hasError then Cold else Hot case RefSet(refs) => @@ -707,7 +709,9 @@ object Semantic: // no source code available promoteArgs() // try promoting the receiver as last resort - val hasErrors = Reporter.hasErrors { ref.promote("try promote value to hot") } + val hasErrors = Reporter.hasErrors { + ref.promote(ref.show + " has no source code and could not be proven to be transitively initialized (Hot).") + } if hasErrors then val error = CallUnknown(target)(trace) reporter.report(error) @@ -992,7 +996,7 @@ object Semantic: eval(body, thisV, klass) } given Trace = Trace.empty.add(body) - res.promote("The function return value is not hot. Found = " + res.show + ".") + res.promote("Only transitively initialized (Hot) values can be returned by functions. The function " + fun.show + " returned " + res.show + ".") } if errors.nonEmpty then reporter.report(UnsafePromotion(msg, errors.head)(trace)) @@ -1036,7 +1040,7 @@ object Semantic: // // This invariant holds because of the Scala/Java/JVM restriction that we cannot use `this` in super constructor calls. if subClassSegmentHot && !isHotSegment then - report.error("[Internal error] Expect current segment to hot in promotion, current klass = " + klass.show + + report.error("[Internal error] Expect current segment to be transitively initialized (hot) in promotion, current klass = " + klass.show + ", subclass = " + subClass.show + Trace.show, Trace.position) // If the outer and parameters of a class are all hot, then accessing fields and methods of the current @@ -1053,12 +1057,12 @@ object Semantic: val args = member.info.paramInfoss.flatten.map(_ => new ArgInfo(Hot: Value, Trace.empty)) val res = warm.call(member, args, receiver = warm.klass.typeRef, superType = NoType) withTrace(trace.add(member.defTree)) { - res.promote("Cannot prove that the return value of " + member.show + " is hot. Found = " + res.show + ".") + res.promote("Could not verify that the return value of " + member.show + " is transitively initialized (Hot). It was found to be " + res.show + ".") } else val res = warm.select(member, receiver = warm.klass.typeRef) withTrace(trace.add(member.defTree)) { - res.promote("Cannot prove that the field " + member.show + " is hot. Found = " + res.show + ".") + res.promote("Could not verify that the field " + member.show + " is transitively initialized (Hot). It was found to be " + res.show + ".") } end for @@ -1144,7 +1148,7 @@ object Semantic: extension (arg: ArgInfo) def promote: Contextual[Unit] = withTrace(arg.trace) { - arg.value.promote("Cannot prove the method argument is hot. Only hot values are safe to leak.\nFound = " + arg.value.show + ".") + arg.value.promote("Could not verify that the method argument is transitively initialized (Hot). It was found to be " + arg.value.show + ". Only transitively initialized arguments may be passed to methods outside the analyzed initialization region.") } /** Evaluate an expression with the given value for `this` in a given class `klass` @@ -1285,12 +1289,12 @@ object Semantic: eval(qual, thisV, klass) val res = eval(rhs, thisV, klass) extendTrace(expr) { - res.ensureHot("The RHS of reassignment must be hot. Found = " + res.show + ". ") + res.ensureHot("The RHS of reassignment must be transitively initialized (Hot). It was found to be " + res.show + ". ") } case id: Ident => val res = eval(rhs, thisV, klass) extendTrace(expr) { - res.ensureHot("The RHS of reassignment must be hot. Found = " + res.show + ". ") + res.ensureHot("The RHS of reassignment must be transitively initialized (Hot). It was found to be " + res.show + ". ") } case closureDef(ddef) => @@ -1313,14 +1317,14 @@ object Semantic: case Match(selector, cases) => val res = eval(selector, thisV, klass) extendTrace(selector) { - res.ensureHot("The value to be matched needs to be hot. Found = " + res.show + ". ") + res.ensureHot("The value to be matched needs to be transitively initialized (Hot). It was found to be " + res.show + ". ") } eval(cases.map(_.body), thisV, klass).join case Return(expr, from) => val res = eval(expr, thisV, klass) extendTrace(expr) { - res.ensureHot("return expression must be hot. Found = " + res.show + ". ") + res.ensureHot("return expression must be transitively initialized (Hot). It was found to be " + res.show + ". ") } case WhileDo(cond, body) => From 09b389b356f3baf6b6d8fa39bd50fb1a789874d5 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 1 Mar 2023 17:22:57 +0000 Subject: [PATCH 216/657] Change trytoShow's to not use raw string --- .../dotty/tools/dotc/core/Decorators.scala | 5 +---- .../tools/dotc/core/ShowDecoratorTest.scala | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 compiler/test/dotty/tools/dotc/core/ShowDecoratorTest.scala diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index 9f55e29c0f59..4ef0dbc9a43b 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -280,10 +280,7 @@ object Decorators { case ex: CyclicReference => "... (caught cyclic reference) ..." case NonFatal(ex) if !ctx.mode.is(Mode.PrintShowExceptions) && !ctx.settings.YshowPrintErrors.value => - val msg = ex match - case te: TypeError => te.toMessage.message - case _ => ex.getMessage - s"[cannot display due to $msg, raw string = $x]" + s"... (cannot display due to ${ex.className} ${ex.getMessage}) ..." case _ => String.valueOf(x).nn /** Returns the simple class name of `x`. */ diff --git a/compiler/test/dotty/tools/dotc/core/ShowDecoratorTest.scala b/compiler/test/dotty/tools/dotc/core/ShowDecoratorTest.scala new file mode 100644 index 000000000000..acc9d1914bf6 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/core/ShowDecoratorTest.scala @@ -0,0 +1,21 @@ +package dotty.tools +package dotc +package core + +import Contexts.*, Decorators.*, Denotations.*, SymDenotations.*, Symbols.*, Types.* +import printing.Formatting.Show + +import org.junit.Test +import org.junit.Assert.* + +class ShowDecoratorTest extends DottyTest: + import ShowDecoratorTest.* + + @Test def t1 = assertEquals("... (cannot display due to FooException boom) ...", Foo().tryToShow) +end ShowDecoratorTest + +object ShowDecoratorTest: + import printing.*, Texts.* + class FooException extends Exception("boom") + case class Foo() extends Showable: + def toText(printer: Printer): Text = throw new FooException From 378f5ff17f76c6370e9cba2f1c9775a055e0d377 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 1 Mar 2023 18:32:25 +0000 Subject: [PATCH 217/657] Add an enrichedErrorMessage on Run, so to only handle the first exception --- compiler/src/dotty/tools/dotc/Driver.scala | 10 +-- compiler/src/dotty/tools/dotc/Run.scala | 32 +++++++--- .../src/dotty/tools/dotc/core/Phases.scala | 4 +- compiler/src/dotty/tools/dotc/report.scala | 63 ++++++++++++++++++- .../src/dotty/tools/dotc/typer/ReTyper.scala | 10 ++- 5 files changed, 97 insertions(+), 22 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Driver.scala b/compiler/src/dotty/tools/dotc/Driver.scala index 5a2c8b7be56e..e548cae55ddd 100644 --- a/compiler/src/dotty/tools/dotc/Driver.scala +++ b/compiler/src/dotty/tools/dotc/Driver.scala @@ -30,18 +30,20 @@ class Driver { protected def doCompile(compiler: Compiler, files: List[AbstractFile])(using Context): Reporter = if files.nonEmpty then + var runOrNull = ctx.run try val run = compiler.newRun + runOrNull = run run.compile(files) finish(compiler, run) catch case ex: FatalError => report.error(ex.getMessage.nn) // signals that we should fail compilation. - case ex: TypeError => - println(s"${ex.toMessage} while compiling ${files.map(_.path).mkString(", ")}") + case ex: TypeError if !runOrNull.enrichedErrorMessage => + println(runOrNull.enrichErrorMessage(s"${ex.toMessage} while compiling ${files.map(_.path).mkString(", ")}")) throw ex - case ex: Throwable => - println(s"$ex while compiling ${files.map(_.path).mkString(", ")}") + case ex: Throwable if !runOrNull.enrichedErrorMessage => + println(runOrNull.enrichErrorMessage(s"Exception while compiling ${files.map(_.path).mkString(", ")}")) throw ex ctx.reporter diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 8cd1d160b42c..a46ded5d948b 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -173,15 +173,22 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint */ var ccImportEncountered = false + private var myEnrichedErrorMessage = false + + def enrichedErrorMessage: Boolean = myEnrichedErrorMessage + + def enrichErrorMessage(errorMessage: String)(using Context): String = + if enrichedErrorMessage then errorMessage + else + myEnrichedErrorMessage = true + report.enrichErrorMessage(errorMessage) + def compile(files: List[AbstractFile]): Unit = - try - val sources = files.map(runContext.getSource(_)) - compileSources(sources) - catch - case NonFatal(ex) => - if units.nonEmpty then report.echo(i"exception occurred while compiling $units%, %") - else report.echo(s"exception occurred while compiling ${files.map(_.name).mkString(", ")}") - throw ex + try compileSources(files.map(runContext.getSource(_))) + catch case NonFatal(ex) if !enrichedErrorMessage => + val files1 = if units.isEmpty then files else units.map(_.source.file) + report.echo(enrichErrorMessage(s"exception occurred while compiling ${files1.map(_.path)}")) + throw ex /** TODO: There's a fundamental design problem here: We assemble phases using `fusePhases` * when we first build the compiler. But we modify them with -Yskip, -Ystop @@ -398,3 +405,12 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint given runContext[Dummy_so_its_a_def]: Context = myCtx.nn assert(runContext.runId <= Periods.MaxPossibleRunId) } + +object Run { + extension (run: Run | Null) + def enrichedErrorMessage: Boolean = if run == null then false else run.enrichedErrorMessage + def enrichErrorMessage(errorMessage: String)(using Context): String = + if run == null + then report.enrichErrorMessage(errorMessage) + else run.enrichErrorMessage(errorMessage) +} diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 205554e418ed..00e017430a5f 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -322,8 +322,8 @@ object Phases { units.map { unit => val unitCtx = ctx.fresh.setPhase(this.start).setCompilationUnit(unit).withRootImports try run(using unitCtx) - catch case ex: Throwable => - println(s"$ex while running $phaseName on $unit") + catch case ex: Throwable if !ctx.run.enrichedErrorMessage => + println(ctx.run.enrichErrorMessage(s"unhandled exception while running $phaseName on $unit")) throw ex unitCtx.compilationUnit } diff --git a/compiler/src/dotty/tools/dotc/report.scala b/compiler/src/dotty/tools/dotc/report.scala index c92fbe5daa56..38f2ab347c4c 100644 --- a/compiler/src/dotty/tools/dotc/report.scala +++ b/compiler/src/dotty/tools/dotc/report.scala @@ -4,13 +4,12 @@ import reporting._ import Diagnostic._ import util.{SourcePosition, NoSourcePosition, SrcPos} import core._ -import Contexts._, Symbols._, Decorators._ +import Contexts._, Flags.*, Symbols._, Decorators._ import config.SourceVersion import ast._ import config.Feature.sourceVersion import java.lang.System.currentTimeMillis - object report: /** For sending messages that are printed only if -verbose is set */ @@ -129,4 +128,64 @@ object report: case Nil => pos recur(pos.sourcePos, tpd.enclosingInlineds) + private object messageRendering extends MessageRendering + + // Should only be called from Run#enrichErrorMessage. + def enrichErrorMessage(errorMessage: String)(using Context): String = try { + def formatExplain(pairs: List[(String, Any)]) = pairs.map((k, v) => f"$k%20s: $v").mkString("\n") + + val settings = ctx.settings.userSetSettings(ctx.settingsState).sortBy(_.name) + val tree = ctx.tree + val sym = tree.symbol + val pos = tree.sourcePos + val path = pos.source.path + val site = ctx.outersIterator.map(_.owner).filter(sym => !sym.exists || sym.isClass || sym.is(Method)).next() + + import untpd.* + extension (tree: Tree) def summaryString: String = tree match + case Literal(const) => s"Literal($const)" + case Ident(name) => s"Ident(${name.decode})" + case Select(qual, name) => s"Select(${qual.summaryString}, ${name.decode})" + case tree: NameTree => (if tree.isType then "type " else "") + tree.name.decode + case tree => s"${tree.className}${if tree.symbol.exists then s"(${tree.symbol})" else ""}" + + val info1 = formatExplain(List( + "while compiling" -> ctx.compilationUnit, + "during phase" -> ctx.phase.prevMega, + "mode" -> ctx.mode, + "library version" -> scala.util.Properties.versionString, + "compiler version" -> dotty.tools.dotc.config.Properties.versionString, + "settings" -> settings.map(s => if s.value == "" then s"${s.name} \"\"" else s"${s.name} ${s.value}").mkString(" "), + )) + val symbolInfos = if sym eq NoSymbol then List("symbol" -> sym) else List( + "symbol" -> sym.showLocated, + "symbol definition" -> s"${sym.showDcl} (a ${sym.className})", + "symbol package" -> sym.enclosingPackageClass.fullName, + "symbol owners" -> sym.showExtendedLocation, + ) + val info2 = formatExplain(List( + "tree" -> tree.summaryString, + "tree position" -> (if pos.exists then s"$path:${pos.line + 1}:${pos.column}" else s"$path:"), + "tree type" -> tree.typeOpt.show, + ) ::: symbolInfos ::: List( + "call site" -> s"${site.showLocated} in ${site.enclosingPackageClass}" + )) + val context_s = try + s""" == Source file context for tree position == + | + |${messageRendering.messageAndPos(Diagnostic.Error("", pos))}""".stripMargin + catch case _: Exception => "" + s""" + | $errorMessage + | + | An unhandled exception was thrown in the compiler. + | Please file a crash report here: + | https://github.com/lampepfl/dotty/issues/new/choose + | + |$info1 + | + |$info2 + | + |$context_s""".stripMargin + } catch case _: Throwable => errorMessage // don't introduce new errors trying to report errors, so swallow exceptions end report diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index b53b2f9ec57a..c64f541fd811 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -124,12 +124,10 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking override def typedUnadapted(tree: untpd.Tree, pt: Type, locked: TypeVars)(using Context): Tree = try super.typedUnadapted(tree, pt, locked) - catch { - case NonFatal(ex) => - if ctx.phase != Phases.typerPhase && ctx.phase != Phases.inliningPhase then - println(i"exception while typing $tree of class ${tree.getClass} # ${tree.uniqueId}") - throw ex - } + catch case NonFatal(ex) if ctx.phase != Phases.typerPhase && ctx.phase != Phases.inliningPhase && !ctx.run.enrichedErrorMessage => + val treeStr = tree.show(using ctx.withPhase(ctx.phase.prevMega)) + println(ctx.run.enrichErrorMessage(s"exception while retyping $treeStr of class ${tree.className} # ${tree.uniqueId}")) + throw ex override def inlineExpansion(mdef: DefDef)(using Context): List[Tree] = mdef :: Nil From 8020c77733b999259faf3fad342526de417c98d9 Mon Sep 17 00:00:00 2001 From: Lucas Leblanc <44496264+Dedelweiss@users.noreply.github.com> Date: Thu, 2 Mar 2023 11:17:44 +0100 Subject: [PATCH 218/657] Fix: Correct the nightly version cut off in scaladoc (#17024) In this commit I corrected the cut off of the nightly version by changing : - The width in auto for desktop version - The max-width in `calc(12 * var(--base-spacing));` for better responsiveness Desktop Before: Screenshot 2023-03-01 at 11 04 44 After: Screenshot 2023-03-01 at 11 04 27 Mobile Before: Screenshot 2023-03-01 at 11 13 07 After: Screenshot 2023-03-01 at 11 07 12 Fixes: #16588 --- scaladoc/resources/dotty_res/styles/theme/layout/header.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/header.css b/scaladoc/resources/dotty_res/styles/theme/layout/header.css index 2e33023d0701..cf3b91db2698 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/header.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/header.css @@ -72,7 +72,7 @@ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - width: calc(9 * var(--base-spacing)); + width: auto; } .single { @@ -103,6 +103,10 @@ #search-toggle { display: none; } + + .projectVersion{ + max-width: calc(12 * var(--base-spacing)); + } } From 20174d7d15a43918a1424c7e8eabfdaeb2703331 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Feb 2023 15:07:47 +0100 Subject: [PATCH 219/657] Replace quoted type variables in signature of HOAS pattern result To be able to construct the lambda returned by the HOAS pattern we need: first resolve the type variables and then use the result to construct the signature of the lambdas. To simplify this transformation, `QuoteMatcher` returns a `Seq[MatchResult]` instead of an untyped `Tuple` containing `Expr[?]`. The tuple is created once we have accumulated and processed all extracted values. Fixes #15165 --- .../quoted/runtime/impl/QuoteMatcher.scala | 87 +++++++++++++------ .../quoted/runtime/impl/QuotesImpl.scala | 23 +++-- tests/pos-macros/i15165a/Macro_1.scala | 9 ++ tests/pos-macros/i15165a/Test_2.scala | 4 + tests/pos-macros/i15165b/Macro_1.scala | 16 ++++ tests/pos-macros/i15165b/Test_2.scala | 4 + tests/pos-macros/i15165c/Macro_1.scala | 9 ++ tests/pos-macros/i15165c/Test_2.scala | 4 + 8 files changed, 122 insertions(+), 34 deletions(-) create mode 100644 tests/pos-macros/i15165a/Macro_1.scala create mode 100644 tests/pos-macros/i15165a/Test_2.scala create mode 100644 tests/pos-macros/i15165b/Macro_1.scala create mode 100644 tests/pos-macros/i15165b/Test_2.scala create mode 100644 tests/pos-macros/i15165c/Macro_1.scala create mode 100644 tests/pos-macros/i15165c/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 9a77cba97400..5477628a30a3 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -109,7 +109,7 @@ object QuoteMatcher { /** Sequence of matched expressions. * These expressions are part of the scrutinee and will be bound to the quote pattern term splices. */ - type MatchingExprs = Seq[Expr[Any]] + type MatchingExprs = Seq[MatchResult] /** A map relating equivalent symbols from the scrutinee and the pattern * For example in @@ -141,12 +141,13 @@ object QuoteMatcher { extension (scrutinee0: Tree) /** Check that the trees match and return the contents from the pattern holes. - * Return None if the trees do not match otherwise return Some of a tuple containing all the contents in the holes. + * Return a sequence containing all the contents in the holes. + * If it does not match, continues to the `optional` with `None`. * * @param scrutinee The tree being matched * @param pattern The pattern tree that the scrutinee should match. Contains `patternHole` holes. * @param `summon[Env]` Set of tuples containing pairs of symbols (s, p) where s defines a symbol in `scrutinee` which corresponds to symbol p in `pattern`. - * @return `None` if it did not match or `Some(tup: MatchingExprs)` if it matched where `tup` contains the contents of the holes. + * @return The sequence with the contents of the holes of the matched expression. */ private def =?= (pattern0: Tree)(using Env, Context): optional[MatchingExprs] = @@ -205,31 +206,12 @@ object QuoteMatcher { // Matches an open term and wraps it into a lambda that provides the free variables case Apply(TypeApply(Ident(_), List(TypeTree())), SeqLiteral(args, _) :: Nil) if pattern.symbol.eq(defn.QuotedRuntimePatterns_higherOrderHole) => - def hoasClosure = { - val names: List[TermName] = args.map { - case Block(List(DefDef(nme.ANON_FUN, _, _, Apply(Ident(name), _))), _) => name.asTermName - case arg => arg.symbol.name.asTermName - } - val argTypes = args.map(x => x.tpe.widenTermRefExpr) - val methTpe = MethodType(names)(_ => argTypes, _ => pattern.tpe) - val meth = newAnonFun(ctx.owner, methTpe) - def bodyFn(lambdaArgss: List[List[Tree]]): Tree = { - val argsMap = args.map(_.symbol).zip(lambdaArgss.head).toMap - val body = new TreeMap { - override def transform(tree: Tree)(using Context): Tree = - tree match - case tree: Ident => summon[Env].get(tree.symbol).flatMap(argsMap.get).getOrElse(tree) - case tree => super.transform(tree) - }.transform(scrutinee) - TreeOps(body).changeNonLocalOwners(meth) - } - Closure(meth, bodyFn) - } + val env = summon[Env] val capturedArgs = args.map(_.symbol) - val captureEnv = summon[Env].filter((k, v) => !capturedArgs.contains(v)) + val captureEnv = env.filter((k, v) => !capturedArgs.contains(v)) withEnv(captureEnv) { scrutinee match - case ClosedPatternTerm(scrutinee) => matched(hoasClosure) + case ClosedPatternTerm(scrutinee) => matchedOpen(scrutinee, pattern.tpe, args, env) case _ => notMatched } @@ -453,6 +435,52 @@ object QuoteMatcher { accumulator.apply(Set.empty, term) } + enum MatchResult: + /** Closed pattern extracted value + * @param tree Scrutinee sub-tree that matched + */ + case ClosedTree(tree: Tree) + /** HOAS pattern extracted value + * + * @param tree Scrutinee sub-tree that matched + * @param patternTpe Type of the pattern hole (from the pattern) + * @param args HOAS arguments (from the pattern) + * @param env Mapping between scrutinee and pattern variables + */ + case OpenTree(tree: Tree, patternTpe: Type, args: List[Tree], env: Env) + + /** Return the expression that was extracted from a hole. + * + * If it was a closed expression it returns that expression. Otherwise, + * if it is a HOAS pattern, the surrounding lambda is generated using + * `mapTypeHoles` to create the signature of the lambda. + * + * This expression is assumed to be a valid expression in the given splice scope. + */ + def toExpr(mapTypeHoles: TypeMap, spliceScope: Scope)(using Context): Expr[Any] = this match + case MatchResult.ClosedTree(tree) => + new ExprImpl(tree, spliceScope) + case MatchResult.OpenTree(tree, patternTpe, args, env) => + val names: List[TermName] = args.map { + case Block(List(DefDef(nme.ANON_FUN, _, _, Apply(Ident(name), _))), _) => name.asTermName + case arg => arg.symbol.name.asTermName + } + val paramTypes = args.map(x => mapTypeHoles(x.tpe.widenTermRefExpr)) + val methTpe = MethodType(names)(_ => paramTypes, _ => mapTypeHoles(patternTpe)) + val meth = newAnonFun(ctx.owner, methTpe) + def bodyFn(lambdaArgss: List[List[Tree]]): Tree = { + val argsMap = args.view.map(_.symbol).zip(lambdaArgss.head).toMap + val body = new TreeMap { + override def transform(tree: Tree)(using Context): Tree = + tree match + case tree: Ident => env.get(tree.symbol).flatMap(argsMap.get).getOrElse(tree) + case tree => super.transform(tree) + }.transform(tree) + TreeOps(body).changeNonLocalOwners(meth) + } + val hoasClosure = Closure(meth, bodyFn) + new ExprImpl(hoasClosure, spliceScope) + private inline def notMatched: optional[MatchingExprs] = optional.break() @@ -460,9 +488,14 @@ object QuoteMatcher { Seq.empty private inline def matched(tree: Tree)(using Context): MatchingExprs = - Seq(new ExprImpl(tree, SpliceScope.getCurrent)) + Seq(MatchResult.ClosedTree(tree)) + + private def matchedOpen(tree: Tree, patternTpe: Type, args: List[Tree], env: Env)(using Context): MatchingExprs = + Seq(MatchResult.OpenTree(tree, patternTpe, args, env)) extension (self: MatchingExprs) - private inline def &&& (that: MatchingExprs): MatchingExprs = self ++ that + /** Concatenates the contents of two successful matchings */ + def &&& (that: MatchingExprs): MatchingExprs = self ++ that + end extension } diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 378f3f6a5c40..a94f40a8d068 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -3137,18 +3137,27 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler ctx1.gadtState.addToConstraint(typeHoles) ctx1 - val matchings = QuoteMatcher.treeMatch(scrutinee, pat1)(using ctx1) - // After matching and doing all subtype checks, we have to approximate all the type bindings // that we have found, seal them in a quoted.Type and add them to the result def typeHoleApproximation(sym: Symbol) = val fromAboveAnnot = sym.hasAnnotation(dotc.core.Symbols.defn.QuotedRuntimePatterns_fromAboveAnnot) val fullBounds = ctx1.gadt.fullBounds(sym) - val tp = if fromAboveAnnot then fullBounds.hi else fullBounds.lo - reflect.TypeReprMethods.asType(tp) - matchings.map { tup => - val results = typeHoles.map(typeHoleApproximation) ++ tup - Tuple.fromIArray(results.toArray.asInstanceOf[IArray[Object]]) + if fromAboveAnnot then fullBounds.hi else fullBounds.lo + + QuoteMatcher.treeMatch(scrutinee, pat1)(using ctx1).map { matchings => + import QuoteMatcher.MatchResult.* + lazy val spliceScope = SpliceScope.getCurrent + val typeHoleApproximations = typeHoles.map(typeHoleApproximation) + val typeHoleMapping = Map(typeHoles.zip(typeHoleApproximations)*) + val typeHoleMap = new Types.TypeMap { + def apply(tp: Types.Type): Types.Type = tp match + case Types.TypeRef(Types.NoPrefix, _) => typeHoleMapping.getOrElse(tp.typeSymbol, tp) + case _ => mapOver(tp) + } + val matchedExprs = matchings.map(_.toExpr(typeHoleMap, spliceScope)) + val matchedTypes = typeHoleApproximations.map(reflect.TypeReprMethods.asType) + val results = matchedTypes ++ matchedExprs + Tuple.fromIArray(IArray.unsafeFromArray(results.toArray)) } } diff --git a/tests/pos-macros/i15165a/Macro_1.scala b/tests/pos-macros/i15165a/Macro_1.scala new file mode 100644 index 000000000000..8838d4c06bd1 --- /dev/null +++ b/tests/pos-macros/i15165a/Macro_1.scala @@ -0,0 +1,9 @@ +import scala.quoted.* + +inline def valToFun[T](inline expr: T): T = + ${ impl('expr) } + +def impl[T: Type](expr: Expr[T])(using quotes: Quotes): Expr[T] = + expr match + case '{ { val ident = ($a: α); $rest(ident): T } } => + '{ { (y: α) => $rest(y) }.apply(???) } diff --git a/tests/pos-macros/i15165a/Test_2.scala b/tests/pos-macros/i15165a/Test_2.scala new file mode 100644 index 000000000000..f7caa67b2df7 --- /dev/null +++ b/tests/pos-macros/i15165a/Test_2.scala @@ -0,0 +1,4 @@ +def test = valToFun { + val a: Int = 1 + a + 1 +} diff --git a/tests/pos-macros/i15165b/Macro_1.scala b/tests/pos-macros/i15165b/Macro_1.scala new file mode 100644 index 000000000000..5d62db37e313 --- /dev/null +++ b/tests/pos-macros/i15165b/Macro_1.scala @@ -0,0 +1,16 @@ +import scala.quoted.* + +inline def valToFun[T](inline expr: T): T = + ${ impl('expr) } + +def impl[T: Type](expr: Expr[T])(using quotes: Quotes): Expr[T] = + expr match + case '{ { val ident = ($a: α); $rest(ident): T } } => + '{ + { (y: α) => + ${ + val bound = '{ ${ rest }(y) } + Expr.betaReduce(bound) + } + }.apply($a) + } diff --git a/tests/pos-macros/i15165b/Test_2.scala b/tests/pos-macros/i15165b/Test_2.scala new file mode 100644 index 000000000000..f7caa67b2df7 --- /dev/null +++ b/tests/pos-macros/i15165b/Test_2.scala @@ -0,0 +1,4 @@ +def test = valToFun { + val a: Int = 1 + a + 1 +} diff --git a/tests/pos-macros/i15165c/Macro_1.scala b/tests/pos-macros/i15165c/Macro_1.scala new file mode 100644 index 000000000000..036363bf274f --- /dev/null +++ b/tests/pos-macros/i15165c/Macro_1.scala @@ -0,0 +1,9 @@ +import scala.quoted.* + +inline def valToFun[T](inline expr: T): T = + ${ impl('expr) } + +def impl[T: Type](expr: Expr[T])(using quotes: Quotes): Expr[T] = + expr match + case '{ type α; { val ident = ($a: `α`); $rest(ident): `α` & T } } => + '{ { (y: α) => $rest(y) }.apply(???) } diff --git a/tests/pos-macros/i15165c/Test_2.scala b/tests/pos-macros/i15165c/Test_2.scala new file mode 100644 index 000000000000..f7caa67b2df7 --- /dev/null +++ b/tests/pos-macros/i15165c/Test_2.scala @@ -0,0 +1,4 @@ +def test = valToFun { + val a: Int = 1 + a + 1 +} From f02f7dda6476a499858576a15c6edb2498650e1b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 3 Mar 2023 11:46:35 +0100 Subject: [PATCH 220/657] Cleanup test that prints onto the `compilationTest` output --- tests/neg-macros/i16582/Macro_1.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/neg-macros/i16582/Macro_1.scala b/tests/neg-macros/i16582/Macro_1.scala index dea58b89f017..c28c83166607 100644 --- a/tests/neg-macros/i16582/Macro_1.scala +++ b/tests/neg-macros/i16582/Macro_1.scala @@ -11,7 +11,6 @@ def ownerWorksImpl(in: Expr[Int])(using Quotes): Expr[String] = val position = Position.ofMacroExpansion val file = position.sourceFile val owner0 = Symbol.spliceOwner.maybeOwner - println("owner0 = " + owner0) val ownerName = owner0.tree match { case ValDef(name, _, _) => name From cf18183c6b6edd0e4949ab1e07713d62ca295ab2 Mon Sep 17 00:00:00 2001 From: kenji yoshida <6b656e6a69@gmail.com> Date: Sat, 4 Mar 2023 10:11:41 +0900 Subject: [PATCH 221/657] remove unnecessary cast in `transformStatement` --- library/src/scala/quoted/Quotes.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 79a4ea039ab5..7668617fa71c 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -4893,7 +4893,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => case self: ValDef => self } val body = tree.body.map(transformStatement(_)(tree.symbol)) - ClassDef.copy(tree)(tree.name, constructor.asInstanceOf[DefDef], parents, self, body) // cast as workaround for lampepfl/dotty#14821. TODO remove when referenceVersion >= 3.2.0-RC1 + ClassDef.copy(tree)(tree.name, constructor, parents, self, body) case tree: Import => Import.copy(tree)(transformTerm(tree.expr)(owner), tree.selectors) case tree: Export => From c417db0bf370ddc4a3e24f5b193c5c7f21d77aea Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Sat, 4 Mar 2023 02:09:44 -0800 Subject: [PATCH 222/657] Set `target="_blank"` for Source links in Scaladoc (#16282) Context: - https://github.com/erikerlandson/coulomb/issues/368 This seems to be necessary so that the source links work with javadoc.io. Note that Scaladoc 2 sets `target="_blank"`. --- .../src/dotty/tools/scaladoc/renderers/MemberRenderer.scala | 2 +- scaladoc/src/dotty/tools/scaladoc/util/html.scala | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index f2501378bf72..7133fe6d657f 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -72,7 +72,7 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext def source(m: Member): Seq[AppliedTag] = summon[DocContext].sourceLinks.pathTo(m).fold(Nil){ link => - tableRow("Source", a(href := link)(m.sources.fold("(source)")(_.path.getFileName().toString()))) + tableRow("Source", a(href := link, target := "_blank")(m.sources.fold("(source)")(_.path.getFileName().toString()))) } def deprecation(m: Member): Seq[AppliedTag] = m.deprecated.fold(Nil){ a => diff --git a/scaladoc/src/dotty/tools/scaladoc/util/html.scala b/scaladoc/src/dotty/tools/scaladoc/util/html.scala index e66ba3a4b706..72776a7413aa 100644 --- a/scaladoc/src/dotty/tools/scaladoc/util/html.scala +++ b/scaladoc/src/dotty/tools/scaladoc/util/html.scala @@ -108,6 +108,7 @@ object HTML: val onclick=Attr("onclick") val titleAttr =Attr("title") val onkeyup = Attr("onkeyup") + val target = Attr("target") def raw(content: String): AppliedTag = new AppliedTag(content) def raw(content: StringBuilder): AppliedTag = content From 137a6842181c353e8d76a9872be996dbc6d6f850 Mon Sep 17 00:00:00 2001 From: Ondra Pelech Date: Sun, 5 Mar 2023 02:12:56 +0100 Subject: [PATCH 223/657] Documentation: principle of optional braces --- docs/_docs/reference/syntax.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 8da41c7e6d0c..6abc3b2011d1 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -105,7 +105,10 @@ semi ::= ‘;’ | nl {nl} ## Optional Braces -The lexical analyzer also inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](./other-new-features/indentation.md). +The principle of optional braces is that any keyword that can be followed by `{` can also be followed by an indented block, without needing an intervening `:`. +(Allowing an optional `:` would be counterproductive since it would introduce several ways to do the same thing.) + +The lexical analyzer inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](./other-new-features/indentation.md). In the context-free productions below we use the notation `<<< ts >>>` to indicate a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent`. Analogously, the From a30176966dc7ec948284ab91a7e96a2420463f1e Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Sun, 5 Mar 2023 20:23:34 +0100 Subject: [PATCH 224/657] Scaladoc: Fix expand icon not changing on anchor link --- scaladoc/resources/dotty_res/scripts/ux.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scaladoc/resources/dotty_res/scripts/ux.js b/scaladoc/resources/dotty_res/scripts/ux.js index 304f2af9e129..0ead006af84d 100644 --- a/scaladoc/resources/dotty_res/scripts/ux.js +++ b/scaladoc/resources/dotty_res/scripts/ux.js @@ -309,6 +309,10 @@ document var selected = document.getElementById(location.hash.substring(1)); if (selected) { selected.classList.toggle("expand"); + selected.classList.toggle("expanded"); + const btn = selected.querySelector(".icon-button"); + btn.classList.toggle("expand"); + btn.classList.toggle("expanded"); } } } From 353d964842b5aa5028f605ffd523b3c031da3591 Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 6 Mar 2023 01:54:18 -0500 Subject: [PATCH 225/657] Remove unnecessary cacheResult=false, add test case. --- .../src/dotty/tools/dotc/transform/init/Semantic.scala | 2 +- tests/init/pos/recursion.scala | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/init/pos/recursion.scala diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 11376f427158..74e14691a45e 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -893,7 +893,7 @@ object Semantic: case Cold => Cold - case ref: Ref => eval(vdef.rhs, ref, enclosingClass, cacheResult = true) + case ref: Ref => eval(vdef.rhs, ref, enclosingClass) case _ => report.error("[Internal error] unexpected this value when accessing local variable, sym = " + sym.show + ", thisValue = " + thisValue2.show + Trace.show, Trace.position) diff --git a/tests/init/pos/recursion.scala b/tests/init/pos/recursion.scala new file mode 100644 index 000000000000..394092945cb3 --- /dev/null +++ b/tests/init/pos/recursion.scala @@ -0,0 +1,9 @@ +class A { + + val f: Int => Int = { + x => f(x) + } + + f(5) + +} From ff4610474e40960cf6354bd4afb4bb621b947b0c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 6 Mar 2023 09:16:14 +0100 Subject: [PATCH 226/657] Add regression test Closes #16943 --- tests/run/i16943.scala | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/run/i16943.scala diff --git a/tests/run/i16943.scala b/tests/run/i16943.scala new file mode 100644 index 000000000000..68e1f8fb5aa3 --- /dev/null +++ b/tests/run/i16943.scala @@ -0,0 +1,6 @@ +@main +@annotation.experimental +def Test(): Unit = fail(compiletime.erasedValue, 1) + +@annotation.experimental +def fail(dumb: CanThrow[Exception], x: Int) = println(x) From 0a1ceb9c2e4bccc26c1af4ed65c89046c380795c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 6 Mar 2023 09:31:15 +0100 Subject: [PATCH 227/657] Fix splice type variable pattern detection Fixes #17039 --- compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 2 +- tests/pos-macros/i17039.scala | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 tests/pos-macros/i17039.scala diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 65d8abfdf6a7..b00c10503b70 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -249,7 +249,7 @@ trait QuotesAndSplices { val pat1 = if (patType eq patType1) pat else pat.withType(patType1) patBuf += pat1 } - case Select(pat, _) if tree.symbol.isTypeSplice => + case Select(pat: Bind, _) if tree.symbol.isTypeSplice => val sym = tree.tpe.dealias.typeSymbol if sym.exists then val tdef = TypeDef(sym.asType).withSpan(sym.span) diff --git a/tests/pos-macros/i17039.scala b/tests/pos-macros/i17039.scala new file mode 100644 index 000000000000..6f983b138526 --- /dev/null +++ b/tests/pos-macros/i17039.scala @@ -0,0 +1,7 @@ +import scala.quoted.* + +def macroImpl(using Quotes) = + val t = summon[Type[Int]] + Type.of[Int] match + case '[t.Underlying] => '{true} + case _ => '{false} From bb5c0987d25ca1063ee15ca2c0060a6c1ec679b0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 6 Mar 2023 10:14:32 +0100 Subject: [PATCH 228/657] Dealias type references when healing types in quotes We where only doing the dealiasing if the type had no prefix but missed it if the type reference was on some path. Fixes #17037 --- .../dotc/transform/PCPCheckAndHeal.scala | 19 ++++++++++++++----- tests/pos-macros/i17037.scala | 8 ++++++++ tests/pos-macros/i17037b.scala | 10 ++++++++++ tests/pos-macros/i17037c.scala | 7 +++++++ 4 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 tests/pos-macros/i17037.scala create mode 100644 tests/pos-macros/i17037b.scala create mode 100644 tests/pos-macros/i17037c.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index e2e494a95074..6d3b62357d7f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -192,11 +192,9 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( case tp: TypeRef => tp.prefix match case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => - val tp1 = tp.dealias - if tp1 != tp then apply(tp1) - else tryHeal(tp.symbol, tp, pos) + tryHealTypeOfType(tp.symbol, tp, pos) case prefix: ThisType if !tp.symbol.isStatic && level > levelOf(prefix.cls) => - tryHeal(tp.symbol, tp, pos) + tryHealTypeOfType(tp.symbol, tp, pos) case prefix: TermRef if tp.symbol.isTypeSplice => prefix.symbol.info.argInfos match case (tb: TypeBounds) :: _ => @@ -205,7 +203,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( // Heal explicit type splice in the code if level > 0 then getQuoteTypeTags.getTagRef(prefix) else tp case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) => - tryHeal(prefix.symbol, tp, pos) + tryHealTypeOfType(prefix.symbol, tp, pos) case _ => mapOver(tp) case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) => @@ -217,6 +215,17 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( derivedAnnotatedType(tp, apply(tp.parent), tp.annot.derivedAnnotation(newAnnotTree)) case _ => mapOver(tp) + + /** Try to dealias or heal reference to type `T` used in a higher level than its definition. + * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a + * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. + * Emits and error if `T` cannot be healed and returns `T`. + */ + private def tryHealTypeOfType(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): Type = { + val tp1 = tp.dealias + if tp1 != tp then apply(tp1) + else tryHeal(tp.symbol, tp, pos) + } } /** Check phase consistency of terms and heal inconsistent type references. */ diff --git a/tests/pos-macros/i17037.scala b/tests/pos-macros/i17037.scala new file mode 100644 index 000000000000..1048d84ffe96 --- /dev/null +++ b/tests/pos-macros/i17037.scala @@ -0,0 +1,8 @@ +import scala.quoted.* + +class Foo: + type Bar = Int + +def macroImpl(using Quotes) = + val foo = new Foo + Type.of[foo.Bar] diff --git a/tests/pos-macros/i17037b.scala b/tests/pos-macros/i17037b.scala new file mode 100644 index 000000000000..60d2bec33330 --- /dev/null +++ b/tests/pos-macros/i17037b.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +class Foo: + type Bar = Int + +def macroImpl(using Quotes) = + val foo = Foo() + Type.of[foo.Bar] match + case '[foo.Bar] => '{true} + case _ => '{false} diff --git a/tests/pos-macros/i17037c.scala b/tests/pos-macros/i17037c.scala new file mode 100644 index 000000000000..56cd8f7a2d41 --- /dev/null +++ b/tests/pos-macros/i17037c.scala @@ -0,0 +1,7 @@ +import scala.quoted.* + +class Foo: + type Bar = Int + def macroImpl(using Quotes) = + val foo = new Foo + Type.of[this.Bar] From 585409a4d16aea90e60d2445595fa3c2798cc2c9 Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 6 Mar 2023 04:20:17 -0500 Subject: [PATCH 229/657] Update check files, minor wording changes to error messages --- .../tools/dotc/transform/init/Semantic.scala | 8 ++-- tests/init/neg/closureLeak.check | 22 +++++------ tests/init/neg/cycle-structure.check | 8 ++-- tests/init/neg/default-this.check | 21 +++++----- tests/init/neg/i15363.check | 4 +- tests/init/neg/i15459.check | 17 ++++----- tests/init/neg/inherit-non-hot.check | 26 ++++++------- tests/init/neg/inlined-method.check | 17 ++++----- tests/init/neg/inner-first.check | 13 +++---- tests/init/neg/leak-warm.check | 2 +- tests/init/neg/promotion-loop.check | 21 +++++----- tests/init/neg/promotion-segment3.check | 15 ++++---- tests/init/neg/secondary-ctor4.check | 8 ++-- tests/init/neg/t3273.check | 38 +++++++++---------- tests/init/neg/unsound1.check | 2 +- tests/init/neg/unsound2.check | 2 +- tests/init/neg/unsound3.check | 2 +- 17 files changed, 108 insertions(+), 118 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index a53a032f9fce..727e54833063 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -64,7 +64,7 @@ object Semantic: sealed abstract class Value: def show(using Context): String = this match case ThisRef(klass) => - "the original object of type " + klass.show + " that started this trace" + "the original object of type \"" + klass.show + "\" that started this trace" case Warm(klass, outer, ctor, args) => val argsText = if args.nonEmpty then ", args = " + args.map(_.show).mkString("(", ", ", ")") else "" "an initialized (Warm) object of type " + klass.show + "{ outer = " + outer.show + argsText + " }" @@ -472,7 +472,7 @@ object Semantic: def widenArg: Contextual[Value] = a match case _: Ref | _: Fun => - val hasError = Reporter.hasErrors { a.promote("Argument cannot be promoted to transitively initialized (Hot)") } + val hasError = Reporter.hasErrors { a.promote("Argument cannot be proven to be transitively initialized (Hot)") } if hasError then Cold else Hot case RefSet(refs) => @@ -996,7 +996,7 @@ object Semantic: eval(body, thisV, klass) } given Trace = Trace.empty.add(body) - res.promote("Only transitively initialized (Hot) values can be returned by functions. The function " + fun.show + " returned " + res.show + ".") + res.promote("Only transitively initialized (Hot) values can be returned by functions. The function " + fun.show + " returns " + res.show + ".") } if errors.nonEmpty then reporter.report(UnsafePromotion(msg, errors.head)(trace)) @@ -1040,7 +1040,7 @@ object Semantic: // // This invariant holds because of the Scala/Java/JVM restriction that we cannot use `this` in super constructor calls. if subClassSegmentHot && !isHotSegment then - report.error("[Internal error] Expect current segment to be transitively initialized (hot) in promotion, current klass = " + klass.show + + report.error("[Internal error] Expect current segment to be transitively initialized (Hot) in promotion, current klass = " + klass.show + ", subclass = " + subClass.show + Trace.show, Trace.position) // If the outer and parameters of a class are all hot, then accessing fields and methods of the current diff --git a/tests/init/neg/closureLeak.check b/tests/init/neg/closureLeak.check index 7019f2274ab6..c4e85d7db4b0 100644 --- a/tests/init/neg/closureLeak.check +++ b/tests/init/neg/closureLeak.check @@ -1,16 +1,14 @@ -- Error: tests/init/neg/closureLeak.scala:11:14 ----------------------------------------------------------------------- 11 | l.foreach(a => a.addX(this)) // error | ^^^^^^^^^^^^^^^^^ - | Cannot prove the method argument is hot. Only hot values are safe to leak. - | Found = Fun { this = ThisRef[class Outer], owner = class Outer }. Calling trace: - | -> class Outer { [ closureLeak.scala:1 ] - | ^ - | -> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] - | ^^^^^^^^^^^^^^^^^ + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function { this = the original object of type "class Outer" that started this trace, owner = class Outer }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |-> class Outer { [ closureLeak.scala:1 ] + | ^ + |-> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] + | ^^^^^^^^^^^^^^^^^ | - | Promoting the value to hot (transitively initialized) failed due to the following problem: - | Cannot prove the method argument is hot. Only hot values are safe to leak. - | Found = ThisRef[class Outer]. - | Non initialized field(s): value p. Promotion trace: - | -> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] - | ^^^^ + |Promoting the value to transitively initialized (Hot) failed due to the following problem: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class Outer" that started this trace. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Non initialized field(s): value p. Promotion trace: + |-> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] + | ^^^^ diff --git a/tests/init/neg/cycle-structure.check b/tests/init/neg/cycle-structure.check index fb7b54c7cac2..dfe7c9b85e2f 100644 --- a/tests/init/neg/cycle-structure.check +++ b/tests/init/neg/cycle-structure.check @@ -1,14 +1,14 @@ -- Error: tests/init/neg/cycle-structure.scala:3:13 -------------------------------------------------------------------- 3 | val x = B(this) // error | ^^^^^^^ - | Problematic object instantiation: arg 1 is not hot (transitively initialized). Calling trace: + | Problematic object instantiation: arg 1 is not transitively initialized (Hot). Calling trace: | -> case class A(b: B) { [ cycle-structure.scala:1 ] | ^ | -> val x = B(this) // error [ cycle-structure.scala:3 ] | ^^^^^^^ | | It leads to the following error during object initialization: - | Access field value x on a cold object. Calling trace: + | Access field value x on an uninitialized (Cold) object. Calling trace: | -> case class B(a: A) { [ cycle-structure.scala:7 ] | ^ | -> val x1 = a.x [ cycle-structure.scala:8 ] @@ -16,14 +16,14 @@ -- Error: tests/init/neg/cycle-structure.scala:9:13 -------------------------------------------------------------------- 9 | val x = A(this) // error | ^^^^^^^ - | Problematic object instantiation: arg 1 is not hot (transitively initialized). Calling trace: + | Problematic object instantiation: arg 1 is not transitively initialized (Hot). Calling trace: | -> case class B(a: A) { [ cycle-structure.scala:7 ] | ^ | -> val x = A(this) // error [ cycle-structure.scala:9 ] | ^^^^^^^ | | It leads to the following error during object initialization: - | Access field value x on a cold object. Calling trace: + | Access field value x on an uninitialized (Cold) object. Calling trace: | -> case class A(b: B) { [ cycle-structure.scala:1 ] | ^ | -> val x1 = b.x [ cycle-structure.scala:2 ] diff --git a/tests/init/neg/default-this.check b/tests/init/neg/default-this.check index 6d08a64450d4..873a83427aed 100644 --- a/tests/init/neg/default-this.check +++ b/tests/init/neg/default-this.check @@ -1,14 +1,13 @@ -- Error: tests/init/neg/default-this.scala:9:8 ------------------------------------------------------------------------ 9 | compare() // error | ^^^^^^^ - | Cannot prove the method argument is hot. Only hot values are safe to leak. - | Found = ThisRef[class B]. - | Non initialized field(s): value result. Calling trace: - | -> class B extends A { [ default-this.scala:6 ] - | ^ - | -> val result = updateThenCompare(5) [ default-this.scala:11 ] - | ^^^^^^^^^^^^^^^^^^^^ - | -> def updateThenCompare(c: Int): Boolean = { [ default-this.scala:7 ] - | ^ - | -> compare() // error [ default-this.scala:9 ] - | ^^^^^^^ + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class B" that started this trace. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Non initialized field(s): value result. Calling trace: + |-> class B extends A { [ default-this.scala:6 ] + | ^ + |-> val result = updateThenCompare(5) [ default-this.scala:11 ] + | ^^^^^^^^^^^^^^^^^^^^ + |-> def updateThenCompare(c: Int): Boolean = { [ default-this.scala:7 ] + | ^ + |-> compare() // error [ default-this.scala:9 ] + | ^^^^^^^ diff --git a/tests/init/neg/i15363.check b/tests/init/neg/i15363.check index 84cf268ef8a1..9912aa186a5b 100644 --- a/tests/init/neg/i15363.check +++ b/tests/init/neg/i15363.check @@ -1,14 +1,14 @@ -- Error: tests/init/neg/i15363.scala:3:10 ----------------------------------------------------------------------------- 3 | val b = new B(this) // error | ^^^^^^^^^^^ - | Problematic object instantiation: arg 1 is not hot (transitively initialized). Calling trace: + | Problematic object instantiation: arg 1 is not transitively initialized (Hot). Calling trace: | -> class A: [ i15363.scala:1 ] | ^ | -> val b = new B(this) // error [ i15363.scala:3 ] | ^^^^^^^^^^^ | | It leads to the following error during object initialization: - | Access field value m on a cold object. Calling trace: + | Access field value m on an uninitialized (Cold) object. Calling trace: | -> class B(a: A): [ i15363.scala:7 ] | ^ | -> val x = a.m [ i15363.scala:8 ] diff --git a/tests/init/neg/i15459.check b/tests/init/neg/i15459.check index 93ba28554895..a4ba1bacf3a2 100644 --- a/tests/init/neg/i15459.check +++ b/tests/init/neg/i15459.check @@ -1,12 +1,11 @@ -- Error: tests/init/neg/i15459.scala:3:10 ----------------------------------------------------------------------------- 3 | println(this) // error | ^^^^ - | Cannot prove the method argument is hot. Only hot values are safe to leak. - | Found = ThisRef[class Sub]. - | Non initialized field(s): value b. Calling trace: - | -> class Sub extends Sup: [ i15459.scala:5 ] - | ^ - | -> class Sup: [ i15459.scala:1 ] - | ^ - | -> println(this) // error [ i15459.scala:3 ] - | ^^^^ + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class Sub" that started this trace. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Non initialized field(s): value b. Calling trace: + |-> class Sub extends Sup: [ i15459.scala:5 ] + | ^ + |-> class Sup: [ i15459.scala:1 ] + | ^ + |-> println(this) // error [ i15459.scala:3 ] + | ^^^^ diff --git a/tests/init/neg/inherit-non-hot.check b/tests/init/neg/inherit-non-hot.check index 408196333a27..a62641473ab2 100644 --- a/tests/init/neg/inherit-non-hot.check +++ b/tests/init/neg/inherit-non-hot.check @@ -1,17 +1,17 @@ -- Error: tests/init/neg/inherit-non-hot.scala:6:32 -------------------------------------------------------------------- 6 | if b == null then b = new B(this) // error | ^^^^^^^^^^^^^^^ - | The RHS of reassignment must be hot. Found = Warm[class B] { outer = Hot, args = (Cold) }. Calling trace: - | -> class C extends A { [ inherit-non-hot.scala:15 ] - | ^ - | -> val bAgain = toB.getBAgain [ inherit-non-hot.scala:16 ] - | ^^^ - | -> def toB: B = [ inherit-non-hot.scala:5 ] - | ^ - | -> if b == null then b = new B(this) // error [ inherit-non-hot.scala:6 ] - | ^^^^^^^^^^^^^^^ + |The RHS of reassignment must be transitively initialized (Hot). It was found to be an initialized (Warm) object of type class B{ outer = a transitively initialized (Hot) object, args = (an uninitialized (Cold) object) }. Calling trace: + |-> class C extends A { [ inherit-non-hot.scala:15 ] + | ^ + |-> val bAgain = toB.getBAgain [ inherit-non-hot.scala:16 ] + | ^^^ + |-> def toB: B = [ inherit-non-hot.scala:5 ] + | ^ + |-> if b == null then b = new B(this) // error [ inherit-non-hot.scala:6 ] + | ^^^^^^^^^^^^^^^ | - | Promoting the value to hot (transitively initialized) failed due to the following problem: - | Cannot prove that the field value a is hot. Found = Cold. Promotion trace: - | -> class B(a: A) { [ inherit-non-hot.scala:10 ] - | ^^^^ + |Promoting the value to transitively initialized (Hot) failed due to the following problem: + |Could not verify that the field value a is transitively initialized (Hot). It was found to be an uninitialized (Cold) object. Promotion trace: + |-> class B(a: A) { [ inherit-non-hot.scala:10 ] + | ^^^^ diff --git a/tests/init/neg/inlined-method.check b/tests/init/neg/inlined-method.check index 62bec184b825..d202c668b9b4 100644 --- a/tests/init/neg/inlined-method.check +++ b/tests/init/neg/inlined-method.check @@ -1,12 +1,11 @@ -- Error: tests/init/neg/inlined-method.scala:8:45 --------------------------------------------------------------------- 8 | scala.runtime.Scala3RunTime.assertFailed(message) // error | ^^^^^^^ - | Cannot prove the method argument is hot. Only hot values are safe to leak. - | Found = ThisRef[class InlineError]. - | Non initialized field(s): value v. Calling trace: - | -> class InlineError { [ inlined-method.scala:1 ] - | ^ - | -> Assertion.failAssert(this) [ inlined-method.scala:2 ] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -> scala.runtime.Scala3RunTime.assertFailed(message) // error [ inlined-method.scala:8 ] - | ^^^^^^^ + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class InlineError" that started this trace. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Non initialized field(s): value v. Calling trace: + |-> class InlineError { [ inlined-method.scala:1 ] + | ^ + |-> Assertion.failAssert(this) [ inlined-method.scala:2 ] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + |-> scala.runtime.Scala3RunTime.assertFailed(message) // error [ inlined-method.scala:8 ] + | ^^^^^^^ diff --git a/tests/init/neg/inner-first.check b/tests/init/neg/inner-first.check index e1df69fbd4a2..5719cf56d9aa 100644 --- a/tests/init/neg/inner-first.check +++ b/tests/init/neg/inner-first.check @@ -1,10 +1,9 @@ -- Error: tests/init/neg/inner-first.scala:3:12 ------------------------------------------------------------------------ 3 | println(this) // error | ^^^^ - | Cannot prove the method argument is hot. Only hot values are safe to leak. - | Found = ThisRef[class B]. - | Non initialized field(s): value n. Calling trace: - | -> class B: [ inner-first.scala:2 ] - | ^ - | -> println(this) // error [ inner-first.scala:3 ] - | ^^^^ + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class B" that started this trace. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Non initialized field(s): value n. Calling trace: + |-> class B: [ inner-first.scala:2 ] + | ^ + |-> println(this) // error [ inner-first.scala:3 ] + | ^^^^ diff --git a/tests/init/neg/leak-warm.check b/tests/init/neg/leak-warm.check index d4d563fc456e..c2fc561a3668 100644 --- a/tests/init/neg/leak-warm.check +++ b/tests/init/neg/leak-warm.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/leak-warm.scala:19:18 ------------------------------------------------------------------------- 19 | val l2 = l.map(_.m()) // error | ^^^^^^^^^^^^ - | Call method method map on a cold object. Calling trace: + | Call method method map on an uninitialized (Cold) object. Calling trace: | -> object leakWarm { [ leak-warm.scala:1 ] | ^ | -> val l2 = l.map(_.m()) // error [ leak-warm.scala:19 ] diff --git a/tests/init/neg/promotion-loop.check b/tests/init/neg/promotion-loop.check index 3d1eb7e74aec..9f67434be26a 100644 --- a/tests/init/neg/promotion-loop.check +++ b/tests/init/neg/promotion-loop.check @@ -1,15 +1,14 @@ -- Error: tests/init/neg/promotion-loop.scala:16:10 -------------------------------------------------------------------- 16 | println(b) // error | ^ - | Cannot prove the method argument is hot. Only hot values are safe to leak. - | Found = Warm[class B] { outer = ThisRef[class Test] }. Calling trace: - | -> class Test { test => [ promotion-loop.scala:1 ] - | ^ - | -> println(b) // error [ promotion-loop.scala:16 ] - | ^ + |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type class B{ outer = the original object of type "class Test" that started this trace }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |-> class Test { test => [ promotion-loop.scala:1 ] + | ^ + |-> println(b) // error [ promotion-loop.scala:16 ] + | ^ | - | Promoting the value to hot (transitively initialized) failed due to the following problem: - | Cannot prove that the field value outer is hot. Found = ThisRef[class Test]. - | Non initialized field(s): value n. Promotion trace: - | -> val outer = test [ promotion-loop.scala:12 ] - | ^^^^^^^^^^^^^^^^ + |Promoting the value to transitively initialized (Hot) failed due to the following problem: + |Could not verify that the field value outer is transitively initialized (Hot). It was found to be the original object of type "class Test" that started this trace. + |Non initialized field(s): value n. Promotion trace: + |-> val outer = test [ promotion-loop.scala:12 ] + | ^^^^^^^^^^^^^^^^ diff --git a/tests/init/neg/promotion-segment3.check b/tests/init/neg/promotion-segment3.check index 220af18bd29a..b334439b4168 100644 --- a/tests/init/neg/promotion-segment3.check +++ b/tests/init/neg/promotion-segment3.check @@ -1,12 +1,11 @@ -- Error: tests/init/neg/promotion-segment3.scala:9:6 ------------------------------------------------------------------ 9 | bar(new B) // error | ^^^^^ - | Cannot prove the method argument is hot. Only hot values are safe to leak. - | Found = Warm[class B] { outer = ThisRef[class A] }. Calling trace: - | -> class A: [ promotion-segment3.scala:2 ] - | ^ - | -> bar(new B) // error [ promotion-segment3.scala:9 ] - | ^^^^^ + |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type class B{ outer = the original object of type "class A" that started this trace }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |-> class A: [ promotion-segment3.scala:2 ] + | ^ + |-> bar(new B) // error [ promotion-segment3.scala:9 ] + | ^^^^^ | - | Promoting the value to hot (transitively initialized) failed due to the following problem: - | Promotion cancelled as the value contains inner class C. + |Promoting the value to transitively initialized (Hot) failed due to the following problem: + |Promotion cancelled as the value contains inner class C. diff --git a/tests/init/neg/secondary-ctor4.check b/tests/init/neg/secondary-ctor4.check index 1bf1a7286357..e867ba65ded5 100644 --- a/tests/init/neg/secondary-ctor4.check +++ b/tests/init/neg/secondary-ctor4.check @@ -1,14 +1,14 @@ -- Error: tests/init/neg/secondary-ctor4.scala:54:14 ------------------------------------------------------------------- 54 | val c = new C(b, 5) // error | ^^^^^^^^^^^ - | Problematic object instantiation: arg 1 is not hot (transitively initialized). Calling trace: + | Problematic object instantiation: arg 1 is not transitively initialized (Hot). Calling trace: | -> class D { [ secondary-ctor4.scala:52 ] | ^ | -> val c = new C(b, 5) // error [ secondary-ctor4.scala:54 ] | ^^^^^^^^^^^ | | It leads to the following error during object initialization: - | Access field value n on a cold object. Calling trace: + | Access field value n on an uninitialized (Cold) object. Calling trace: | -> def this(b: B, x: Int) = this(b) [ secondary-ctor4.scala:49 ] | ^^^^^^^ | -> class C(b: B) extends A(b) with T { [ secondary-ctor4.scala:48 ] @@ -24,7 +24,7 @@ -- Error: tests/init/neg/secondary-ctor4.scala:42:4 -------------------------------------------------------------------- 42 | new A(new B(new D)) // error | ^^^^^^^^^^^^^^^^^^^ - |Problematic object instantiation: the outer M.this and arg 1 are not hot (transitively initialized). Calling trace: + |Problematic object instantiation: the outer M.this and arg 1 are not transitively initialized (Hot). Calling trace: |-> class N(d: D) extends M(d) { [ secondary-ctor4.scala:59 ] | ^ |-> def this(d: D) = { [ secondary-ctor4.scala:7 ] @@ -33,7 +33,7 @@ | ^^^^^^^^^^^^^^^^^^^ | |It leads to the following error during object initialization: - |Access field value n on a cold object. Calling trace: + |Access field value n on an uninitialized (Cold) object. Calling trace: |-> def this(b: B) = { [ secondary-ctor4.scala:17 ] | ^ |-> Inner().foo() [ secondary-ctor4.scala:26 ] diff --git a/tests/init/neg/t3273.check b/tests/init/neg/t3273.check index e548a5964cac..b5d4c30822a7 100644 --- a/tests/init/neg/t3273.check +++ b/tests/init/neg/t3273.check @@ -1,28 +1,26 @@ -- Error: tests/init/neg/t3273.scala:4:42 ------------------------------------------------------------------------------ 4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error | ^^^^^^^^^^^^^^^ - | Cannot prove the method argument is hot. Only hot values are safe to leak. - | Found = Fun { this = ThisRef[object Test], owner = object Test }. Calling trace: - | -> object Test { [ t3273.scala:3 ] - | ^ - | -> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ] - | ^^^^^^^^^^^^^^^ + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function { this = the original object of type "object Test" that started this trace, owner = object Test }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |-> object Test { [ t3273.scala:3 ] + | ^ + |-> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ] + | ^^^^^^^^^^^^^^^ | - | Promoting the value to hot (transitively initialized) failed due to the following problem: - | Access non-initialized value num1. Promotion trace: - | -> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ] - | ^^^^ + |Promoting the value to transitively initialized (Hot) failed due to the following problem: + |Access non-initialized value num1. Promotion trace: + |-> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ] + | ^^^^ -- Error: tests/init/neg/t3273.scala:5:61 ------------------------------------------------------------------------------ 5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Cannot prove the method argument is hot. Only hot values are safe to leak. - | Found = Fun { this = ThisRef[object Test], owner = object Test }. Calling trace: - | -> object Test { [ t3273.scala:3 ] - | ^ - | -> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function { this = the original object of type "object Test" that started this trace, owner = object Test }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |-> object Test { [ t3273.scala:3 ] + | ^ + |-> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - | Promoting the value to hot (transitively initialized) failed due to the following problem: - | Access non-initialized value num2. Promotion trace: - | -> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ] - | ^^^^ + |Promoting the value to transitively initialized (Hot) failed due to the following problem: + |Access non-initialized value num2. Promotion trace: + |-> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ] + | ^^^^ diff --git a/tests/init/neg/unsound1.check b/tests/init/neg/unsound1.check index c3057a6a6067..d114ba072db6 100644 --- a/tests/init/neg/unsound1.check +++ b/tests/init/neg/unsound1.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/unsound1.scala:2:35 --------------------------------------------------------------------------- 2 | if (m > 0) println(foo(m - 1).a2.n) // error | ^^^^^^^^^^^^^^^ - | Access field variable n on a cold object. Calling trace: + | Access field variable n on an uninitialized (Cold) object. Calling trace: | -> class A(m: Int) { [ unsound1.scala:1 ] | ^ | -> if (m > 0) println(foo(m - 1).a2.n) // error [ unsound1.scala:2 ] diff --git a/tests/init/neg/unsound2.check b/tests/init/neg/unsound2.check index a90b16c8bf71..69d1278e94df 100644 --- a/tests/init/neg/unsound2.check +++ b/tests/init/neg/unsound2.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/unsound2.scala:5:26 --------------------------------------------------------------------------- 5 | def getN: Int = a.n // error | ^^^ - | Access field value n on a cold object. Calling trace: + | Access field value n on an uninitialized (Cold) object. Calling trace: | -> case class A(x: Int) { [ unsound2.scala:1 ] | ^ | -> println(foo(x).getB) [ unsound2.scala:8 ] diff --git a/tests/init/neg/unsound3.check b/tests/init/neg/unsound3.check index d62b97e1abaf..c32e66272d1a 100644 --- a/tests/init/neg/unsound3.check +++ b/tests/init/neg/unsound3.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/unsound3.scala:10:38 -------------------------------------------------------------------------- 10 | if (x < 12) then foo().getC().b else newB // error | ^^^^^^^^^^^^^^ - | Access field value b on a cold object. Calling trace: + | Access field value b on an uninitialized (Cold) object. Calling trace: | -> class C { [ unsound3.scala:5 ] | ^ | -> val b = foo() [ unsound3.scala:12 ] From 099f9f2b4b84aeca7fd9ca5782e80e5ef8fa3912 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 6 Mar 2023 14:28:22 +0000 Subject: [PATCH 230/657] Unduplicate enrich{,ed}ErrorMessage Having a same-named enrichement will cause the original to be called if/when explicit nulls is switched off - which leads to the original method to be called, meaning an NPE will be thrown. So, instead, move all the functionality as an extension method, so we don't need mangled names. --- compiler/src/dotty/tools/dotc/Run.scala | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index a46ded5d948b..944ae794c94f 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -175,19 +175,11 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint private var myEnrichedErrorMessage = false - def enrichedErrorMessage: Boolean = myEnrichedErrorMessage - - def enrichErrorMessage(errorMessage: String)(using Context): String = - if enrichedErrorMessage then errorMessage - else - myEnrichedErrorMessage = true - report.enrichErrorMessage(errorMessage) - def compile(files: List[AbstractFile]): Unit = try compileSources(files.map(runContext.getSource(_))) - catch case NonFatal(ex) if !enrichedErrorMessage => + catch case NonFatal(ex) if !this.enrichedErrorMessage => val files1 = if units.isEmpty then files else units.map(_.source.file) - report.echo(enrichErrorMessage(s"exception occurred while compiling ${files1.map(_.path)}")) + report.echo(this.enrichErrorMessage(s"exception occurred while compiling ${files1.map(_.path)}")) throw ex /** TODO: There's a fundamental design problem here: We assemble phases using `fusePhases` @@ -408,9 +400,13 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint object Run { extension (run: Run | Null) - def enrichedErrorMessage: Boolean = if run == null then false else run.enrichedErrorMessage + def enrichedErrorMessage: Boolean = if run == null then false else run.myEnrichedErrorMessage def enrichErrorMessage(errorMessage: String)(using Context): String = - if run == null - then report.enrichErrorMessage(errorMessage) - else run.enrichErrorMessage(errorMessage) + if run == null then + report.enrichErrorMessage(errorMessage) + else if !run.enrichedErrorMessage then + run.myEnrichedErrorMessage = true + report.enrichErrorMessage(errorMessage) + else + errorMessage } From 8595d951a9adcc2c57f7ea17b83fb854cae1cf7a Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 6 Mar 2023 22:02:01 -0500 Subject: [PATCH 231/657] Add optimization to reduce extra iterations of the safe init checker. --- .../src/dotty/tools/dotc/transform/init/Cache.scala | 9 ++++++++- .../dotty/tools/dotc/transform/init/Semantic.scala | 13 ++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala index 14a52d995131..080c56d6fb5f 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala @@ -75,6 +75,8 @@ class Cache[Config, Res]: */ protected var changed: Boolean = false + protected var cacheUsed: Boolean = false + /** Used to avoid allocation, its state does not matter */ protected given MutableTreeWrapper = new MutableTreeWrapper @@ -99,7 +101,9 @@ class Cache[Config, Res]: */ def cachedEval(config: Config, expr: Tree, cacheResult: Boolean, default: Res)(eval: Tree => Res): Res = this.get(config, expr) match - case Some(value) => value + case Some(value) => + cacheUsed = true + value case None => val assumeValue: Res = this.last.get(config, expr) match @@ -124,6 +128,8 @@ class Cache[Config, Res]: def hasChanged = changed + def isUsed = cacheUsed + /** Prepare cache for the next iteration * * 1. Reset changed flag. @@ -132,6 +138,7 @@ class Cache[Config, Res]: */ def prepareForNextIteration()(using Context) = this.changed = false + this.cacheUsed = false this.last = this.current this.current = Map.empty end Cache diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 286e3a124d12..49d9ae4d2b2a 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -1101,12 +1101,15 @@ object Semantic: * * The class to be checked must be an instantiable concrete class. */ - private def checkClass(classSym: ClassSymbol)(using Cache.Data, Context): Unit = + private def checkClass(classSym: ClassSymbol)(using Cache.Data, Context): Int = val thisRef = ThisRef(classSym) val tpl = classSym.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template] + var accum = 0 + @tailrec def iterate(): Unit = { + accum += 1 given Promoted = Promoted.empty(classSym) given Trace = Trace.empty.add(classSym.defTree) given reporter: Reporter.BufferedReporter = new Reporter.BufferedReporter @@ -1120,7 +1123,7 @@ object Semantic: log("checking " + classSym) { eval(tpl, thisRef, classSym) } reporter.errors.foreach(_.issue) - if cache.hasChanged && reporter.errors.isEmpty then + if cache.hasChanged && reporter.errors.isEmpty && cache.isUsed then // code to prepare cache and heap for next iteration cache.prepareForNextIteration() iterate() @@ -1129,15 +1132,19 @@ object Semantic: } iterate() + + accum - 1 end checkClass /** * Check the specified concrete classes */ def checkClasses(classes: List[ClassSymbol])(using Context): Unit = + var total = 0 given Cache.Data() for classSym <- classes if isConcreteClass(classSym) do - checkClass(classSym) + total += checkClass(classSym) + System.err.nn.println(total) // ----- Semantic definition -------------------------------- type ArgInfo = TraceValue[Value] From 4cc629ec06ea6bbd678bd06294eae16ce86a6186 Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 6 Mar 2023 23:00:09 -0500 Subject: [PATCH 232/657] Reword the stringified versions of some objects --- compiler/src/dotty/tools/dotc/transform/init/Semantic.scala | 4 ++-- tests/init/neg/closureLeak.check | 4 ++-- tests/init/neg/default-this.check | 2 +- tests/init/neg/i15459.check | 2 +- tests/init/neg/inlined-method.check | 2 +- tests/init/neg/inner-first.check | 2 +- tests/init/neg/promotion-loop.check | 4 ++-- tests/init/neg/promotion-segment3.check | 2 +- tests/init/neg/t3273.check | 4 ++-- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 727e54833063..60f0266d7ede 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -64,12 +64,12 @@ object Semantic: sealed abstract class Value: def show(using Context): String = this match case ThisRef(klass) => - "the original object of type \"" + klass.show + "\" that started this trace" + "the original object of type \"" + klass.show + "\" where initialization checking started" case Warm(klass, outer, ctor, args) => val argsText = if args.nonEmpty then ", args = " + args.map(_.show).mkString("(", ", ", ")") else "" "an initialized (Warm) object of type " + klass.show + "{ outer = " + outer.show + argsText + " }" case Fun(expr, thisV, klass) => - "a function { this = " + thisV.show + ", owner = " + klass.show + " }" + "a function where \"this\" is " + thisV.show + " and the function owner is an object of type " + klass.show case RefSet(values) => values.map(_.show).mkString("Set { ", ", ", " }") case Hot => diff --git a/tests/init/neg/closureLeak.check b/tests/init/neg/closureLeak.check index c4e85d7db4b0..19292f535fd1 100644 --- a/tests/init/neg/closureLeak.check +++ b/tests/init/neg/closureLeak.check @@ -1,14 +1,14 @@ -- Error: tests/init/neg/closureLeak.scala:11:14 ----------------------------------------------------------------------- 11 | l.foreach(a => a.addX(this)) // error | ^^^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function { this = the original object of type "class Outer" that started this trace, owner = class Outer }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is the original object of type "class Outer" where initialization checking started and the function owner is an object of type class Outer. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> class Outer { [ closureLeak.scala:1 ] | ^ |-> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] | ^^^^^^^^^^^^^^^^^ | |Promoting the value to transitively initialized (Hot) failed due to the following problem: - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class Outer" that started this trace. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class Outer" where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. |Non initialized field(s): value p. Promotion trace: |-> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] | ^^^^ diff --git a/tests/init/neg/default-this.check b/tests/init/neg/default-this.check index 873a83427aed..384efdf5414e 100644 --- a/tests/init/neg/default-this.check +++ b/tests/init/neg/default-this.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/default-this.scala:9:8 ------------------------------------------------------------------------ 9 | compare() // error | ^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class B" that started this trace. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class B" where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. |Non initialized field(s): value result. Calling trace: |-> class B extends A { [ default-this.scala:6 ] | ^ diff --git a/tests/init/neg/i15459.check b/tests/init/neg/i15459.check index a4ba1bacf3a2..e25d7efadda2 100644 --- a/tests/init/neg/i15459.check +++ b/tests/init/neg/i15459.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/i15459.scala:3:10 ----------------------------------------------------------------------------- 3 | println(this) // error | ^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class Sub" that started this trace. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class Sub" where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. |Non initialized field(s): value b. Calling trace: |-> class Sub extends Sup: [ i15459.scala:5 ] | ^ diff --git a/tests/init/neg/inlined-method.check b/tests/init/neg/inlined-method.check index d202c668b9b4..dce46d3d7b5a 100644 --- a/tests/init/neg/inlined-method.check +++ b/tests/init/neg/inlined-method.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/inlined-method.scala:8:45 --------------------------------------------------------------------- 8 | scala.runtime.Scala3RunTime.assertFailed(message) // error | ^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class InlineError" that started this trace. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class InlineError" where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. |Non initialized field(s): value v. Calling trace: |-> class InlineError { [ inlined-method.scala:1 ] | ^ diff --git a/tests/init/neg/inner-first.check b/tests/init/neg/inner-first.check index 5719cf56d9aa..62bc8c3c4668 100644 --- a/tests/init/neg/inner-first.check +++ b/tests/init/neg/inner-first.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/inner-first.scala:3:12 ------------------------------------------------------------------------ 3 | println(this) // error | ^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class B" that started this trace. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class B" where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. |Non initialized field(s): value n. Calling trace: |-> class B: [ inner-first.scala:2 ] | ^ diff --git a/tests/init/neg/promotion-loop.check b/tests/init/neg/promotion-loop.check index 9f67434be26a..73adb08e282c 100644 --- a/tests/init/neg/promotion-loop.check +++ b/tests/init/neg/promotion-loop.check @@ -1,14 +1,14 @@ -- Error: tests/init/neg/promotion-loop.scala:16:10 -------------------------------------------------------------------- 16 | println(b) // error | ^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type class B{ outer = the original object of type "class Test" that started this trace }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type class B{ outer = the original object of type "class Test" where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> class Test { test => [ promotion-loop.scala:1 ] | ^ |-> println(b) // error [ promotion-loop.scala:16 ] | ^ | |Promoting the value to transitively initialized (Hot) failed due to the following problem: - |Could not verify that the field value outer is transitively initialized (Hot). It was found to be the original object of type "class Test" that started this trace. + |Could not verify that the field value outer is transitively initialized (Hot). It was found to be the original object of type "class Test" where initialization checking started. |Non initialized field(s): value n. Promotion trace: |-> val outer = test [ promotion-loop.scala:12 ] | ^^^^^^^^^^^^^^^^ diff --git a/tests/init/neg/promotion-segment3.check b/tests/init/neg/promotion-segment3.check index b334439b4168..40747b94f41d 100644 --- a/tests/init/neg/promotion-segment3.check +++ b/tests/init/neg/promotion-segment3.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/promotion-segment3.scala:9:6 ------------------------------------------------------------------ 9 | bar(new B) // error | ^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type class B{ outer = the original object of type "class A" that started this trace }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type class B{ outer = the original object of type "class A" where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> class A: [ promotion-segment3.scala:2 ] | ^ |-> bar(new B) // error [ promotion-segment3.scala:9 ] diff --git a/tests/init/neg/t3273.check b/tests/init/neg/t3273.check index b5d4c30822a7..4b8d3e2521e0 100644 --- a/tests/init/neg/t3273.check +++ b/tests/init/neg/t3273.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/t3273.scala:4:42 ------------------------------------------------------------------------------ 4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error | ^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function { this = the original object of type "object Test" that started this trace, owner = object Test }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is the original object of type "object Test" where initialization checking started and the function owner is an object of type object Test. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> object Test { [ t3273.scala:3 ] | ^ |-> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ] @@ -14,7 +14,7 @@ -- Error: tests/init/neg/t3273.scala:5:61 ------------------------------------------------------------------------------ 5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function { this = the original object of type "object Test" that started this trace, owner = object Test }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is the original object of type "object Test" where initialization checking started and the function owner is an object of type object Test. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> object Test { [ t3273.scala:3 ] | ^ |-> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ] From 6f229802cf1eb128c0128c5bf5e59c5422739634 Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 6 Mar 2023 23:08:19 -0500 Subject: [PATCH 233/657] Add parentheses to make warnings more clear --- compiler/src/dotty/tools/dotc/transform/init/Semantic.scala | 6 +++--- tests/init/neg/closureLeak.check | 4 ++-- tests/init/neg/default-this.check | 2 +- tests/init/neg/i15459.check | 2 +- tests/init/neg/inherit-non-hot.check | 2 +- tests/init/neg/inlined-method.check | 2 +- tests/init/neg/inner-first.check | 2 +- tests/init/neg/promotion-loop.check | 4 ++-- tests/init/neg/promotion-segment3.check | 2 +- tests/init/neg/t3273.check | 4 ++-- 10 files changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 60f0266d7ede..7981153f46a7 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -64,12 +64,12 @@ object Semantic: sealed abstract class Value: def show(using Context): String = this match case ThisRef(klass) => - "the original object of type \"" + klass.show + "\" where initialization checking started" + "the original object of type (" + klass.show + ") where initialization checking started" case Warm(klass, outer, ctor, args) => val argsText = if args.nonEmpty then ", args = " + args.map(_.show).mkString("(", ", ", ")") else "" - "an initialized (Warm) object of type " + klass.show + "{ outer = " + outer.show + argsText + " }" + "an initialized (Warm) object of type (" + klass.show + ") { outer = " + outer.show + argsText + " }" case Fun(expr, thisV, klass) => - "a function where \"this\" is " + thisV.show + " and the function owner is an object of type " + klass.show + "a function where \"this\" is (" + thisV.show + ") and the function owner is an object of type (" + klass.show + ")" case RefSet(values) => values.map(_.show).mkString("Set { ", ", ", " }") case Hot => diff --git a/tests/init/neg/closureLeak.check b/tests/init/neg/closureLeak.check index 19292f535fd1..d4ea2ec8ef47 100644 --- a/tests/init/neg/closureLeak.check +++ b/tests/init/neg/closureLeak.check @@ -1,14 +1,14 @@ -- Error: tests/init/neg/closureLeak.scala:11:14 ----------------------------------------------------------------------- 11 | l.foreach(a => a.addX(this)) // error | ^^^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is the original object of type "class Outer" where initialization checking started and the function owner is an object of type class Outer. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (class Outer) where initialization checking started) and the function owner is an object of type (class Outer). Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> class Outer { [ closureLeak.scala:1 ] | ^ |-> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] | ^^^^^^^^^^^^^^^^^ | |Promoting the value to transitively initialized (Hot) failed due to the following problem: - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class Outer" where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class Outer) where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. |Non initialized field(s): value p. Promotion trace: |-> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] | ^^^^ diff --git a/tests/init/neg/default-this.check b/tests/init/neg/default-this.check index 384efdf5414e..84556f0a9d82 100644 --- a/tests/init/neg/default-this.check +++ b/tests/init/neg/default-this.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/default-this.scala:9:8 ------------------------------------------------------------------------ 9 | compare() // error | ^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class B" where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class B) where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. |Non initialized field(s): value result. Calling trace: |-> class B extends A { [ default-this.scala:6 ] | ^ diff --git a/tests/init/neg/i15459.check b/tests/init/neg/i15459.check index e25d7efadda2..fffeabd560e1 100644 --- a/tests/init/neg/i15459.check +++ b/tests/init/neg/i15459.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/i15459.scala:3:10 ----------------------------------------------------------------------------- 3 | println(this) // error | ^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class Sub" where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class Sub) where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. |Non initialized field(s): value b. Calling trace: |-> class Sub extends Sup: [ i15459.scala:5 ] | ^ diff --git a/tests/init/neg/inherit-non-hot.check b/tests/init/neg/inherit-non-hot.check index a62641473ab2..2c195bcacf1d 100644 --- a/tests/init/neg/inherit-non-hot.check +++ b/tests/init/neg/inherit-non-hot.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/inherit-non-hot.scala:6:32 -------------------------------------------------------------------- 6 | if b == null then b = new B(this) // error | ^^^^^^^^^^^^^^^ - |The RHS of reassignment must be transitively initialized (Hot). It was found to be an initialized (Warm) object of type class B{ outer = a transitively initialized (Hot) object, args = (an uninitialized (Cold) object) }. Calling trace: + |The RHS of reassignment must be transitively initialized (Hot). It was found to be an initialized (Warm) object of type (class B) { outer = a transitively initialized (Hot) object, args = (an uninitialized (Cold) object) }. Calling trace: |-> class C extends A { [ inherit-non-hot.scala:15 ] | ^ |-> val bAgain = toB.getBAgain [ inherit-non-hot.scala:16 ] diff --git a/tests/init/neg/inlined-method.check b/tests/init/neg/inlined-method.check index dce46d3d7b5a..9751ef09b0c1 100644 --- a/tests/init/neg/inlined-method.check +++ b/tests/init/neg/inlined-method.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/inlined-method.scala:8:45 --------------------------------------------------------------------- 8 | scala.runtime.Scala3RunTime.assertFailed(message) // error | ^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class InlineError" where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class InlineError) where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. |Non initialized field(s): value v. Calling trace: |-> class InlineError { [ inlined-method.scala:1 ] | ^ diff --git a/tests/init/neg/inner-first.check b/tests/init/neg/inner-first.check index 62bc8c3c4668..9340479f79a9 100644 --- a/tests/init/neg/inner-first.check +++ b/tests/init/neg/inner-first.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/inner-first.scala:3:12 ------------------------------------------------------------------------ 3 | println(this) // error | ^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type "class B" where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class B) where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. |Non initialized field(s): value n. Calling trace: |-> class B: [ inner-first.scala:2 ] | ^ diff --git a/tests/init/neg/promotion-loop.check b/tests/init/neg/promotion-loop.check index 73adb08e282c..28562aa0effb 100644 --- a/tests/init/neg/promotion-loop.check +++ b/tests/init/neg/promotion-loop.check @@ -1,14 +1,14 @@ -- Error: tests/init/neg/promotion-loop.scala:16:10 -------------------------------------------------------------------- 16 | println(b) // error | ^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type class B{ outer = the original object of type "class Test" where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type (class B) { outer = the original object of type (class Test) where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> class Test { test => [ promotion-loop.scala:1 ] | ^ |-> println(b) // error [ promotion-loop.scala:16 ] | ^ | |Promoting the value to transitively initialized (Hot) failed due to the following problem: - |Could not verify that the field value outer is transitively initialized (Hot). It was found to be the original object of type "class Test" where initialization checking started. + |Could not verify that the field value outer is transitively initialized (Hot). It was found to be the original object of type (class Test) where initialization checking started. |Non initialized field(s): value n. Promotion trace: |-> val outer = test [ promotion-loop.scala:12 ] | ^^^^^^^^^^^^^^^^ diff --git a/tests/init/neg/promotion-segment3.check b/tests/init/neg/promotion-segment3.check index 40747b94f41d..1765f08d872a 100644 --- a/tests/init/neg/promotion-segment3.check +++ b/tests/init/neg/promotion-segment3.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/promotion-segment3.scala:9:6 ------------------------------------------------------------------ 9 | bar(new B) // error | ^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type class B{ outer = the original object of type "class A" where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type (class B) { outer = the original object of type (class A) where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> class A: [ promotion-segment3.scala:2 ] | ^ |-> bar(new B) // error [ promotion-segment3.scala:9 ] diff --git a/tests/init/neg/t3273.check b/tests/init/neg/t3273.check index 4b8d3e2521e0..a63f7bc7a5ea 100644 --- a/tests/init/neg/t3273.check +++ b/tests/init/neg/t3273.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/t3273.scala:4:42 ------------------------------------------------------------------------------ 4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error | ^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is the original object of type "object Test" where initialization checking started and the function owner is an object of type object Test. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (object Test) where initialization checking started) and the function owner is an object of type (object Test). Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> object Test { [ t3273.scala:3 ] | ^ |-> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ] @@ -14,7 +14,7 @@ -- Error: tests/init/neg/t3273.scala:5:61 ------------------------------------------------------------------------------ 5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is the original object of type "object Test" where initialization checking started and the function owner is an object of type object Test. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (object Test) where initialization checking started) and the function owner is an object of type (object Test). Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> object Test { [ t3273.scala:3 ] | ^ |-> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ] From 1b5a2fff06d66b463105643c15e2a0dfa7dd3359 Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 6 Mar 2023 23:14:41 -0500 Subject: [PATCH 234/657] Remove benchmarking output --- .../src/dotty/tools/dotc/transform/init/Semantic.scala | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 49d9ae4d2b2a..6a0c8cd4216d 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -1101,15 +1101,12 @@ object Semantic: * * The class to be checked must be an instantiable concrete class. */ - private def checkClass(classSym: ClassSymbol)(using Cache.Data, Context): Int = + private def checkClass(classSym: ClassSymbol)(using Cache.Data, Context): Unit = val thisRef = ThisRef(classSym) val tpl = classSym.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template] - var accum = 0 - @tailrec def iterate(): Unit = { - accum += 1 given Promoted = Promoted.empty(classSym) given Trace = Trace.empty.add(classSym.defTree) given reporter: Reporter.BufferedReporter = new Reporter.BufferedReporter @@ -1132,19 +1129,15 @@ object Semantic: } iterate() - - accum - 1 end checkClass /** * Check the specified concrete classes */ def checkClasses(classes: List[ClassSymbol])(using Context): Unit = - var total = 0 given Cache.Data() for classSym <- classes if isConcreteClass(classSym) do total += checkClass(classSym) - System.err.nn.println(total) // ----- Semantic definition -------------------------------- type ArgInfo = TraceValue[Value] From 170b36d04530fd9596494250041dd47513c62941 Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 6 Mar 2023 23:16:36 -0500 Subject: [PATCH 235/657] Add documentation for new field. --- compiler/src/dotty/tools/dotc/transform/init/Cache.scala | 4 ++++ compiler/src/dotty/tools/dotc/transform/init/Semantic.scala | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala index 080c56d6fb5f..2dcd6360554a 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala @@ -75,6 +75,10 @@ class Cache[Config, Res]: */ protected var changed: Boolean = false + /** Whether any value in the cache was accessed after being added. + * If no cached values are used after they are added for the first time + * then another iteration of analysis is not needed. + */ protected var cacheUsed: Boolean = false /** Used to avoid allocation, its state does not matter */ diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 6a0c8cd4216d..1aa38e1429df 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -1137,7 +1137,7 @@ object Semantic: def checkClasses(classes: List[ClassSymbol])(using Context): Unit = given Cache.Data() for classSym <- classes if isConcreteClass(classSym) do - total += checkClass(classSym) + checkClass(classSym) // ----- Semantic definition -------------------------------- type ArgInfo = TraceValue[Value] From fe1620bb2362e8ca359899765de4904a5bcdc49c Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 7 Mar 2023 08:40:36 +0000 Subject: [PATCH 236/657] Dupe fix when finding default arg getters Duplicate of i16814. --- tests/pos/i17008.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/pos/i17008.scala diff --git a/tests/pos/i17008.scala b/tests/pos/i17008.scala new file mode 100644 index 000000000000..2bf0f155afbc --- /dev/null +++ b/tests/pos/i17008.scala @@ -0,0 +1,9 @@ +abstract class A { + protected def foo(text: String, bar: () => Unit = () => ()): Unit = println(s"$text, $bar") +} + +class B extends A { + def f1(): Unit = { + super.foo("X") + } +} From ff9961702c37bea80514660cc3023ad81f8a8fc6 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 7 Mar 2023 14:41:40 +0100 Subject: [PATCH 237/657] Update library/src/scala/quoted/Quotes.scala --- library/src/scala/quoted/Quotes.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 05c253a76fd3..ddd7eb7d66fe 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -34,7 +34,7 @@ transparent inline def quotes(using q: Quotes): q.type = q * ``` */ -@implicitNotFound("""explain=Maybe this methods is missing a `(using Quotes)` parameter. +@implicitNotFound("""explain=Maybe this method is missing a `(using Quotes)` parameter. Maybe that splice `$ { ... }` is missing? Given instances of `Quotes` are generated from an enclosing splice `$ { ... }` (or `scala.staging.run` call). From 3cb01d6cbd549c11f71556ee7d8a592e354afd46 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 7 Mar 2023 13:52:50 +0000 Subject: [PATCH 238/657] Drop incorrect super accessor in trait subclass When a trait extends a class and accesses a protected member (while in a different package), we would emitted a super accessor and call that instead. That breaks the semantics because if the protected member is overridden, that implementation won't be called, as the call is to the super method instead. In addition to that, the call to the super accessor was causing a false positive error as we don't allow super calls to bind to vals. --- .../tools/dotc/transform/AccessProxies.scala | 2 +- .../dotc/transform/ProtectedAccessors.scala | 12 +++------ .../tools/dotc/transform/SuperAccessors.scala | 26 +++++++------------ tests/{neg => pos}/i11170a.scala | 4 +-- tests/run/i17021.defs.scala | 16 ++++++++++++ tests/run/i17021.scala | 18 +++++++++++++ 6 files changed, 50 insertions(+), 28 deletions(-) rename tests/{neg => pos}/i11170a.scala (82%) create mode 100644 tests/run/i17021.defs.scala create mode 100644 tests/run/i17021.scala diff --git a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala index 14362260d032..3175ffceae49 100644 --- a/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala +++ b/compiler/src/dotty/tools/dotc/transform/AccessProxies.scala @@ -71,7 +71,7 @@ abstract class AccessProxies { def needsAccessor(sym: Symbol)(using Context): Boolean def ifNoHost(reference: RefTree)(using Context): Tree = { - assert(false, "no host found for $reference with ${reference.symbol.showLocated} from ${ctx.owner}") + assert(false, i"no host found for $reference with ${reference.symbol.showLocated} from ${ctx.owner}") reference } diff --git a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala index 6d8f7bdb32cb..c675938b86f7 100644 --- a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala @@ -33,17 +33,11 @@ object ProtectedAccessors { ctx.owner.isContainedIn(boundary) || ctx.owner.isContainedIn(boundary.linkedClass) } - /** Do we need a protected accessor if the current context's owner - * is not in a subclass or subtrait of `sym`? - */ - def needsAccessorIfNotInSubclass(sym: Symbol)(using Context): Boolean = - sym.isTerm && sym.is(Protected) && - !sym.owner.is(Trait) && // trait methods need to be handled specially, are currently always public - !insideBoundaryOf(sym) - /** Do we need a protected accessor for accessing sym from the current context's owner? */ def needsAccessor(sym: Symbol)(using Context): Boolean = - needsAccessorIfNotInSubclass(sym) && + sym.isTerm && sym.is(Protected) && + !sym.owner.is(Trait) && // trait methods need to be handled specially, are currently always public + !insideBoundaryOf(sym) && !ctx.owner.enclosingClass.derivesFrom(sym.owner) } diff --git a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala index 2307f759b571..5d7210a59a96 100644 --- a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -174,26 +174,20 @@ class SuperAccessors(thisPhase: DenotTransformer) { val sel @ Select(qual, name) = tree: @unchecked val sym = sel.symbol - /** If an accesses to protected member of a class comes from a trait, - * or would need a protected accessor placed in a trait, we cannot - * perform the access to the protected member directly since jvm access - * restrictions require the call site to be in an actual subclass and - * traits don't count as subclasses in this respect. In this case - * we generate a super accessor instead. See SI-2296. - */ def needsSuperAccessor = - ProtectedAccessors.needsAccessorIfNotInSubclass(sym) && + ProtectedAccessors.needsAccessor(sym) && AccessProxies.hostForAccessorOf(sym).is(Trait) qual match { case _: This if needsSuperAccessor => - /* - * A trait which extends a class and accesses a protected member - * of that class cannot implement the necessary accessor method - * because jvm access restrictions require the call site to be in - * an actual subclass and traits don't count as subclasses in this - * respect. We generate a super accessor itself, which will be fixed - * by the implementing class. See SI-2296. - */ + /* Given a protected member m defined in class C, + * and a trait T that calls m. + * + * If T extends C, then we can access it by casting + * the qualifier of the select to C. + * + * Otherwise, we need to go through an accessor, + * which the implementing class will provide an implementation for. + */ superAccessorCall(sel) case Super(_, mix) => transformSuperSelect(sel) diff --git a/tests/neg/i11170a.scala b/tests/pos/i11170a.scala similarity index 82% rename from tests/neg/i11170a.scala rename to tests/pos/i11170a.scala index 5268c506f33f..bbf627ce8864 100644 --- a/tests/neg/i11170a.scala +++ b/tests/pos/i11170a.scala @@ -23,6 +23,6 @@ package cpackage { import apackage._ import bpackage._ - case class C(override protected val x: Int) extends A with B // error + case class C(override protected val x: Int) extends A with B case class C2(override val x: Int) extends A2 with B2 -} \ No newline at end of file +} diff --git a/tests/run/i17021.defs.scala b/tests/run/i17021.defs.scala new file mode 100644 index 000000000000..126759b5d268 --- /dev/null +++ b/tests/run/i17021.defs.scala @@ -0,0 +1,16 @@ +// Derives from run/i17021, but with defs instead of vals +package p1: + class A: + protected def foo: Int = 1 + +package p2: + trait B extends p1.A: + def bar: Int = foo + + class C extends B: + override def foo: Int = 2 + +object Test: + def main(args: Array[String]): Unit = + val n = new p2.C().bar + assert(n == 2, n) // was: assertion failed: 1 diff --git a/tests/run/i17021.scala b/tests/run/i17021.scala new file mode 100644 index 000000000000..7465508e7f0a --- /dev/null +++ b/tests/run/i17021.scala @@ -0,0 +1,18 @@ +package p1: + class A: + protected val foo: Int = 1 + +package p2: + trait B extends p1.A: + def bar: Int = foo + + class C extends B: // was: error: parent trait B has a super call which binds to the value p1.A.foo. Super calls can only target methods. + override val foo: Int = 2 + +// Also, assert that the access continues to delegate: +// i.e. B#bar delegates to this.foo and so C#bar returns 2, +// not B#bar delegates to super.foo and so C#bar returns 1. +object Test: + def main(args: Array[String]): Unit = + val n = new p2.C().bar + assert(n == 2, n) // was: assertion failed: 1 From 4b83f1ff392340b5829ef5ea67453ed159600241 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 7 Mar 2023 17:21:21 +0100 Subject: [PATCH 239/657] Update tests/neg-custom-args/explain/i16888.check --- tests/neg-custom-args/explain/i16888.check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neg-custom-args/explain/i16888.check b/tests/neg-custom-args/explain/i16888.check index e184ea418a94..53103576d158 100644 --- a/tests/neg-custom-args/explain/i16888.check +++ b/tests/neg-custom-args/explain/i16888.check @@ -5,7 +5,7 @@ |--------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Maybe this methods is missing a `(using Quotes)` parameter. + | Maybe this method is missing a `(using Quotes)` parameter. | | Maybe that splice `$ { ... }` is missing? | Given instances of `Quotes` are generated from an enclosing splice `$ { ... }` (or `scala.staging.run` call). From 9b15b2ed0205ed6e273c3cfbad9ed9a24e8bc2f1 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Tue, 7 Mar 2023 17:24:15 +0100 Subject: [PATCH 240/657] Remove extra spaces in error messages --- .../tools/dotc/core/MatchTypeTrace.scala | 10 ++-- tests/neg/6570-1.check | 4 +- tests/neg/6571.check | 4 +- tests/neg/i11982a.check | 6 +- tests/neg/i12049.check | 14 ++--- tests/neg/i13780.check | 4 +- tests/neg/i15618.check | 2 +- tests/neg/matchtype-seq.check | 58 +++++++++---------- 8 files changed, 51 insertions(+), 51 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala index 062ddd5e846c..60ebc95e7bed 100644 --- a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala +++ b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala @@ -100,16 +100,16 @@ object MatchTypeTrace: case TryReduce(scrut: Type) => i" trying to reduce $scrut" case NoMatches(scrut, cases) => - i""" failed since selector $scrut + i""" failed since selector $scrut | matches none of the cases | | ${casesText(cases)}""" case EmptyScrutinee(scrut) => - i""" failed since selector $scrut + i""" failed since selector $scrut | is uninhabited (there are no values of that type).""" case Stuck(scrut, stuckCase, otherCases) => val msg = - i""" failed since selector $scrut + i""" failed since selector $scrut | does not match ${caseText(stuckCase)} | and cannot be shown to be disjoint from it either.""" if otherCases.length == 0 then msg @@ -121,14 +121,14 @@ object MatchTypeTrace: | ${casesText(otherCases)}""" case NoInstance(scrut, stuckCase, fails) => def params = if fails.length == 1 then "parameter" else "parameters" - i""" failed since selector $scrut + i""" failed since selector $scrut | does not uniquely determine $params ${fails.map(_._1)}%, % in | ${caseText(stuckCase)} | The computed bounds for the $params are: | ${fails.map((name, bounds) => i"$name$bounds")}%\n %""" def noMatchesText(scrut: Type, cases: List[Type])(using Context): String = - i"""failed since selector $scrut + i"""failed since selector $scrut |matches none of the cases | | ${casesText(cases)}""" diff --git a/tests/neg/6570-1.check b/tests/neg/6570-1.check index fa53e71cbb6b..bdbadd0f752a 100644 --- a/tests/neg/6570-1.check +++ b/tests/neg/6570-1.check @@ -7,7 +7,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce N[Box[Int & String]] - | failed since selector Box[Int & String] + | failed since selector Box[Int & String] | is uninhabited (there are no values of that type). | | longer explanation available when compiling with `-explain` @@ -23,7 +23,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce M[T] - | failed since selector T + | failed since selector T | does not uniquely determine parameter x in | case Cov[x] => N[x] | The computed bounds for the parameter are: diff --git a/tests/neg/6571.check b/tests/neg/6571.check index 42997407765f..4172abb2919b 100644 --- a/tests/neg/6571.check +++ b/tests/neg/6571.check @@ -7,7 +7,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.M[Test.Inv[Int] & Test.Inv[String]] - | failed since selector Test.Inv[Int] & Test.Inv[String] + | failed since selector Test.Inv[Int] & Test.Inv[String] | is uninhabited (there are no values of that type). | | longer explanation available when compiling with `-explain` @@ -20,7 +20,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.M[Test.Inv[String] & Test.Inv[Int]] - | failed since selector Test.Inv[String] & Test.Inv[Int] + | failed since selector Test.Inv[String] & Test.Inv[Int] | is uninhabited (there are no values of that type). | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i11982a.check b/tests/neg/i11982a.check index bc07c82059cc..1977aa30e8b5 100644 --- a/tests/neg/i11982a.check +++ b/tests/neg/i11982a.check @@ -6,7 +6,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Tuple.Tail[X] - | failed since selector X + | failed since selector X | does not uniquely determine parameter xs in | case _ *: xs => xs | The computed bounds for the parameter are: @@ -21,7 +21,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Tuple.Tail[X] - | failed since selector X + | failed since selector X | does not uniquely determine parameter xs in | case _ *: xs => xs | The computed bounds for the parameter are: @@ -36,7 +36,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Tuple.Tail[X] - | failed since selector X + | failed since selector X | does not uniquely determine parameter xs in | case _ *: xs => xs | The computed bounds for the parameter are: diff --git a/tests/neg/i12049.check b/tests/neg/i12049.check index a58624ec6778..11c648e35a57 100644 --- a/tests/neg/i12049.check +++ b/tests/neg/i12049.check @@ -7,7 +7,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce M[B] - | failed since selector B + | failed since selector B | does not match case A => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -18,7 +18,7 @@ -- Error: tests/neg/i12049.scala:14:23 --------------------------------------------------------------------------------- 14 |val y3: String = ??? : Last[Int *: Int *: Boolean *: String *: EmptyTuple] // error | ^ - | Match type reduction failed since selector EmptyTuple.type + | Match type reduction failed since selector EmptyTuple.type | matches none of the cases | | case _ *: _ *: t => Last[t] @@ -26,7 +26,7 @@ -- Error: tests/neg/i12049.scala:22:26 --------------------------------------------------------------------------------- 22 |val z3: (A, B, A) = ??? : Reverse[(A, B, A)] // error | ^ - | Match type reduction failed since selector A *: EmptyTuple.type + | Match type reduction failed since selector A *: EmptyTuple.type | matches none of the cases | | case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)] @@ -39,7 +39,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce M[B] - | failed since selector B + | failed since selector B | does not match case A => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -48,7 +48,7 @@ -- Error: tests/neg/i12049.scala:25:26 --------------------------------------------------------------------------------- 25 |val _ = summon[String =:= Last[Int *: Int *: Boolean *: String *: EmptyTuple]] // error | ^ - | Match type reduction failed since selector EmptyTuple.type + | Match type reduction failed since selector EmptyTuple.type | matches none of the cases | | case _ *: _ *: t => Last[t] @@ -56,7 +56,7 @@ -- Error: tests/neg/i12049.scala:26:29 --------------------------------------------------------------------------------- 26 |val _ = summon[(A, B, A) =:= Reverse[(A, B, A)]] // error | ^ - | Match type reduction failed since selector A *: EmptyTuple.type + | Match type reduction failed since selector A *: EmptyTuple.type | matches none of the cases | | case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)] @@ -69,7 +69,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce M[B] - | failed since selector B + | failed since selector B | does not match case A => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case diff --git a/tests/neg/i13780.check b/tests/neg/i13780.check index 56b6a67ac8e7..aa0a47db5737 100644 --- a/tests/neg/i13780.check +++ b/tests/neg/i13780.check @@ -10,7 +10,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Head[X] - | failed since selector X + | failed since selector X | does not uniquely determine parameters a, b in | case (a, b) => a | The computed bounds for the parameters are: @@ -30,7 +30,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Head[X] - | failed since selector X + | failed since selector X | does not uniquely determine parameters a, b in | case (a, b) => a | The computed bounds for the parameters are: diff --git a/tests/neg/i15618.check b/tests/neg/i15618.check index 16fadcd9b886..099e3fe0a0b7 100644 --- a/tests/neg/i15618.check +++ b/tests/neg/i15618.check @@ -9,7 +9,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce ScalaType[T] - | failed since selector T + | failed since selector T | does not match case Float16 => Float | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining cases diff --git a/tests/neg/matchtype-seq.check b/tests/neg/matchtype-seq.check index aba1e312da01..9c37fc08c4df 100644 --- a/tests/neg/matchtype-seq.check +++ b/tests/neg/matchtype-seq.check @@ -1,7 +1,7 @@ -- Error: tests/neg/matchtype-seq.scala:9:11 --------------------------------------------------------------------------- 9 | identity[T1[3]]("") // error | ^ - | Match type reduction failed since selector (3 : Int) + | Match type reduction failed since selector (3 : Int) | matches none of the cases | | case (1 : Int) => Int @@ -9,7 +9,7 @@ -- Error: tests/neg/matchtype-seq.scala:10:11 -------------------------------------------------------------------------- 10 | identity[T1[3]](1) // error | ^ - | Match type reduction failed since selector (3 : Int) + | Match type reduction failed since selector (3 : Int) | matches none of the cases | | case (1 : Int) => Int @@ -23,7 +23,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T1[Int] - | failed since selector Int + | failed since selector Int | does not match case (1 : Int) => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -40,7 +40,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T1[Int] - | failed since selector Int + | failed since selector Int | does not match case (1 : Int) => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -57,7 +57,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T2[Int] - | failed since selector Int + | failed since selector Int | does not match case (1 : Int) => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -81,7 +81,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T2[Int] - | failed since selector Int + | failed since selector Int | does not match case (1 : Int) => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -98,7 +98,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T3[Test.A] - | failed since selector Test.A + | failed since selector Test.A | does not match case Test.B => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -115,7 +115,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T3[Test.A] - | failed since selector Test.A + | failed since selector Test.A | does not match case Test.B => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -132,7 +132,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T5[Test.A] - | failed since selector Test.A + | failed since selector Test.A | does not match case Test.C => String | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -149,7 +149,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T5[Test.A] - | failed since selector Test.A + | failed since selector Test.A | does not match case Test.C => String | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -166,7 +166,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T7[Test.D] - | failed since selector Test.D + | failed since selector Test.D | does not match case Test.A2 => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -183,7 +183,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T7[Test.D] - | failed since selector Test.D + | failed since selector Test.D | does not match case Test.A2 => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -200,7 +200,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T8[Test.E2] - | failed since selector Test.E2 + | failed since selector Test.E2 | does not match case Test.E1 => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -224,7 +224,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T8[Test.E2] - | failed since selector Test.E2 + | failed since selector Test.E2 | does not match case Test.E1 => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -241,7 +241,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T9[(Nothing, String)] - | failed since selector (Nothing, String) + | failed since selector (Nothing, String) | is uninhabited (there are no values of that type). | | longer explanation available when compiling with `-explain` @@ -254,7 +254,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T9[(String, Nothing)] - | failed since selector (String, Nothing) + | failed since selector (String, Nothing) | is uninhabited (there are no values of that type). | | longer explanation available when compiling with `-explain` @@ -267,7 +267,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T9[(Int, Nothing)] - | failed since selector (Int, Nothing) + | failed since selector (Int, Nothing) | is uninhabited (there are no values of that type). | | longer explanation available when compiling with `-explain` @@ -280,7 +280,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T9[(Nothing, Int)] - | failed since selector (Nothing, Int) + | failed since selector (Nothing, Int) | is uninhabited (there are no values of that type). | | longer explanation available when compiling with `-explain` @@ -293,7 +293,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T9[(?, ?)] - | failed since selector (?, ?) + | failed since selector (?, ?) | does not match case (Int, String) => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -310,7 +310,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T9[(?, ?)] - | failed since selector (?, ?) + | failed since selector (?, ?) | does not match case (Int, String) => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -327,7 +327,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T9[(Any, Any)] - | failed since selector (Any, Any) + | failed since selector (Any, Any) | does not match case (Int, String) => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -344,7 +344,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.T9[(Any, Any)] - | failed since selector (Any, Any) + | failed since selector (Any, Any) | does not match case (Int, String) => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -361,7 +361,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.TA[Test.Box2[Int, Int, String]] - | failed since selector Test.Box2[Int, Int, String] + | failed since selector Test.Box2[Int, Int, String] | does not match case Test.Box2[Int, Int, Int] => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -378,7 +378,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.TA[Test.Box2[Int, Int, String]] - | failed since selector Test.Box2[Int, Int, String] + | failed since selector Test.Box2[Int, Int, String] | does not match case Test.Box2[Int, Int, Int] => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -395,7 +395,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test.TD[Test.Box2_C[Int, Int, String]] - | failed since selector Test.Box2_C[Int, Int, String] + | failed since selector Test.Box2_C[Int, Int, String] | does not match case Test.Box2_C[Int, Int, Int] => Int | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -412,7 +412,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test2.M[Some[A]] - | failed since selector Some[A] + | failed since selector Some[A] | does not match case Option[Int] => String | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -429,7 +429,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test2.M[Some[A]] - | failed since selector Some[A] + | failed since selector Some[A] | does not match case Option[Int] => String | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -446,7 +446,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test3.M[Test3.Inv[A]] - | failed since selector Test3.Inv[A] + | failed since selector Test3.Inv[A] | does not match case Test3.Inv[Int] => String | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case @@ -463,7 +463,7 @@ | Note: a match type could not be fully reduced: | | trying to reduce Test4.M[Test4.Inv[Foo.this.A]] - | failed since selector Test4.Inv[Foo.this.A] + | failed since selector Test4.Inv[Foo.this.A] | does not match case Test4.Inv[Int] => String | and cannot be shown to be disjoint from it either. | Therefore, reduction cannot advance to the remaining case From 50ff0475510cb2364bc4b0d42cabf50421a69f6b Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Tue, 7 Mar 2023 17:47:48 +0100 Subject: [PATCH 241/657] Consider TypeBounds not fully instantiated --- .../src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- tests/pos/i16869.scala | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i16869.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index f097bd160fdd..9dda18c72f26 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2725,7 +2725,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling x && { t.dealias match { case tp: TypeRef if !tp.symbol.isClass => false - case _: SkolemType | _: TypeVar | _: TypeParamRef => false + case _: SkolemType | _: TypeVar | _: TypeParamRef | _: TypeBounds => false case _ => foldOver(x, t) } } diff --git a/tests/pos/i16869.scala b/tests/pos/i16869.scala new file mode 100644 index 000000000000..4067e34ac8ef --- /dev/null +++ b/tests/pos/i16869.scala @@ -0,0 +1,13 @@ +class C[T] + +type Foo[T] = T match + case C[true] => true + case C[false] => false + +class W[T] extends C[Foo[T]] + +def f[T <: C[?]](t: T) = W[T]() + +def test = + val b = C[true]() + f(b): C[true] From 7d4e103a941a30306ddde28a11f8bc3a8841acf8 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Thu, 2 Mar 2023 11:47:27 +0100 Subject: [PATCH 242/657] Include top-level symbols from same file in outer ambiguity error When checking if an inherited definition is ambiguous with an outer definition, include top-level outer definitions defined in the same compilation unit. --- .../src/dotty/tools/dotc/typer/Typer.scala | 19 ++++++++++++---- tests/neg/ambiref.check | 16 ++++++++++++++ tests/neg/ambiref.scala | 22 ++++++++++++++++++- tests/pos-special/fatal-warnings/i9260.scala | 2 +- tests/run/protectedacc.scala | 2 +- 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9d8fdcc006c9..02a432d1fd9e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -366,11 +366,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Does reference `tp` refer only to inherited symbols? def isInherited(denot: Denotation) = def isCurrent(mbr: SingleDenotation): Boolean = - !mbr.symbol.exists || mbr.symbol.owner == ctx.owner + !mbr.symbol.exists || mbr.symbol.owner == ctx.owner || ctx.owner.is(Package) denot match case denot: SingleDenotation => !isCurrent(denot) case denot => !denot.hasAltWith(isCurrent) + /* It is an error if an identifier x is available as an inherited member in an inner scope + * and the same name x is defined in an outer scope in the same source file, unless + * the inherited member (has an overloaded alternative that) coincides with + * (an overloaded alternative of) the definition x. + */ def checkNoOuterDefs(denot: Denotation, last: Context, prevCtx: Context): Unit = def sameTermOrType(d1: SingleDenotation, d2: Denotation) = d2.containsSym(d1.symbol) || d2.hasUniqueSym && { @@ -387,9 +392,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val owner = outer.owner if (owner eq last.owner) && (outer.scope eq last.scope) then checkNoOuterDefs(denot, outer, prevCtx) - else if !owner.is(Package) then - val scope = if owner.isClass then owner.info.decls else outer.scope - val competing = scope.denotsNamed(name).filterWithFlags(required, excluded) + else if !owner.isRoot then + val found = + if owner.is(Package) then + owner.denot.asClass.membersNamed(name) + .filterWithPredicate(d => !d.symbol.is(Package) && d.symbol.source == denot.symbol.source) + else + val scope = if owner.isClass then owner.info.decls else outer.scope + scope.denotsNamed(name) + val competing = found.filterWithFlags(required, excluded | Synthetic) if competing.exists then val symsMatch = competing .filterWithPredicate(sd => sameTermOrType(sd, denot)) diff --git a/tests/neg/ambiref.check b/tests/neg/ambiref.check index 5d701b3b3b71..32b4078f1346 100644 --- a/tests/neg/ambiref.check +++ b/tests/neg/ambiref.check @@ -30,3 +30,19 @@ | and inherited subsequently in class E | | longer explanation available when compiling with `-explain` +-- [E049] Reference Error: tests/neg/ambiref.scala:43:10 --------------------------------------------------------------- +43 | println(global) // error + | ^^^^^^ + | Reference to global is ambiguous. + | It is both defined in package + | and inherited subsequently in object D + | + | longer explanation available when compiling with `-explain` +-- [E049] Reference Error: tests/neg/ambiref.scala:49:16 --------------------------------------------------------------- +49 | def t = new T { } // error + | ^ + | Reference to T is ambiguous. + | It is both defined in package p + | and inherited subsequently in class C + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/ambiref.scala b/tests/neg/ambiref.scala index e7a5d5efbd7e..bb48997cd465 100644 --- a/tests/neg/ambiref.scala +++ b/tests/neg/ambiref.scala @@ -40,4 +40,24 @@ val global = 0 class C: val global = 1 object D extends C: - println(global) // OK, since global is defined in package \ No newline at end of file + println(global) // error + +package p: + class T + trait P { trait T } + class C extends P: + def t = new T { } // error + +package scala: + trait P { trait Option[+A] } + class C extends P: + def t = new Option[String] { } // OK, competing scala.Option is not defined in the same compilation unit + +object test5: + class Mu // generates a synthetic companion object with an apply method + trait A { + val Mu = 1 + } + trait B extends A { + def t = Mu // don't warn about synthetic companion + } diff --git a/tests/pos-special/fatal-warnings/i9260.scala b/tests/pos-special/fatal-warnings/i9260.scala index df548f393eea..0392c1c96fa8 100644 --- a/tests/pos-special/fatal-warnings/i9260.scala +++ b/tests/pos-special/fatal-warnings/i9260.scala @@ -10,7 +10,7 @@ end AstImpl object untpd extends AstImpl[Null]: - def DefDef(ast: Ast): DefDef = ast match + def DefDef(ast: this.Ast): DefDef = ast match case ast: DefDef => ast end untpd diff --git a/tests/run/protectedacc.scala b/tests/run/protectedacc.scala index a08e7201fd15..85aa3438faa3 100644 --- a/tests/run/protectedacc.scala +++ b/tests/run/protectedacc.scala @@ -134,7 +134,7 @@ package p { abstract class X[T] extends PolyA[T] { - trait Inner extends B { + trait Inner extends this.B { def self: T; def self2: Node; def getB: Inner; From 36298a2dd4c988771c8e4093113da3edca5137de Mon Sep 17 00:00:00 2001 From: David Hua Date: Wed, 8 Mar 2023 20:43:20 -0500 Subject: [PATCH 243/657] Address feedback. --- compiler/src/dotty/tools/dotc/transform/init/Semantic.scala | 6 +++--- tests/init/neg/inherit-non-hot.check | 2 +- tests/init/neg/promotion-loop.check | 2 +- tests/init/neg/promotion-segment3.check | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 7981153f46a7..1b7e1266ac13 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -67,7 +67,7 @@ object Semantic: "the original object of type (" + klass.show + ") where initialization checking started" case Warm(klass, outer, ctor, args) => val argsText = if args.nonEmpty then ", args = " + args.map(_.show).mkString("(", ", ", ")") else "" - "an initialized (Warm) object of type (" + klass.show + ") { outer = " + outer.show + argsText + " }" + "a non-transitively initialized (Warm) object of type (" + klass.show + ") { outer = " + outer.show + argsText + " }" case Fun(expr, thisV, klass) => "a function where \"this\" is (" + thisV.show + ") and the function owner is an object of type (" + klass.show + ")" case RefSet(values) => @@ -472,7 +472,7 @@ object Semantic: def widenArg: Contextual[Value] = a match case _: Ref | _: Fun => - val hasError = Reporter.hasErrors { a.promote("Argument cannot be proven to be transitively initialized (Hot)") } + val hasError = Reporter.hasErrors { a.promote("Argument is not provably transitively initialized (Hot)") } if hasError then Cold else Hot case RefSet(refs) => @@ -710,7 +710,7 @@ object Semantic: promoteArgs() // try promoting the receiver as last resort val hasErrors = Reporter.hasErrors { - ref.promote(ref.show + " has no source code and could not be proven to be transitively initialized (Hot).") + ref.promote(ref.show + " has no source code and is not provably transitively initialized (Hot).") } if hasErrors then val error = CallUnknown(target)(trace) diff --git a/tests/init/neg/inherit-non-hot.check b/tests/init/neg/inherit-non-hot.check index 2c195bcacf1d..068ba9662fd1 100644 --- a/tests/init/neg/inherit-non-hot.check +++ b/tests/init/neg/inherit-non-hot.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/inherit-non-hot.scala:6:32 -------------------------------------------------------------------- 6 | if b == null then b = new B(this) // error | ^^^^^^^^^^^^^^^ - |The RHS of reassignment must be transitively initialized (Hot). It was found to be an initialized (Warm) object of type (class B) { outer = a transitively initialized (Hot) object, args = (an uninitialized (Cold) object) }. Calling trace: + |The RHS of reassignment must be transitively initialized (Hot). It was found to be a non-transitively initialized (Warm) object of type (class B) { outer = a transitively initialized (Hot) object, args = (an uninitialized (Cold) object) }. Calling trace: |-> class C extends A { [ inherit-non-hot.scala:15 ] | ^ |-> val bAgain = toB.getBAgain [ inherit-non-hot.scala:16 ] diff --git a/tests/init/neg/promotion-loop.check b/tests/init/neg/promotion-loop.check index 28562aa0effb..ec0a6854bbac 100644 --- a/tests/init/neg/promotion-loop.check +++ b/tests/init/neg/promotion-loop.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/promotion-loop.scala:16:10 -------------------------------------------------------------------- 16 | println(b) // error | ^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type (class B) { outer = the original object of type (class Test) where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a non-transitively initialized (Warm) object of type (class B) { outer = the original object of type (class Test) where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> class Test { test => [ promotion-loop.scala:1 ] | ^ |-> println(b) // error [ promotion-loop.scala:16 ] diff --git a/tests/init/neg/promotion-segment3.check b/tests/init/neg/promotion-segment3.check index 1765f08d872a..bc7a076d7d72 100644 --- a/tests/init/neg/promotion-segment3.check +++ b/tests/init/neg/promotion-segment3.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/promotion-segment3.scala:9:6 ------------------------------------------------------------------ 9 | bar(new B) // error | ^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be an initialized (Warm) object of type (class B) { outer = the original object of type (class A) where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a non-transitively initialized (Warm) object of type (class B) { outer = the original object of type (class A) where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: |-> class A: [ promotion-segment3.scala:2 ] | ^ |-> bar(new B) // error [ promotion-segment3.scala:9 ] From eb0d8176658eece2fc77887b4543b6b39b473bc3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 8 Mar 2023 17:56:16 +0100 Subject: [PATCH 244/657] Optimize captured type in splice hole If we have a quote that defined a type `T` and use it in the contents of the splice we want to pass a `Type[T]` to the hole. This improves a case where we had a `List[T]` and the hole could end up with a `Type[T]` and `Type[List[T]]`. We should only pass the `Type[T]`. --- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 8 ++++---- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 5 +++++ tests/pos-macros/i12440.scala | 6 ++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 393fe46b8438..deea32dba6ca 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -246,7 +246,7 @@ class Splicing extends MacroTransform: if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => val newContent = capturedPartTypes(tpt) newContent match - case block: Block => + case block: Block => inContext(ctx.withSource(tree.source)) { Apply(TypeApply(typeof, List(newContent)), List(quotes)).withSpan(tree.span) } @@ -342,7 +342,7 @@ class Splicing extends MacroTransform: val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2 ref(bindingSym) - private def newQuotedTypeClassBinding(tpe: Type)(using Context) = + private def newQuotedTypeClassBinding(tpe: Type)(using Context) = newSymbol( spliceOwner, UniqueName.fresh(nme.Type).toTermName, @@ -361,7 +361,7 @@ class Splicing extends MacroTransform: healedTypes = PCPCheckAndHeal.QuoteTypeTags(tpt.span) val capturePartTypes = new TypeMap { def apply(tp: Type) = tp match { - case typeRef @ TypeRef(prefix, _) if isCaptured(prefix.typeSymbol) || isCaptured(prefix.termSymbol) => + case typeRef: TypeRef if containsCapturedType(typeRef) => val termRef = refBindingMap .getOrElseUpdate(typeRef.symbol, (TypeTree(typeRef), newQuotedTypeClassBinding(typeRef)))._2.termRef val tagRef = healedTypes.nn.getTagRef(termRef) @@ -376,7 +376,7 @@ class Splicing extends MacroTransform: tpt match case block: Block => cpy.Block(block)(newHealedTypes ::: block.stats, TypeTree(captured)) - case _ => + case _ => if newHealedTypes.nonEmpty then cpy.Block(tpt)(newHealedTypes, TypeTree(captured)) else diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 52942ea719f9..5d2d3c7e2df8 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -655,6 +655,11 @@ object TreeChecker { override def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = { val tree1 @ Hole(isTermHole, _, args, content, tpt) = super.typedHole(tree, pt): @unchecked + // Check that we only add the captured type `T` instead of a more complex type like `List[T]`. + // If we have `F[T]` with captured `F` and `T`, we should list `F` and `T` separately in the args. + for arg <- args do + assert(arg.isTerm || arg.tpe.isInstanceOf[TypeRef], "Expected TypeRef in Hole type args but got: " + arg.tpe) + // Check result type of the hole if isTermHole then assert(tpt.typeOpt <:< pt) else assert(tpt.typeOpt =:= pt) diff --git a/tests/pos-macros/i12440.scala b/tests/pos-macros/i12440.scala index 4b4c56fef568..02122be28deb 100644 --- a/tests/pos-macros/i12440.scala +++ b/tests/pos-macros/i12440.scala @@ -5,6 +5,12 @@ trait Mirror: class Eq: + def test0(using Quotes): Unit = '{ + type T + ${ summonType[T]; ??? } + ${ summonType[List[T]]; ??? } + } + def test1(using Quotes): Unit = '{ val m: Mirror = ??? ${ summonType[m.ElemTypes]; ??? } From 6eeea197158f7e22970bbb7cf74de5195b945a11 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sat, 11 Mar 2023 18:02:19 +0100 Subject: [PATCH 245/657] Include `P` in the implicit scope of `P.this.type` Before this commit, `one` compiled but `two` didn't in: class Foo[+T] class Elem: def one(a: Elem, x: Foo[a.type]): Int = x.ext def two(x: Foo[Elem.this.type]): Int = x.ext object Elem: extension (x: Foo[Elem]) def ext: Int = 1 This happened because the anchors of the singleton reference `a.type` includes its underlying type `Elem`, but `Elem.this.type` has no anchors at all. This commit changes the definition of anchors to rectify this, which also brings us closer to how Scala 2 behaves. --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 3 +++ .../_docs/reference/changed-features/implicit-resolution.md | 3 ++- tests/pos/this-implicit-scope.scala | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/pos/this-implicit-scope.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 7d185d335218..c37da9fcf701 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -622,6 +622,8 @@ trait ImplicitRunInfo: traverse(t.prefix) case t: ThisType if t.cls.is(Module) && t.cls.isStaticOwner => traverse(t.cls.sourceModule.termRef) + case t: ThisType => + traverse(t.tref) case t: ConstantType => traverse(t.underlying) case t: TypeParamRef => @@ -743,6 +745,7 @@ trait ImplicitRunInfo: * - If `T` is a singleton reference, the anchors of its underlying type, plus, * if `T` is of the form `(P#x).type`, the anchors of `P`. * - If `T` is the this-type of a static object, the anchors of a term reference to that object. + * - If `T` is some other this-type `P.this.type`, the anchors of `P`. * - If `T` is some other type, the union of the anchors of each constituent type of `T`. * * The _implicit scope_ of a type `tp` is the smallest set S of term references (i.e. TermRefs) diff --git a/docs/_docs/reference/changed-features/implicit-resolution.md b/docs/_docs/reference/changed-features/implicit-resolution.md index bf15baa3299c..6a898690b565 100644 --- a/docs/_docs/reference/changed-features/implicit-resolution.md +++ b/docs/_docs/reference/changed-features/implicit-resolution.md @@ -67,7 +67,8 @@ Opaque type aliases count as anchors only outside the scope where their alias is 1. If _T_ is a reference to a type parameter, the union of the anchors of both of its bounds. 1. If _T_ is a singleton reference, the anchors of its underlying type, plus, if _T_ is of the form _(P#x).type_, the anchors of _P_. - 1. If _T_ is the this-type _o.this_ of a static object _o_, the anchors of a term reference _o.type_ to that object. + 1. If _T_ is the this-type _o.this_ of a static object _o_, the anchors of a term reference _o.type_ to that object, + 1. If _T_ is some other this-type _P.this.type_, the anchors of _P_. 1. If _T_ is some other type, the union of the anchors of each constituent type of _T_. **Definition:** The _implicit scope_ of a type _T_ is the smallest set _S_ of term references such that diff --git a/tests/pos/this-implicit-scope.scala b/tests/pos/this-implicit-scope.scala new file mode 100644 index 000000000000..ead235e53bce --- /dev/null +++ b/tests/pos/this-implicit-scope.scala @@ -0,0 +1,6 @@ +class Foo[+T] +class Elem: + def one(a: Elem, x: Foo[a.type]): Int = x.ext + def two(x: Foo[Elem.this.type]): Int = x.ext +object Elem: + extension (x: Foo[Elem]) def ext: Int = 1 From f22420fea5662f883a5daa5ec00921fea74bb218 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 12 Mar 2023 13:16:09 +0100 Subject: [PATCH 246/657] Handle @companionClass and @companionMethod meta-annotations Fixes #17002 --- .../dotty/tools/dotc/core/Definitions.scala | 4 +++- .../tools/dotc/core/SymDenotations.scala | 9 ++++++--- .../dotty/tools/dotc/transform/Memoize.scala | 4 ++-- .../tools/dotc/transform/PostTyper.scala | 16 +++++++++++---- tests/neg/i17002.scala | 20 +++++++++++++++++++ tests/pos/i17002.scala | 10 ++++++++++ 6 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 tests/neg/i17002.scala create mode 100644 tests/pos/i17002.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 20a1d59316eb..148b314220a8 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1028,6 +1028,8 @@ class Definitions { @tu lazy val GetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.getter") @tu lazy val ParamMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.param") @tu lazy val SetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.setter") + @tu lazy val CompanionClassMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.companionClass") + @tu lazy val CompanionMethodMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.companionMethod") @tu lazy val ShowAsInfixAnnot: ClassSymbol = requiredClass("scala.annotation.showAsInfix") @tu lazy val FunctionalInterfaceAnnot: ClassSymbol = requiredClass("java.lang.FunctionalInterface") @tu lazy val TargetNameAnnot: ClassSymbol = requiredClass("scala.annotation.targetName") @@ -1041,7 +1043,7 @@ class Definitions { // A list of meta-annotations that are relevant for fields and accessors @tu lazy val NonBeanMetaAnnots: Set[Symbol] = - Set(FieldMetaAnnot, GetterMetaAnnot, ParamMetaAnnot, SetterMetaAnnot) + Set(FieldMetaAnnot, GetterMetaAnnot, ParamMetaAnnot, SetterMetaAnnot, CompanionClassMetaAnnot, CompanionMethodMetaAnnot) @tu lazy val MetaAnnots: Set[Symbol] = NonBeanMetaAnnots + BeanGetterMetaAnnot + BeanSetterMetaAnnot diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 9d7a3945a1ca..0f94d52f5be9 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -255,10 +255,13 @@ object SymDenotations { def annotationsCarrying(meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): List[Annotation] = annotations.filterConserve(_.hasOneOfMetaAnnotation(meta, orNoneOf = orNoneOf)) - def copyAndKeepAnnotationsCarrying(phase: DenotTransformer, meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): Unit = - if annotations.nonEmpty then + def keepAnnotationsCarrying(phase: DenotTransformer, meta: Set[Symbol], orNoneOf: Set[Symbol] = Set.empty)(using Context): Unit = + updateAnnotationsAfter(phase, annotationsCarrying(meta, orNoneOf = orNoneOf)) + + def updateAnnotationsAfter(phase: DenotTransformer, annots: List[Annotation])(using Context): Unit = + if annots ne annotations then val cpy = copySymDenotation() - cpy.annotations = annotationsCarrying(meta, orNoneOf = orNoneOf) + cpy.annotations = annots cpy.installAfter(phase) /** Optionally, the annotation matching the given class symbol */ diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 5a2eda4101a4..1392d00011a2 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -167,7 +167,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => if isErasableBottomField(field, rhsClass) then erasedBottomTree(rhsClass) else transformFollowingDeep(ref(field))(using ctx.withOwner(sym)) val getterDef = cpy.DefDef(tree)(rhs = getterRhs) - sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot)) + sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot)) Thicket(fieldDef, getterDef) else if sym.isSetter then if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs: @unchecked } // This is intended as an assertion @@ -193,7 +193,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => then Literal(Constant(())) else Assign(ref(field), adaptToField(field, ref(tree.termParamss.head.head.symbol))) val setterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(using ctx.withOwner(sym))) - sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) + sym.keepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) setterDef else // Curiously, some accessors from Scala2 have ' ' suffixes. diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 574db18c9c7f..7f3e47c14732 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -157,14 +157,20 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase checkInferredWellFormed(tree.tpt) if sym.is(Method) then if sym.isSetter then - sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) + sym.keepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot)) + if sym.isOneOf(GivenOrImplicit) then + val cls = sym.info.finalResultType.classSymbol + if cls.isOneOf(GivenOrImplicit) then + sym.updateAnnotationsAfter(thisPhase, + atPhase(thisPhase)(cls.annotationsCarrying(Set(defn.CompanionMethodMetaAnnot))) + ++ sym.annotations) else if sym.is(Param) then - sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) + sym.keepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) else if sym.is(ParamAccessor) then - sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot)) + sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot)) else - sym.copyAndKeepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) + sym.keepAnnotationsCarrying(thisPhase, Set(defn.GetterMetaAnnot, defn.FieldMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) if sym.isScala2Macro && !ctx.settings.XignoreScala2Macros.value then if !sym.owner.unforcedDecls.exists(p => !p.isScala2Macro && p.name == sym.name && p.signature == sym.signature) // Allow scala.reflect.materializeClassTag to be able to compile scala/reflect/package.scala @@ -388,6 +394,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase VarianceChecker.check(tree) annotateExperimental(sym) checkMacroAnnotation(sym) + if sym.isOneOf(GivenOrImplicit) then + sym.keepAnnotationsCarrying(thisPhase, Set(defn.CompanionClassMetaAnnot), orNoneOf = defn.MetaAnnots) tree.rhs match case impl: Template => for parent <- impl.parents do diff --git a/tests/neg/i17002.scala b/tests/neg/i17002.scala new file mode 100644 index 000000000000..c2a21dd3d415 --- /dev/null +++ b/tests/neg/i17002.scala @@ -0,0 +1,20 @@ +import scala.annotation.compileTimeOnly + +sealed trait Test[T] + +object Test: + @compileTimeOnly("Error") + given test0[T]: Test[T] = ??? + + @compileTimeOnly("Error") + given test1[T]: Test[T]() + + @compileTimeOnly("Error") + implicit class ic(x: Int): + def foo = 2 + + test0 // error + + test1 // error + + 2.foo // error \ No newline at end of file diff --git a/tests/pos/i17002.scala b/tests/pos/i17002.scala new file mode 100644 index 000000000000..d33c1bd386d9 --- /dev/null +++ b/tests/pos/i17002.scala @@ -0,0 +1,10 @@ +import scala.annotation.meta.companionMethod + +@companionMethod +class methOnly extends annotation.Annotation + +class Test +object Test: + + @methOnly + given test2[T]: Test with {} From 2ac2315d1550b807fea3c0ec3fab74e8642a3d39 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 12 Mar 2023 18:25:52 +0100 Subject: [PATCH 247/657] Better comparisons for type projections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We already implemented in essence the rule suggested in lampepfl/dotty-feature-requests#14: ``` Γ ⊨ p : T ------------------------ (Sel-<:-Proj) Γ ⊨ p.A <: T#A ``` This rule is implemented in `isSubPrefix`. But we did not get to check that since we concluded prematurely that an alias type would never match. The alias type in question in i17064.scala was ```scala Outer#Inner ``` Since `Outer` is not a path, the asSeenFrom to recover the info of `Outer#Inner this got approximated with a `Nothing` argument and therefore the alias failed. It is important in this case that we could still succeed with a `isSubPrefix` test, which comes later. So we should not return `false` when the prefix is not a singleton. Fixes #17064 --- .../dotty/tools/dotc/core/TypeComparer.scala | 17 ++++++++++++++--- tests/neg/i15525.scala | 2 +- tests/pos/i17064.scala | 10 ++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 tests/pos/i17064.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 9dda18c72f26..07a573e9169d 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -283,17 +283,28 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val ctx = comparerContext given Context = ctx // optimization for performance val info2 = tp2.info + + /** Does `tp2` have a singleton type or NoPrefix as a prefix? + * If that's not the case, following an alias via asSeenFrom could be lossy + * so we should not conclude `false` if comparing aliases fails. + * See pos/i17064.scala for a test case + */ + def hasPrecisePrefix(tp: NamedType) = + tp.prefix.isSingleton || tp.prefix == NoPrefix + info2 match case info2: TypeAlias => if recur(tp1, info2.alias) then return true - if tp2.asInstanceOf[TypeRef].canDropAlias then return false + if tp2.asInstanceOf[TypeRef].canDropAlias && hasPrecisePrefix(tp2) then + return false case _ => tp1 match case tp1: NamedType => tp1.info match { case info1: TypeAlias => if recur(info1.alias, tp2) then return true - if tp1.asInstanceOf[TypeRef].canDropAlias then return false + if tp1.asInstanceOf[TypeRef].canDropAlias && hasPrecisePrefix(tp2) then + return false case _ => } val sym2 = tp2.symbol @@ -302,7 +313,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling // For convenience we want X$ <:< X.type // This is safe because X$ self-type is X.type sym1 = sym1.companionModule - if ((sym1 ne NoSymbol) && (sym1 eq sym2)) + if (sym1 ne NoSymbol) && (sym1 eq sym2) then ctx.erasedTypes || sym1.isStaticOwner || isSubPrefix(tp1.prefix, tp2.prefix) || diff --git a/tests/neg/i15525.scala b/tests/neg/i15525.scala index b14c244b43c8..0813d7c82435 100644 --- a/tests/neg/i15525.scala +++ b/tests/neg/i15525.scala @@ -37,7 +37,7 @@ def element22( transmittable20.Type / transmittable21.Type } = ??? -def test22 = // error +def test22 = Resolution( element22( Resolution(element0), Resolution(element0), // error // error diff --git a/tests/pos/i17064.scala b/tests/pos/i17064.scala new file mode 100644 index 000000000000..e5a1f636f56c --- /dev/null +++ b/tests/pos/i17064.scala @@ -0,0 +1,10 @@ +class HiddenInner[+O<:Outer](val outer:O){ +} + +class Outer{ + type Inner = HiddenInner[this.type] +} + +val o : Outer = new Outer +def a : o.Inner = new o.Inner(o) +val b : Outer#Inner = a // DOES NOT COMPILE \ No newline at end of file From 37e4fe703a883098a4c0e02c91faf23a5681ccf0 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 12 Mar 2023 20:01:12 +0100 Subject: [PATCH 248/657] Check trait constructor for accessibility even if not called at Typer This allows to restrict inheritance of traits even if the constructor is not called, or not called at Typer. Fixes #17089 --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 3 +++ tests/neg/i17089.scala | 4 ++++ 2 files changed, 7 insertions(+) create mode 100644 tests/neg/i17089.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 96149caf215d..f6048f6fb156 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2511,6 +2511,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer checkSimpleKinded(parent) // allow missing type parameters if there are implicit arguments to pass // since we can infer type arguments from them + val constr = psym.primaryConstructor + if constr.exists then + ensureAccessible(constr.termRef, superAccess = true, tree.srcPos) else checkParentCall(result, cls) if cls is Case then diff --git a/tests/neg/i17089.scala b/tests/neg/i17089.scala new file mode 100644 index 000000000000..46968aa6f093 --- /dev/null +++ b/tests/neg/i17089.scala @@ -0,0 +1,4 @@ +object o: + trait T private[o]() + +def test = new o.T { } // error From 603822ec7e80b524033d10b05cb326bb9e347adc Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 13 Mar 2023 02:18:43 -0400 Subject: [PATCH 249/657] Make warning message more accurate. --- compiler/src/dotty/tools/dotc/transform/init/Semantic.scala | 2 +- tests/init/neg/closureLeak.check | 4 ++-- tests/init/neg/default-this.check | 2 +- tests/init/neg/i15459.check | 2 +- tests/init/neg/inlined-method.check | 2 +- tests/init/neg/inner-first.check | 2 +- tests/init/neg/promotion-loop.check | 2 +- tests/init/neg/promotion-segment3.check | 2 +- tests/init/neg/t3273.check | 4 ++-- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 1b7e1266ac13..b8d867e96a7e 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -1148,7 +1148,7 @@ object Semantic: extension (arg: ArgInfo) def promote: Contextual[Unit] = withTrace(arg.trace) { - arg.value.promote("Could not verify that the method argument is transitively initialized (Hot). It was found to be " + arg.value.show + ". Only transitively initialized arguments may be passed to methods outside the analyzed initialization region.") + arg.value.promote("Could not verify that the method argument is transitively initialized (Hot). It was found to be " + arg.value.show + ". Only transitively initialized arguments may be passed to methods (except constructors).") } /** Evaluate an expression with the given value for `this` in a given class `klass` diff --git a/tests/init/neg/closureLeak.check b/tests/init/neg/closureLeak.check index d4ea2ec8ef47..2437509faa73 100644 --- a/tests/init/neg/closureLeak.check +++ b/tests/init/neg/closureLeak.check @@ -1,14 +1,14 @@ -- Error: tests/init/neg/closureLeak.scala:11:14 ----------------------------------------------------------------------- 11 | l.foreach(a => a.addX(this)) // error | ^^^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (class Outer) where initialization checking started) and the function owner is an object of type (class Outer). Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (class Outer) where initialization checking started) and the function owner is an object of type (class Outer). Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: |-> class Outer { [ closureLeak.scala:1 ] | ^ |-> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] | ^^^^^^^^^^^^^^^^^ | |Promoting the value to transitively initialized (Hot) failed due to the following problem: - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class Outer) where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class Outer) where initialization checking started. Only transitively initialized arguments may be passed to methods (except constructors). |Non initialized field(s): value p. Promotion trace: |-> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] | ^^^^ diff --git a/tests/init/neg/default-this.check b/tests/init/neg/default-this.check index 84556f0a9d82..f64f36304e9b 100644 --- a/tests/init/neg/default-this.check +++ b/tests/init/neg/default-this.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/default-this.scala:9:8 ------------------------------------------------------------------------ 9 | compare() // error | ^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class B) where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class B) where initialization checking started. Only transitively initialized arguments may be passed to methods (except constructors). |Non initialized field(s): value result. Calling trace: |-> class B extends A { [ default-this.scala:6 ] | ^ diff --git a/tests/init/neg/i15459.check b/tests/init/neg/i15459.check index fffeabd560e1..a8c9972276db 100644 --- a/tests/init/neg/i15459.check +++ b/tests/init/neg/i15459.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/i15459.scala:3:10 ----------------------------------------------------------------------------- 3 | println(this) // error | ^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class Sub) where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class Sub) where initialization checking started. Only transitively initialized arguments may be passed to methods (except constructors). |Non initialized field(s): value b. Calling trace: |-> class Sub extends Sup: [ i15459.scala:5 ] | ^ diff --git a/tests/init/neg/inlined-method.check b/tests/init/neg/inlined-method.check index 9751ef09b0c1..f3061bcb63ed 100644 --- a/tests/init/neg/inlined-method.check +++ b/tests/init/neg/inlined-method.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/inlined-method.scala:8:45 --------------------------------------------------------------------- 8 | scala.runtime.Scala3RunTime.assertFailed(message) // error | ^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class InlineError) where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class InlineError) where initialization checking started. Only transitively initialized arguments may be passed to methods (except constructors). |Non initialized field(s): value v. Calling trace: |-> class InlineError { [ inlined-method.scala:1 ] | ^ diff --git a/tests/init/neg/inner-first.check b/tests/init/neg/inner-first.check index 9340479f79a9..fe90423c828f 100644 --- a/tests/init/neg/inner-first.check +++ b/tests/init/neg/inner-first.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/inner-first.scala:3:12 ------------------------------------------------------------------------ 3 | println(this) // error | ^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class B) where initialization checking started. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. + |Could not verify that the method argument is transitively initialized (Hot). It was found to be the original object of type (class B) where initialization checking started. Only transitively initialized arguments may be passed to methods (except constructors). |Non initialized field(s): value n. Calling trace: |-> class B: [ inner-first.scala:2 ] | ^ diff --git a/tests/init/neg/promotion-loop.check b/tests/init/neg/promotion-loop.check index ec0a6854bbac..bc05640d10d2 100644 --- a/tests/init/neg/promotion-loop.check +++ b/tests/init/neg/promotion-loop.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/promotion-loop.scala:16:10 -------------------------------------------------------------------- 16 | println(b) // error | ^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a non-transitively initialized (Warm) object of type (class B) { outer = the original object of type (class Test) where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a non-transitively initialized (Warm) object of type (class B) { outer = the original object of type (class Test) where initialization checking started }. Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: |-> class Test { test => [ promotion-loop.scala:1 ] | ^ |-> println(b) // error [ promotion-loop.scala:16 ] diff --git a/tests/init/neg/promotion-segment3.check b/tests/init/neg/promotion-segment3.check index bc7a076d7d72..a7320b5c3ed3 100644 --- a/tests/init/neg/promotion-segment3.check +++ b/tests/init/neg/promotion-segment3.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/promotion-segment3.scala:9:6 ------------------------------------------------------------------ 9 | bar(new B) // error | ^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a non-transitively initialized (Warm) object of type (class B) { outer = the original object of type (class A) where initialization checking started }. Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a non-transitively initialized (Warm) object of type (class B) { outer = the original object of type (class A) where initialization checking started }. Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: |-> class A: [ promotion-segment3.scala:2 ] | ^ |-> bar(new B) // error [ promotion-segment3.scala:9 ] diff --git a/tests/init/neg/t3273.check b/tests/init/neg/t3273.check index a63f7bc7a5ea..2e4d8db50ebb 100644 --- a/tests/init/neg/t3273.check +++ b/tests/init/neg/t3273.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/t3273.scala:4:42 ------------------------------------------------------------------------------ 4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error | ^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (object Test) where initialization checking started) and the function owner is an object of type (object Test). Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (object Test) where initialization checking started) and the function owner is an object of type (object Test). Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: |-> object Test { [ t3273.scala:3 ] | ^ |-> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ] @@ -14,7 +14,7 @@ -- Error: tests/init/neg/t3273.scala:5:61 ------------------------------------------------------------------------------ 5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (object Test) where initialization checking started) and the function owner is an object of type (object Test). Only transitively initialized arguments may be passed to methods outside the analyzed initialization region. Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (object Test) where initialization checking started) and the function owner is an object of type (object Test). Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: |-> object Test { [ t3273.scala:3 ] | ^ |-> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ] From ddafb7819c0f36b8583224174bf6de87a1a90c0c Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 13 Mar 2023 03:01:21 -0400 Subject: [PATCH 250/657] Implement suggested change. --- compiler/src/dotty/tools/dotc/transform/init/Cache.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala index 2dcd6360554a..4d091d88eefd 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala @@ -85,7 +85,9 @@ class Cache[Config, Res]: protected given MutableTreeWrapper = new MutableTreeWrapper def get(config: Config, expr: Tree): Option[Res] = - current.get(config, expr) + val res = current.get(config, expr) + cacheUsed = cacheUsed || res.nonEmpty + res /** Evaluate an expression with cache * @@ -105,9 +107,7 @@ class Cache[Config, Res]: */ def cachedEval(config: Config, expr: Tree, cacheResult: Boolean, default: Res)(eval: Tree => Res): Res = this.get(config, expr) match - case Some(value) => - cacheUsed = true - value + case Some(value) => value case None => val assumeValue: Res = this.last.get(config, expr) match From 7a7f144afba36015d345342241cc38f8804e6df7 Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 13 Mar 2023 03:23:29 -0400 Subject: [PATCH 251/657] Fix test case to error when missing cacheResult flag --- .../src/dotty/tools/dotc/transform/init/Semantic.scala | 2 +- tests/init/pos/recursion.scala | 9 --------- tests/init/pos/self-ref.scala | 9 +++++++++ 3 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 tests/init/pos/recursion.scala create mode 100644 tests/init/pos/self-ref.scala diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index 74e14691a45e..82bb45851f72 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -893,7 +893,7 @@ object Semantic: case Cold => Cold - case ref: Ref => eval(vdef.rhs, ref, enclosingClass) + case ref: Ref => eval(vdef.rhs, ref, enclosingClass, cacheResult = sym.is(Flags.Lazy)) case _ => report.error("[Internal error] unexpected this value when accessing local variable, sym = " + sym.show + ", thisValue = " + thisValue2.show + Trace.show, Trace.position) diff --git a/tests/init/pos/recursion.scala b/tests/init/pos/recursion.scala deleted file mode 100644 index 394092945cb3..000000000000 --- a/tests/init/pos/recursion.scala +++ /dev/null @@ -1,9 +0,0 @@ -class A { - - val f: Int => Int = { - x => f(x) - } - - f(5) - -} diff --git a/tests/init/pos/self-ref.scala b/tests/init/pos/self-ref.scala new file mode 100644 index 000000000000..1a9f199b9f7a --- /dev/null +++ b/tests/init/pos/self-ref.scala @@ -0,0 +1,9 @@ +class A { + def foo(a: Int) = { + lazy val x: Int = if (a == 0) x else 0 + println(x) + } + foo(0) + + val y = 5 +} From 4172269bf12e22b314cdabe8825d2d31f195c981 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 09:17:17 +0100 Subject: [PATCH 252/657] Strip annotations from the type of a quoted expression These annotations may contain spliced expression that would leak out of the quote if not stripped. See `tests/pos-macros/i7519b.scala`. --- .../src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index 6d3b62357d7f..df9e780cd388 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -103,6 +103,9 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) + val stripAnnotsDeep: TypeMap = new TypeMap: + def apply(tp: Type): Type = mapOver(tp.stripAnnots) + val contextWithQuote = if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext) else quoteContext @@ -115,7 +118,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( if body.isTerm then // `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T2]()` val TypeApply(fun, targs) = quote.fun: @unchecked - val targs2 = targs.map(targ => TypeTree(healTypeOfTerm(quote.fun.srcPos)(targ.tpe))) + val targs2 = targs.map(targ => TypeTree(healTypeOfTerm(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), body2 :: Nil) else val quotes = quote.args.mapConserve(transform) From 7347e03680f7d436f3c4817101e89f2e37da467c Mon Sep 17 00:00:00 2001 From: David Hua Date: Mon, 13 Mar 2023 04:31:39 -0400 Subject: [PATCH 253/657] Fix * alignment in comment. --- compiler/src/dotty/tools/dotc/transform/init/Cache.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala index 4d091d88eefd..36c0d8c175f8 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala @@ -75,10 +75,10 @@ class Cache[Config, Res]: */ protected var changed: Boolean = false - /** Whether any value in the cache was accessed after being added. - * If no cached values are used after they are added for the first time - * then another iteration of analysis is not needed. - */ + /** Whether any value in the output cache (this.current) was accessed + * after being added. If no cached values are used after they are added + * for the first time then another iteration of analysis is not needed. + */ protected var cacheUsed: Boolean = false /** Used to avoid allocation, its state does not matter */ From 391730ae4df3c1a046b57734cb19b19fdbb5f4c8 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 13 Mar 2023 10:34:50 +0100 Subject: [PATCH 254/657] Don't check parents of refinement classes for accessibility Only check trait constructor accessibility if the trait is a parent of some other class or trait. Refinements don't fall into this category. --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index f6048f6fb156..d45e24bf30e5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2512,7 +2512,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // allow missing type parameters if there are implicit arguments to pass // since we can infer type arguments from them val constr = psym.primaryConstructor - if constr.exists then + if psym.is(Trait) && constr.exists && !cls.isRefinementClass then ensureAccessible(constr.termRef, superAccess = true, tree.srcPos) else checkParentCall(result, cls) From 121af881cf3f66d291cb4343330c905781c91dd9 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 13 Mar 2023 11:22:31 +0000 Subject: [PATCH 255/657] Add a Java protected member case, which can only call super --- .../tools/dotc/transform/ProtectedAccessors.scala | 2 +- tests/run/i17021.ext-java/A.java | 6 ++++++ tests/run/i17021.ext-java/Test.scala | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/run/i17021.ext-java/A.java create mode 100644 tests/run/i17021.ext-java/Test.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala index c675938b86f7..2b150d321812 100644 --- a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala @@ -38,7 +38,7 @@ object ProtectedAccessors { sym.isTerm && sym.is(Protected) && !sym.owner.is(Trait) && // trait methods need to be handled specially, are currently always public !insideBoundaryOf(sym) && - !ctx.owner.enclosingClass.derivesFrom(sym.owner) + (sym.is(JavaDefined) || !ctx.owner.enclosingClass.derivesFrom(sym.owner)) } class ProtectedAccessors extends MiniPhase { diff --git a/tests/run/i17021.ext-java/A.java b/tests/run/i17021.ext-java/A.java new file mode 100644 index 000000000000..536e9caa4a38 --- /dev/null +++ b/tests/run/i17021.ext-java/A.java @@ -0,0 +1,6 @@ +// Derives from run/i17021.defs, but with a Java protected member +package p1; + +public class A { + protected int foo() { return 1; } +} diff --git a/tests/run/i17021.ext-java/Test.scala b/tests/run/i17021.ext-java/Test.scala new file mode 100644 index 000000000000..80dbca9fdd5d --- /dev/null +++ b/tests/run/i17021.ext-java/Test.scala @@ -0,0 +1,14 @@ +// Derives from run/i17021.defs +// but with a Java protected member +// which changes the behaviour +package p2: + trait B extends p1.A: + def bar: Int = foo + + class C extends B: + override def foo: Int = 2 + +object Test: + def main(args: Array[String]): Unit = + val n = new p2.C().bar + assert(n == 1, n) // B can only call super.foo From 8362d14c77d33fa597d94d6d46b18d9c6b3f3cb9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 10 Mar 2023 17:32:33 +0100 Subject: [PATCH 256/657] Refactor level checking / type healing logic Disentangle level checking from type healing. --- .../dotc/transform/PCPCheckAndHeal.scala | 98 +++++++++---------- .../tools/dotc/transform/TreeChecker.scala | 2 +- 2 files changed, 49 insertions(+), 51 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index df9e780cd388..23ad3cadf1a4 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -61,24 +61,26 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( super.transform(tree) else tree match { - case _: TypeTree | _: RefTree if tree.isType => + case _: TypeTree | _: RefTree if tree.isType => val healedType = healType(tree.srcPos)(tree.tpe) if healedType == tree.tpe then tree else TypeTree(healedType).withSpan(tree.span) + case tree: Ident if isWildcardArg(tree) => + tree.withType(healType(tree.srcPos)(tree.tpe)) + case tree: Ident => // this is a term Ident + checkLevelConsistency(tree) + tree + case tree: This => + checkLevelConsistency(tree) + tree case _: AppliedTypeTree => super.transform(tree) match case tree1: AppliedTypeTree if tree1 ne tree => // propagate healed types tree1.withType(tree1.tpt.tpe.appliedTo(tree1.args.map(_.tpe))) case tree1 => tree1 - - case _: Ident | _: This => - tree.withType(healTypeOfTerm(tree.srcPos)(tree.tpe)) - - // Remove inline defs in quoted code. Already fully inlined. case tree: DefDef if tree.symbol.is(Inline) && level > 0 => - EmptyTree - + EmptyTree // Remove inline defs in quoted code. Already fully inlined. case tree: ValOrDefDef => checkAnnotations(tree) healInfo(tree, tree.tpt.srcPos) @@ -88,7 +90,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( healInfo(tree, tree.srcPos) super.transform(tree) case tree: UnApply => - super.transform(tree).withType(healTypeOfTerm(tree.srcPos)(tree.tpe)) + super.transform(tree).withType(healType(tree.srcPos)(tree.tpe)) case tree: TypeDef if tree.symbol.is(Case) && level > 0 => report.error(reporting.CaseClassInInlinedCode(tree), tree) super.transform(tree) @@ -118,7 +120,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( if body.isTerm then // `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T2]()` val TypeApply(fun, targs) = quote.fun: @unchecked - val targs2 = targs.map(targ => TypeTree(healTypeOfTerm(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) + val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), body2 :: Nil) else val quotes = quote.args.mapConserve(transform) @@ -193,61 +195,57 @@ class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages( def apply(tp: Type): Type = tp match case tp: TypeRef => - tp.prefix match - case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => - tryHealTypeOfType(tp.symbol, tp, pos) - case prefix: ThisType if !tp.symbol.isStatic && level > levelOf(prefix.cls) => - tryHealTypeOfType(tp.symbol, tp, pos) - case prefix: TermRef if tp.symbol.isTypeSplice => - prefix.symbol.info.argInfos match - case (tb: TypeBounds) :: _ => - report.error(em"Cannot splice $tp because it is a wildcard type", pos) - case _ => - // Heal explicit type splice in the code - if level > 0 then getQuoteTypeTags.getTagRef(prefix) else tp - case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) => - tryHealTypeOfType(prefix.symbol, tp, pos) - case _ => - mapOver(tp) + healTypeRef(tp) case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) => levelError(tp.symbol, tp, pos) - case tp: ThisType if level != -1 && level != levelOf(tp.cls) => - levelError(tp.cls, tp, pos) case tp: AnnotatedType => val newAnnotTree = transform(tp.annot.tree) derivedAnnotatedType(tp, apply(tp.parent), tp.annot.derivedAnnotation(newAnnotTree)) case _ => mapOver(tp) - /** Try to dealias or heal reference to type `T` used in a higher level than its definition. - * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a - * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. - * Emits and error if `T` cannot be healed and returns `T`. - */ - private def tryHealTypeOfType(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): Type = { + private def healTypeRef(tp: TypeRef): Type = + tp.prefix match + case prefix: TermRef if tp.symbol.isTypeSplice => + checkNotWildcardSplice(tp) + if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix) + case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) => + dealiasAndTryHeal(prefix.symbol, tp, pos) + case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => + dealiasAndTryHeal(tp.symbol, tp, pos) + case prefix: ThisType if level > levelOf(prefix.cls) && !tp.symbol.isStatic => + dealiasAndTryHeal(tp.symbol, tp, pos) + case _ => + mapOver(tp) + + private def dealiasAndTryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): Type = val tp1 = tp.dealias if tp1 != tp then apply(tp1) else tryHeal(tp.symbol, tp, pos) - } - } - /** Check phase consistency of terms and heal inconsistent type references. */ - private def healTypeOfTerm(pos: SrcPos)(using Context) = new TypeMap { - def apply(tp: Type): Type = - tp match - case tp @ TypeRef(NoPrefix, _) if level > levelOf(tp.symbol) => - tryHeal(tp.symbol, tp, pos) - case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level != levelOf(tp.symbol) => - levelError(tp.symbol, tp, pos) - case tp: ThisType if level != -1 && level != levelOf(tp.cls) => - levelError(tp.cls, tp, pos) - case tp: AnnotatedType => - derivedAnnotatedType(tp, apply(tp.parent), tp.annot) + private def checkNotWildcardSplice(splice: TypeRef)(using Context): Unit = + splice.prefix.termSymbol.info.argInfos match + case (tb: TypeBounds) :: _ => report.error(em"Cannot splice $splice because it is a wildcard type", pos) case _ => - if tp.typeSymbol.is(Package) then tp - else mapOver(tp) } + /** Check level consistency of terms references */ + private def checkLevelConsistency(tree: Ident | This)(using Context): Unit = + new TypeTraverser { + def traverse(tp: Type): Unit = + tp match + case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level != levelOf(tp.symbol) => + levelError(tp.symbol, tp, tree.srcPos) + case tp: ThisType if level != -1 && level != levelOf(tp.cls) => + levelError(tp.cls, tp, tree.srcPos) + case tp: AnnotatedType => + traverse(tp.parent) + case _ if tp.typeSymbol.is(Package) => + // OK + case _ => + traverseChildren(tp) + }.traverse(tree.tpe) + /** Try to heal reference to type `T` used in a higher level than its definition. * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 52942ea719f9..309c61f5c7d6 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -678,7 +678,7 @@ object TreeChecker { defn.FunctionOf(List(defn.QuotesClass.typeRef), expectedResultType, isContextual = true) val expectedContentType = defn.FunctionOf(argQuotedTypes, contextualResult) - assert(content.typeOpt =:= expectedContentType) + assert(content.typeOpt =:= expectedContentType, i"expected content of the hole to be ${expectedContentType} but got ${content.typeOpt}") tree1 } From 67a647543d91ea60fb67fa4fbba6de24f4d3c8bc Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 10:10:51 +0100 Subject: [PATCH 257/657] Extract StagingLevel from TreeMapWithStages --- .../dotc/inlines/PrepareInlineable.scala | 4 +- .../dotty/tools/dotc/quoted/Interpreter.scala | 1 - .../tools/dotc/staging/StagingLevel.scala | 38 +++++++++++++++++++ .../dotc/transform/PCPCheckAndHeal.scala | 5 +-- .../tools/dotc/transform/PickleQuotes.scala | 4 +- .../tools/dotc/transform/ReifiedReflect.scala | 1 - .../dotty/tools/dotc/transform/Splicing.scala | 10 ++--- .../dotty/tools/dotc/transform/Staging.scala | 10 ++--- .../dotc/transform/TreeMapWithStages.scala | 29 ++------------ 9 files changed, 58 insertions(+), 44 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/staging/StagingLevel.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 85293d4a82d7..f1bef77f684b 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -21,7 +21,7 @@ import transform.{AccessProxies, PCPCheckAndHeal, Splicer} import transform.SymUtils.* import config.Printers.inlining import util.Property -import dotty.tools.dotc.transform.TreeMapWithStages._ +import dotty.tools.dotc.staging.StagingLevel.freshStagingLevelContext object PrepareInlineable { import tpd._ @@ -293,7 +293,7 @@ object PrepareInlineable { if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) - new PCPCheckAndHeal(freshStagingContext).transform(body) // Ignore output, only check PCP + (new PCPCheckAndHeal).transform(body)(using freshStagingLevelContext) // Ignore output, only check PCP case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat) case Block(Nil, expr) => checkMacro(expr) case Typed(expr, _) => checkMacro(expr) diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index aadfedd2417c..7153c17e30ed 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -25,7 +25,6 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.TypeErasure import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.quoted._ -import dotty.tools.dotc.transform.TreeMapWithStages._ import dotty.tools.dotc.typer.ImportInfo.withRootImports import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.reporting.Message diff --git a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala new file mode 100644 index 000000000000..356c157f7f57 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala @@ -0,0 +1,38 @@ +package dotty.tools.dotc.staging + +import dotty.tools.dotc.ast.{TreeMapWithImplicits, tpd} +import dotty.tools.dotc.config.Printers.staging +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.StagingContext._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.util.Property + +import scala.collection.mutable + +object StagingLevel { + + /** A key to be used in a context property that caches the `levelOf` mapping */ + private val LevelOfKey = new Property.Key[mutable.HashMap[Symbol, Int]] + + /** Initial context for a StagingTransformer transformation. */ + def freshStagingLevelContext(using Context): Context = + ctx.fresh.setProperty(LevelOfKey, new mutable.HashMap[Symbol, Int]) + + /** The quotation level of the definition of the locally defined symbol */ + def levelOf(sym: Symbol)(using Context): Int = + ctx.property(LevelOfKey).get.getOrElse(sym, 0) + + def removeLevelOf(sym: Symbol)(using Context): Unit = + val levelOfMap = ctx.property(LevelOfKey).get + levelOfMap -= sym + + /** Enter staging level of symbol defined by `tree` */ + def markSymbol(sym: Symbol)(using Context): Boolean = + val levelOfMap = ctx.property(LevelOfKey).get + if level != 0 && !levelOfMap.contains(sym) then + levelOfMap(sym) = level + true + else + false +} \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala index 23ad3cadf1a4..1f6621558a45 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala @@ -17,11 +17,10 @@ import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.typer.Checking import dotty.tools.dotc.typer.Implicits.SearchFailureType import dotty.tools.dotc.core.Annotations._ +import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.util.Property -import scala.annotation.constructorOnly - /** Checks that the Phase Consistency Principle (PCP) holds and heals types. * * Local term references are phase consistent if and only if they are used at the same level as their definition. @@ -48,7 +47,7 @@ import scala.annotation.constructorOnly * } * */ -class PCPCheckAndHeal(@constructorOnly ictx: Context) extends TreeMapWithStages(ictx) with Checking { +class PCPCheckAndHeal extends TreeMapWithStages with Checking { import tpd._ private val InAnnotation = Property.Key[Unit]() diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 87c6e294c104..9a381eaafa66 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -19,7 +19,7 @@ import scala.collection.mutable import dotty.tools.dotc.core.Annotations._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ -import dotty.tools.dotc.transform.TreeMapWithStages._ +import dotty.tools.dotc.staging.StagingLevel.freshStagingLevelContext import dotty.tools.dotc.inlines.Inlines import scala.annotation.constructorOnly @@ -93,7 +93,7 @@ class PickleQuotes extends MacroTransform { case _ => override def run(using Context): Unit = - if (ctx.compilationUnit.needsStaging) super.run(using freshStagingContext) + if (ctx.compilationUnit.needsStaging) super.run(using freshStagingLevelContext) protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = diff --git a/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala b/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala index e462f82b1dad..b2059195b8e4 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala @@ -17,7 +17,6 @@ import dotty.tools.dotc.core.Annotations._ import dotty.tools.dotc.core.Names._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ -import dotty.tools.dotc.transform.TreeMapWithStages._ import scala.annotation.constructorOnly diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 393fe46b8438..8e0535e3af50 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -21,7 +21,7 @@ import dotty.tools.dotc.core.Annotations._ import dotty.tools.dotc.core.Names._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ -import dotty.tools.dotc.transform.TreeMapWithStages._ +import dotty.tools.dotc.staging.StagingLevel.freshStagingLevelContext import dotty.tools.dotc.config.ScalaRelease.* import scala.annotation.constructorOnly @@ -77,7 +77,7 @@ class Splicing extends MacroTransform: override def run(using Context): Unit = if ctx.compilationUnit.needsStaging then - super.run(using freshStagingContext) + super.run(using freshStagingLevelContext) protected def newTransformer(using Context): Transformer = Level0QuoteTransformer @@ -246,7 +246,7 @@ class Splicing extends MacroTransform: if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => val newContent = capturedPartTypes(tpt) newContent match - case block: Block => + case block: Block => inContext(ctx.withSource(tree.source)) { Apply(TypeApply(typeof, List(newContent)), List(quotes)).withSpan(tree.span) } @@ -342,7 +342,7 @@ class Splicing extends MacroTransform: val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2 ref(bindingSym) - private def newQuotedTypeClassBinding(tpe: Type)(using Context) = + private def newQuotedTypeClassBinding(tpe: Type)(using Context) = newSymbol( spliceOwner, UniqueName.fresh(nme.Type).toTermName, @@ -376,7 +376,7 @@ class Splicing extends MacroTransform: tpt match case block: Block => cpy.Block(block)(newHealedTypes ::: block.stats, TypeTree(captured)) - case _ => + case _ => if newHealedTypes.nonEmpty then cpy.Block(tpt)(newHealedTypes, TypeTree(captured)) else diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 1de050a9a6c1..ea7c8d1b39b7 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -11,7 +11,7 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.transform.SymUtils._ -import dotty.tools.dotc.transform.TreeMapWithStages._ +import dotty.tools.dotc.staging.StagingLevel.* @@ -35,7 +35,7 @@ class Staging extends MacroTransform { // Recheck that PCP holds but do not heal any inconsistent types as they should already have been heald tree match { case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass => - val checker = new PCPCheckAndHeal(freshStagingContext) { + val checker = new PCPCheckAndHeal { override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = { def symStr = if (sym.is(ModuleClass)) sym.sourceModule.show @@ -51,7 +51,7 @@ class Staging extends MacroTransform { tp } } - checker.transform(tree) + checker.transform(tree)(using freshStagingLevelContext) case _ => } @@ -66,11 +66,11 @@ class Staging extends MacroTransform { } override def run(using Context): Unit = - if (ctx.compilationUnit.needsStaging) super.run(using freshStagingContext) + if (ctx.compilationUnit.needsStaging) super.run(using freshStagingLevelContext) protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = - new PCPCheckAndHeal(ctx).transform(tree) + (new PCPCheckAndHeal).transform(tree) } } diff --git a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala index b514b8a7bf11..1046194a04e4 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala @@ -8,9 +8,9 @@ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.util.Property +import dotty.tools.dotc.staging.StagingLevel import scala.collection.mutable -import scala.annotation.constructorOnly /** The main transformer class * @param level the current level, where quotes add one and splices subtract one level. @@ -18,13 +18,8 @@ import scala.annotation.constructorOnly * and `l == -1` is code inside a top level splice (in an inline method). * @param levels a stacked map from symbols to the levels in which they were defined */ -abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMapWithImplicits { - +abstract class TreeMapWithStages extends TreeMapWithImplicits { import tpd._ - import TreeMapWithStages._ - - /** A map from locally defined symbols to their definition quotation level */ - private[this] val levelOfMap: mutable.HashMap[Symbol, Int] = ictx.property(LevelOfKey).get /** A stack of entered symbols, to be unwound after scope exit */ private[this] var enteredSyms: List[Symbol] = Nil @@ -32,9 +27,6 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap /** If we are inside a quote or a splice */ private[this] var inQuoteOrSplice = false - /** The quotation level of the definition of the locally defined symbol */ - protected def levelOf(sym: Symbol): Int = levelOfMap.getOrElse(sym, 0) - /** Locally defined symbols seen so far by `StagingTransformer.transform` */ protected def localSymbols: List[Symbol] = enteredSyms @@ -43,8 +35,7 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap /** Enter staging level of symbol defined by `tree` */ private def markSymbol(sym: Symbol)(using Context): Unit = - if level != 0 && !levelOfMap.contains(sym) then - levelOfMap(sym) = level + if StagingLevel.markSymbol(sym) then enteredSyms = sym :: enteredSyms /** Enter staging level of symbol defined by `tree`, if applicable. */ @@ -79,7 +70,7 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap try super.transform(tree) finally while (enteredSyms ne lastEntered) { - levelOfMap -= enteredSyms.head + StagingLevel.removeLevelOf(enteredSyms.head) enteredSyms = enteredSyms.tail } @@ -150,15 +141,3 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap } } } - - -object TreeMapWithStages { - - /** A key to be used in a context property that caches the `levelOf` mapping */ - private val LevelOfKey = new Property.Key[mutable.HashMap[Symbol, Int]] - - /** Initial context for a StagingTransformer transformation. */ - def freshStagingContext(using Context): Context = - ctx.fresh.setProperty(LevelOfKey, new mutable.HashMap[Symbol, Int]) - -} From 527a9ec83642aef046a72eba91c49134755e3e89 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 11:34:24 +0100 Subject: [PATCH 258/657] Remove mutable state from StagingLevel --- .../dotc/inlines/PrepareInlineable.scala | 3 +- .../tools/dotc/staging/StagingLevel.scala | 28 ++++----- .../tools/dotc/transform/PickleQuotes.scala | 3 +- .../dotty/tools/dotc/transform/Splicing.scala | 3 +- .../dotty/tools/dotc/transform/Staging.scala | 4 +- .../dotc/transform/TreeMapWithStages.scala | 60 ++++++------------- 6 files changed, 33 insertions(+), 68 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index f1bef77f684b..837f9a60d6ea 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -21,7 +21,6 @@ import transform.{AccessProxies, PCPCheckAndHeal, Splicer} import transform.SymUtils.* import config.Printers.inlining import util.Property -import dotty.tools.dotc.staging.StagingLevel.freshStagingLevelContext object PrepareInlineable { import tpd._ @@ -293,7 +292,7 @@ object PrepareInlineable { if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) - (new PCPCheckAndHeal).transform(body)(using freshStagingLevelContext) // Ignore output, only check PCP + (new PCPCheckAndHeal).transform(body) // Ignore output, only check PCP case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat) case Block(Nil, expr) => checkMacro(expr) case Typed(expr, _) => checkMacro(expr) diff --git a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala index 356c157f7f57..4cf57d28ed8c 100644 --- a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala +++ b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala @@ -13,26 +13,20 @@ import scala.collection.mutable object StagingLevel { /** A key to be used in a context property that caches the `levelOf` mapping */ - private val LevelOfKey = new Property.Key[mutable.HashMap[Symbol, Int]] - - /** Initial context for a StagingTransformer transformation. */ - def freshStagingLevelContext(using Context): Context = - ctx.fresh.setProperty(LevelOfKey, new mutable.HashMap[Symbol, Int]) + private val LevelOfKey = new Property.Key[Map[Symbol, Int]] /** The quotation level of the definition of the locally defined symbol */ def levelOf(sym: Symbol)(using Context): Int = - ctx.property(LevelOfKey).get.getOrElse(sym, 0) - - def removeLevelOf(sym: Symbol)(using Context): Unit = - val levelOfMap = ctx.property(LevelOfKey).get - levelOfMap -= sym + ctx.property(LevelOfKey) match + case Some(map) => map.getOrElse(sym, 0) + case None => 0 - /** Enter staging level of symbol defined by `tree` */ - def markSymbol(sym: Symbol)(using Context): Boolean = - val levelOfMap = ctx.property(LevelOfKey).get - if level != 0 && !levelOfMap.contains(sym) then - levelOfMap(sym) = level - true + /** Context with the current staging level set for the symbols */ + def symbolsInCurrentLevel(syms: List[Symbol])(using Context): Context = + if level == 0 then ctx else - false + val levelOfMap = ctx.property(LevelOfKey).getOrElse(Map.empty) + val syms1 = syms//.filter(sym => !levelOfMap.contains(sym)) + val newMap = syms1.foldLeft(levelOfMap)((acc, sym) => acc.updated(sym, level)) + ctx.fresh.setProperty(LevelOfKey, newMap) } \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 9a381eaafa66..728ee9552c81 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -19,7 +19,6 @@ import scala.collection.mutable import dotty.tools.dotc.core.Annotations._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ -import dotty.tools.dotc.staging.StagingLevel.freshStagingLevelContext import dotty.tools.dotc.inlines.Inlines import scala.annotation.constructorOnly @@ -93,7 +92,7 @@ class PickleQuotes extends MacroTransform { case _ => override def run(using Context): Unit = - if (ctx.compilationUnit.needsStaging) super.run(using freshStagingLevelContext) + if (ctx.compilationUnit.needsStaging) super.run protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 8e0535e3af50..d31b92fc17db 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -21,7 +21,6 @@ import dotty.tools.dotc.core.Annotations._ import dotty.tools.dotc.core.Names._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ -import dotty.tools.dotc.staging.StagingLevel.freshStagingLevelContext import dotty.tools.dotc.config.ScalaRelease.* import scala.annotation.constructorOnly @@ -77,7 +76,7 @@ class Splicing extends MacroTransform: override def run(using Context): Unit = if ctx.compilationUnit.needsStaging then - super.run(using freshStagingLevelContext) + super.run protected def newTransformer(using Context): Transformer = Level0QuoteTransformer diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index ea7c8d1b39b7..2e8964c8b1b4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -51,7 +51,7 @@ class Staging extends MacroTransform { tp } } - checker.transform(tree)(using freshStagingLevelContext) + checker.transform(tree) case _ => } @@ -66,7 +66,7 @@ class Staging extends MacroTransform { } override def run(using Context): Unit = - if (ctx.compilationUnit.needsStaging) super.run(using freshStagingLevelContext) + if (ctx.compilationUnit.needsStaging) super.run protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = diff --git a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala index 1046194a04e4..a4535a639c13 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala @@ -8,42 +8,20 @@ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.util.Property -import dotty.tools.dotc.staging.StagingLevel +import dotty.tools.dotc.staging.StagingLevel.* import scala.collection.mutable -/** The main transformer class - * @param level the current level, where quotes add one and splices subtract one level. - * The initial level is 0, a level `l` where `l > 0` implies code has been quoted `l` times - * and `l == -1` is code inside a top level splice (in an inline method). - * @param levels a stacked map from symbols to the levels in which they were defined - */ +/** TreeMap that keeps track of staging levels using StagingLevel. */ abstract class TreeMapWithStages extends TreeMapWithImplicits { import tpd._ - /** A stack of entered symbols, to be unwound after scope exit */ - private[this] var enteredSyms: List[Symbol] = Nil - /** If we are inside a quote or a splice */ private[this] var inQuoteOrSplice = false - /** Locally defined symbols seen so far by `StagingTransformer.transform` */ - protected def localSymbols: List[Symbol] = enteredSyms - /** If we are inside a quote or a splice */ protected def isInQuoteOrSplice: Boolean = inQuoteOrSplice - /** Enter staging level of symbol defined by `tree` */ - private def markSymbol(sym: Symbol)(using Context): Unit = - if StagingLevel.markSymbol(sym) then - enteredSyms = sym :: enteredSyms - - /** Enter staging level of symbol defined by `tree`, if applicable. */ - private def markDef(tree: Tree)(using Context): Unit = tree match { - case tree: DefTree => markSymbol(tree.symbol) - case _ => - } - /** Transform the quote `quote` which contains the quoted `body`. * * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` @@ -66,14 +44,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) else reporting.trace(i"StagingTransformer.transform $tree at $level", staging, show = true) { - def mapOverTree(lastEntered: List[Symbol]) = - try super.transform(tree) - finally - while (enteredSyms ne lastEntered) { - StagingLevel.removeLevelOf(enteredSyms.head) - enteredSyms = enteredSyms.tail - } - def dropEmptyBlocks(tree: Tree): Tree = tree match { case Block(Nil, expr) => dropEmptyBlocks(expr) case _ => tree @@ -118,26 +88,30 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { finally inQuoteOrSplice = old case Block(stats, _) => - val last = enteredSyms - stats.foreach(markDef) - mapOverTree(last) + val defSyms = stats.collect { case defTree: DefTree => defTree.symbol } + super.transform(tree)(using symbolsInCurrentLevel(defSyms)) case CaseDef(pat, guard, body) => - val last = enteredSyms - tpd.patVars(pat).foreach(markSymbol) - mapOverTree(last) + super.transform(tree)(using symbolsInCurrentLevel(tpd.patVars(pat))) case (_:Import | _:Export) => tree case _: Template => - val last = enteredSyms - tree.symbol.owner.info.decls.foreach(markSymbol) - mapOverTree(last) + val decls = tree.symbol.owner.info.decls.toList + super.transform(tree)(using symbolsInCurrentLevel(decls)) + + case LambdaTypeTree(tparams, body) => + super.transform(tree)(using symbolsInCurrentLevel(tparams.map(_.symbol))) + + case tree: DefTree => + val paramSyms = tree match + case tree: DefDef => tree.paramss.flatten.map(_.symbol) + case _ => Nil + super.transform(tree)(using symbolsInCurrentLevel(tree.symbol :: paramSyms)) case _ => - markDef(tree) - mapOverTree(enteredSyms) + super.transform(tree) } } } From 6d351f985f0a53e97779c06d62bf852bea9e930f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 11:43:25 +0100 Subject: [PATCH 259/657] Move PCPCheckAndHeal to dotty.tools.dotc.staging --- compiler/src/dotty/tools/dotc/core/StagingContext.scala | 2 +- compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala | 3 ++- .../tools/dotc/{transform => staging}/PCPCheckAndHeal.scala | 3 ++- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 1 + compiler/src/dotty/tools/dotc/transform/Staging.scala | 3 +-- 5 files changed, 7 insertions(+), 5 deletions(-) rename compiler/src/dotty/tools/dotc/{transform => staging}/PCPCheckAndHeal.scala (99%) diff --git a/compiler/src/dotty/tools/dotc/core/StagingContext.scala b/compiler/src/dotty/tools/dotc/core/StagingContext.scala index 41e77655d5d6..f6d81582dc97 100644 --- a/compiler/src/dotty/tools/dotc/core/StagingContext.scala +++ b/compiler/src/dotty/tools/dotc/core/StagingContext.scala @@ -4,7 +4,7 @@ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.util.Property -import dotty.tools.dotc.transform.PCPCheckAndHeal +import dotty.tools.dotc.staging.PCPCheckAndHeal object StagingContext { diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 837f9a60d6ea..619e36d942b3 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -17,7 +17,8 @@ import NameKinds.{InlineAccessorName, UniqueInlineName} import inlines.Inlines import NameOps._ import Annotations._ -import transform.{AccessProxies, PCPCheckAndHeal, Splicer} +import transform.{AccessProxies, Splicer} +import staging.PCPCheckAndHeal import transform.SymUtils.* import config.Printers.inlining import util.Property diff --git a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala similarity index 99% rename from compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala rename to compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala index 1f6621558a45..4768113a0f90 100644 --- a/compiler/src/dotty/tools/dotc/transform/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala @@ -1,5 +1,5 @@ package dotty.tools.dotc -package transform +package staging import dotty.tools.dotc.ast.{tpd, untpd} import dotty.tools.dotc.core.Annotations.BodyAnnotation @@ -14,6 +14,7 @@ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.util.Spans._ import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.transform.TreeMapWithStages import dotty.tools.dotc.typer.Checking import dotty.tools.dotc.typer.Implicits.SearchFailureType import dotty.tools.dotc.core.Annotations._ diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index d31b92fc17db..22c95d56c688 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -22,6 +22,7 @@ import dotty.tools.dotc.core.Names._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.config.ScalaRelease.* +import dotty.tools.dotc.staging.PCPCheckAndHeal import scala.annotation.constructorOnly diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 2e8964c8b1b4..37640cbb265d 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -12,8 +12,7 @@ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.staging.StagingLevel.* - - +import dotty.tools.dotc.staging.PCPCheckAndHeal /** Checks that the Phase Consistency Principle (PCP) holds and heals types. * From aa73a7051005a0d89b51cc686f79ffd8741b56f4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 11:45:55 +0100 Subject: [PATCH 260/657] Move TreeMapWithStages to dotty.tools.dotc.staging --- compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala | 1 - .../tools/dotc/{transform => staging}/TreeMapWithStages.scala | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) rename compiler/src/dotty/tools/dotc/{transform => staging}/TreeMapWithStages.scala (99%) diff --git a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala index 4768113a0f90..9ae6862945ee 100644 --- a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala @@ -14,7 +14,6 @@ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.util.Spans._ import dotty.tools.dotc.transform.SymUtils._ -import dotty.tools.dotc.transform.TreeMapWithStages import dotty.tools.dotc.typer.Checking import dotty.tools.dotc.typer.Implicits.SearchFailureType import dotty.tools.dotc.core.Annotations._ diff --git a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala similarity index 99% rename from compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala rename to compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index a4535a639c13..49c5b4c12747 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -1,5 +1,5 @@ package dotty.tools.dotc -package transform +package staging import dotty.tools.dotc.ast.{TreeMapWithImplicits, tpd} import dotty.tools.dotc.config.Printers.staging From bd5e3b0fec03aa094ea7c084596c8ca879a3a917 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 11:57:09 +0100 Subject: [PATCH 261/657] Remove Checking from PCPCheckAndHeal --- compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala index 9ae6862945ee..ab78e078c194 100644 --- a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala @@ -47,7 +47,7 @@ import dotty.tools.dotc.util.Property * } * */ -class PCPCheckAndHeal extends TreeMapWithStages with Checking { +class PCPCheckAndHeal extends TreeMapWithStages { import tpd._ private val InAnnotation = Property.Key[Unit]() @@ -256,7 +256,7 @@ class PCPCheckAndHeal extends TreeMapWithStages with Checking { tag.tpe match case tp: TermRef => - checkStable(tp, pos, "type witness") + ctx.typer.checkStable(tp, pos, "type witness") getQuoteTypeTags.getTagRef(tp) case _: SearchFailureType => report.error( From f32e204a768fbe39ee1f51c67c316b3e6ba4d6fa Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 13:22:50 +0100 Subject: [PATCH 262/657] Split annotation splice transformation from type healing --- .../tools/dotc/staging/PCPCheckAndHeal.scala | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala index ab78e078c194..43cd44869a25 100644 --- a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala @@ -59,8 +59,12 @@ class PCPCheckAndHeal extends TreeMapWithStages { checkAnnotations(tree) super.transform(tree) else tree match { - - case _: TypeTree | _: RefTree if tree.isType => + case _: TypeTree => + val tp1 = transformTypeAnnotationSplices(tree.tpe) + val healedType = healType(tree.srcPos)(tp1) + if healedType == tree.tpe then tree + else TypeTree(healedType).withSpan(tree.span) + case _: RefTree if tree.isType => val healedType = healType(tree.srcPos)(tree.tpe) if healedType == tree.tpe then tree else TypeTree(healedType).withSpan(tree.span) @@ -163,6 +167,15 @@ class PCPCheckAndHeal extends TreeMapWithStages { ref(tagRef).withSpan(splice.span) } + def transformTypeAnnotationSplices(tp: Type)(using Context) = new TypeMap { + def apply(tp: Type): Type = tp match + case tp: AnnotatedType => + val newAnnotTree = transform(tp.annot.tree) + derivedAnnotatedType(tp, apply(tp.parent), tp.annot.derivedAnnotation(newAnnotTree)) + case _ => + mapOver(tp) + }.apply(tp) + /** Check that annotations do not contain quotes and and that splices are valid */ private def checkAnnotations(tree: Tree)(using Context): Unit = tree match @@ -198,8 +211,7 @@ class PCPCheckAndHeal extends TreeMapWithStages { case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) => levelError(tp.symbol, tp, pos) case tp: AnnotatedType => - val newAnnotTree = transform(tp.annot.tree) - derivedAnnotatedType(tp, apply(tp.parent), tp.annot.derivedAnnotation(newAnnotTree)) + derivedAnnotatedType(tp, apply(tp.parent), tp.annot) case _ => mapOver(tp) From 247b2c709b5e35acece0fe2a554373f1d3db0f0e Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 13 Mar 2023 15:11:53 +0100 Subject: [PATCH 263/657] Fix WUnused with indents in derived code --- .../tools/dotc/transform/CheckUnused.scala | 14 +++++++------- .../fatal-warnings/i15503i.scala | 19 ++++++++++++++++++- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index e7e6e1c4952c..c0ea483efea9 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -15,19 +15,14 @@ import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo import dotty.tools.dotc.util.{Property, SrcPos} import dotty.tools.dotc.core.Mode -import dotty.tools.dotc.core.Types.TypeTraverser -import dotty.tools.dotc.core.Types.Type -import dotty.tools.dotc.core.Types.AnnotatedType +import dotty.tools.dotc.core.Types.{AnnotatedType, ConstantType, NoType, TermRef, Type, TypeTraverser} import dotty.tools.dotc.core.Flags.flagsString import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.transform.MegaPhase.MiniPhase import dotty.tools.dotc.core.Annotations import dotty.tools.dotc.core.Definitions -import dotty.tools.dotc.core.Types.ConstantType import dotty.tools.dotc.core.NameKinds.WildcardParamName -import dotty.tools.dotc.core.Types.TermRef -import dotty.tools.dotc.core.Types.NameFilter import dotty.tools.dotc.core.Symbols.Symbol @@ -82,6 +77,12 @@ class CheckUnused extends MiniPhase: override def prepareForIdent(tree: tpd.Ident)(using Context): Context = if tree.symbol.exists then + val prefixes = LazyList.iterate(tree.typeOpt.normalizedPrefix)(_.normalizedPrefix).takeWhile(_ != NoType) + for { + prefix <- prefixes + } { + unusedDataApply(_.registerUsed(prefix.classSymbol, None)) + } unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) @@ -409,7 +410,6 @@ object CheckUnused: val kept = used.filterNot { t => val (sym, isAccessible, optName) = t // keep the symbol for outer scope, if it matches **no** import - // This is the first matching wildcard selector var selWildCard: Option[ImportSelector] = None diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index ab83e1dafb3b..9f8416146af4 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -207,4 +207,21 @@ package foo.test.i16925: for { i <- 1 to 2 if true _ = println(i) // OK - } yield () \ No newline at end of file + } yield () + +package foo.test.i16679: + object myPackage: + trait CaseClassName[A]: + def name: String + object CaseClassName: + trait CaseClassByStringName[A] extends CaseClassName[A] + import scala.deriving.Mirror + object CaseClassByStringName: + inline final def derived[A](using inline A: Mirror.Of[A]): CaseClassByStringName[A] = + new CaseClassByStringName[A]: + def name: String = A.toString + + object secondPackage: + import myPackage.CaseClassName // OK + case class CoolClass(i: Int) derives CaseClassName.CaseClassByStringName + println(summon[CaseClassName[CoolClass]].name) From d9c7f2915c9dd764e214d5f960b3a38c1484c1fb Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 13 Mar 2023 15:35:00 +0100 Subject: [PATCH 264/657] Remove the duplicate parameter when generating the scaladoc. --- .../src/dotty/tools/scaladoc/renderers/MemberRenderer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index 7133fe6d657f..47f6bb4b9728 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -414,7 +414,7 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext val argsSig = SignatureBuilder() .functionTermParameters(on.argsLists) .content - val sig = typeSig ++ Signature(Plain(s"(${on.name}: ")) ++ on.signature ++ Signature(Plain(")")) ++ argsSig + val sig = typeSig ++ argsSig MGroup(span(cls := "groupHeader")(sig.map(renderElement(_))), members.sortBy(_.name).toSeq, on.name) -> on.position }.toSeq.sortBy(_._2).map(_._1) From 7f5aa62c36d8fa7e6d5ad6531242bf4ae08225ec Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 13 Mar 2023 15:38:12 +0100 Subject: [PATCH 265/657] Add failsafe for a case where prefixes in CheckUnused/prepareIndent formed an infinite cycle --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index c0ea483efea9..66b0876668be 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -78,6 +78,7 @@ class CheckUnused extends MiniPhase: override def prepareForIdent(tree: tpd.Ident)(using Context): Context = if tree.symbol.exists then val prefixes = LazyList.iterate(tree.typeOpt.normalizedPrefix)(_.normalizedPrefix).takeWhile(_ != NoType) + .take(10) // Failsafe for the odd case if there was an infinite cycle for { prefix <- prefixes } { From 790051e6d4cca04c22cf6ab6d640f0459ad3dbb0 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 13 Mar 2023 15:39:19 +0100 Subject: [PATCH 266/657] Fix isJavaOverride test Fixes #17066 --- compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala | 3 ++- tests/pos/i17066/Bar.scala | 2 ++ tests/pos/i17066/Foo.java | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i17066/Bar.scala create mode 100644 tests/pos/i17066/Foo.java diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index 78baec70bee6..359b882ef26b 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -87,7 +87,8 @@ class ElimRepeated extends MiniPhase with InfoTransformer { thisPhase => * signatures of a Java varargs method and a Scala varargs override are not the same. */ private def overridesJava(sym: Symbol)(using Context) = - sym.owner.info.baseClasses.drop(1).exists { bc => + sym.memberCanMatchInheritedSymbols + && sym.owner.info.baseClasses.drop(1).exists { bc => bc.is(JavaDefined) && { val other = bc.info.nonPrivateDecl(sym.name) other.hasAltWith { alt => diff --git a/tests/pos/i17066/Bar.scala b/tests/pos/i17066/Bar.scala new file mode 100644 index 000000000000..7d3e67f85387 --- /dev/null +++ b/tests/pos/i17066/Bar.scala @@ -0,0 +1,2 @@ +class Bar extends Foo: + def this(xs: String*) = this() diff --git a/tests/pos/i17066/Foo.java b/tests/pos/i17066/Foo.java new file mode 100644 index 000000000000..cb4335c7d444 --- /dev/null +++ b/tests/pos/i17066/Foo.java @@ -0,0 +1,3 @@ +public class Foo { + public Foo(String... xs) { } +} From 915c44c1f36af0594c86186f9aee5aa2a09edb09 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 20:59:10 +0100 Subject: [PATCH 267/657] Extract HealType from PCPCheckAndHeal --- .../dotty/tools/dotc/staging/HealType.scala | 98 +++++++++++++++++++ .../tools/dotc/staging/PCPCheckAndHeal.scala | 77 +-------------- .../tools/dotc/staging/StagingLevel.scala | 12 ++- .../dotty/tools/dotc/transform/Staging.scala | 27 ++--- 4 files changed, 125 insertions(+), 89 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/staging/HealType.scala diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala new file mode 100644 index 000000000000..ad266e257d7e --- /dev/null +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -0,0 +1,98 @@ +package dotty.tools.dotc +package staging + +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.StagingContext._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.staging.StagingLevel.* +import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.typer.Implicits.SearchFailureType +import dotty.tools.dotc.util.SrcPos + +class HealType(pos: SrcPos)(using Context) extends TypeMap { + + /** If the type refers to a locally defined symbol (either directly, or in a pickled type), + * check that its staging level matches the current level. + * - Static types and term are allowed at any level. + * - If a type reference is used a higher level, then it is inconsistent. + * Will attempt to heal before failing. + * - If a term reference is used a higher level, then it is inconsistent. + * It cannot be healed because the term will not exist in any future stage. + * + * If `T` is a reference to a type at the wrong level, try to heal it by replacing it with + * a type tag of type `quoted.Type[T]`. + * The tag is generated by an instance of `QuoteTypeTags` directly if the splice is explicit + * or indirectly by `tryHeal`. + */ + def apply(tp: Type): Type = + tp match + case tp: TypeRef => + healTypeRef(tp) + case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) => + levelError(tp.symbol, tp, pos) + case tp: AnnotatedType => + derivedAnnotatedType(tp, apply(tp.parent), tp.annot) + case _ => + mapOver(tp) + + private def healTypeRef(tp: TypeRef): Type = + tp.prefix match + case prefix: TermRef if tp.symbol.isTypeSplice => + checkNotWildcardSplice(tp) + if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix) + case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) => + dealiasAndTryHeal(prefix.symbol, tp, pos) + case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => + dealiasAndTryHeal(tp.symbol, tp, pos) + case prefix: ThisType if level > levelOf(prefix.cls) && !tp.symbol.isStatic => + dealiasAndTryHeal(tp.symbol, tp, pos) + case _ => + mapOver(tp) + + private def checkNotWildcardSplice(splice: TypeRef): Unit = + splice.prefix.termSymbol.info.argInfos match + case (tb: TypeBounds) :: _ => report.error(em"Cannot splice $splice because it is a wildcard type", pos) + case _ => + + private def dealiasAndTryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): Type = + val tp1 = tp.dealias + if tp1 != tp then apply(tp1) + else tryHeal(tp.symbol, tp, pos) + + /** Try to heal reference to type `T` used in a higher level than its definition. + * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a + * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. + * Emits and error if `T` cannot be healed and returns `T`. + */ + protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = { + val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp) + val tag = ctx.typer.inferImplicitArg(reqType, pos.span) + tag.tpe match + case tp: TermRef => + ctx.typer.checkStable(tp, pos, "type witness") + getQuoteTypeTags.getTagRef(tp) + case _: SearchFailureType => + report.error( + ctx.typer.missingArgMsg(tag, reqType, "") + .prepend(i"Reference to $tp within quotes requires a given $reqType in scope.\n") + .append("\n"), + pos) + tp + case _ => + report.error(em"""Reference to $tp within quotes requires a given $reqType in scope. + | + |""", pos) + tp + } + + private def levelError(sym: Symbol, tp: Type, pos: SrcPos): tp.type = { + report.error( + em"""access to $sym from wrong staging level: + | - the definition is at level ${levelOf(sym)}, + | - but the access is at level $level""", pos) + tp + } +} diff --git a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala index 43cd44869a25..59039278ffe1 100644 --- a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc package staging import dotty.tools.dotc.ast.{tpd, untpd} -import dotty.tools.dotc.core.Annotations.BodyAnnotation +import dotty.tools.dotc.core.Annotations._ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ @@ -11,15 +11,10 @@ import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.util.SrcPos -import dotty.tools.dotc.util.Spans._ -import dotty.tools.dotc.transform.SymUtils._ -import dotty.tools.dotc.typer.Checking -import dotty.tools.dotc.typer.Implicits.SearchFailureType -import dotty.tools.dotc.core.Annotations._ import dotty.tools.dotc.staging.StagingLevel.* - import dotty.tools.dotc.util.Property +import dotty.tools.dotc.util.Spans._ +import dotty.tools.dotc.util.SrcPos /** Checks that the Phase Consistency Principle (PCP) holds and heals types. * @@ -203,42 +198,8 @@ class PCPCheckAndHeal extends TreeMapWithStages { * The tag is generated by an instance of `QuoteTypeTags` directly if the splice is explicit * or indirectly by `tryHeal`. */ - private def healType(pos: SrcPos)(using Context) = new TypeMap { - def apply(tp: Type): Type = - tp match - case tp: TypeRef => - healTypeRef(tp) - case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) => - levelError(tp.symbol, tp, pos) - case tp: AnnotatedType => - derivedAnnotatedType(tp, apply(tp.parent), tp.annot) - case _ => - mapOver(tp) - - private def healTypeRef(tp: TypeRef): Type = - tp.prefix match - case prefix: TermRef if tp.symbol.isTypeSplice => - checkNotWildcardSplice(tp) - if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix) - case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) => - dealiasAndTryHeal(prefix.symbol, tp, pos) - case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => - dealiasAndTryHeal(tp.symbol, tp, pos) - case prefix: ThisType if level > levelOf(prefix.cls) && !tp.symbol.isStatic => - dealiasAndTryHeal(tp.symbol, tp, pos) - case _ => - mapOver(tp) - - private def dealiasAndTryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): Type = - val tp1 = tp.dealias - if tp1 != tp then apply(tp1) - else tryHeal(tp.symbol, tp, pos) - - private def checkNotWildcardSplice(splice: TypeRef)(using Context): Unit = - splice.prefix.termSymbol.info.argInfos match - case (tb: TypeBounds) :: _ => report.error(em"Cannot splice $splice because it is a wildcard type", pos) - case _ => - } + protected def healType(pos: SrcPos)(using Context) = + new HealType(pos) /** Check level consistency of terms references */ private def checkLevelConsistency(tree: Ident | This)(using Context): Unit = @@ -257,33 +218,6 @@ class PCPCheckAndHeal extends TreeMapWithStages { traverseChildren(tp) }.traverse(tree.tpe) - /** Try to heal reference to type `T` used in a higher level than its definition. - * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a - * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. - * Emits and error if `T` cannot be healed and returns `T`. - */ - protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = { - val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp) - val tag = ctx.typer.inferImplicitArg(reqType, pos.span) - tag.tpe match - - case tp: TermRef => - ctx.typer.checkStable(tp, pos, "type witness") - getQuoteTypeTags.getTagRef(tp) - case _: SearchFailureType => - report.error( - ctx.typer.missingArgMsg(tag, reqType, "") - .prepend(i"Reference to $tp within quotes requires a given $reqType in scope.\n") - .append("\n"), - pos) - tp - case _ => - report.error(em"""Reference to $tp within quotes requires a given $reqType in scope. - | - |""", pos) - tp - } - private def levelError(sym: Symbol, tp: Type, pos: SrcPos)(using Context): tp.type = { def symStr = if (!tp.isInstanceOf[ThisType]) sym.show @@ -301,7 +235,6 @@ class PCPCheckAndHeal extends TreeMapWithStages { | - but the access is at level $level.$hint""", pos) tp } - } object PCPCheckAndHeal { diff --git a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala index 4cf57d28ed8c..8067f5c9aa18 100644 --- a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala +++ b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala @@ -1,12 +1,14 @@ -package dotty.tools.dotc.staging +package dotty.tools.dotc +package staging -import dotty.tools.dotc.ast.{TreeMapWithImplicits, tpd} -import dotty.tools.dotc.config.Printers.staging -import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.util.Property +import dotty.tools.dotc.util.SrcPos import scala.collection.mutable @@ -29,4 +31,4 @@ object StagingLevel { val syms1 = syms//.filter(sym => !levelOfMap.contains(sym)) val newMap = syms1.foldLeft(levelOfMap)((acc, sym) => acc.updated(sym, level)) ctx.fresh.setProperty(LevelOfKey, newMap) -} \ No newline at end of file +} diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 37640cbb265d..f8e2c86169f2 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -13,6 +13,7 @@ import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.PCPCheckAndHeal +import dotty.tools.dotc.staging.HealType /** Checks that the Phase Consistency Principle (PCP) holds and heals types. * @@ -35,19 +36,21 @@ class Staging extends MacroTransform { tree match { case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass => val checker = new PCPCheckAndHeal { - override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = { - def symStr = - if (sym.is(ModuleClass)) sym.sourceModule.show - else i"${sym.name}.this" - val errMsg = s"\nin ${ctx.owner.fullName}" - assert( - ctx.owner.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) || - (sym.isType && levelOf(sym) > 0), - em"""access to $symStr from wrong staging level: - | - the definition is at level ${levelOf(sym)}, - | - but the access is at level $level.$errMsg""") + override protected def healType(pos: SrcPos)(using Context) = new HealType(pos) { + override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = { + def symStr = + if (sym.is(ModuleClass)) sym.sourceModule.show + else i"${sym.name}.this" + val errMsg = s"\nin ${ctx.owner.fullName}" + assert( + ctx.owner.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) || + (sym.isType && levelOf(sym) > 0), + em"""access to $symStr from wrong staging level: + | - the definition is at level ${levelOf(sym)}, + | - but the access is at level $level.$errMsg""") - tp + tp + } } } checker.transform(tree) From 540277bdb16dfcc892d913af7d7b25c731ed2493 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 21:15:50 +0100 Subject: [PATCH 268/657] Move level tracking into StagingLevel --- .../tools/dotc/core/StagingContext.scala | 21 +++---------------- .../dotty/tools/dotc/inlines/Inliner.scala | 5 +++-- .../dotty/tools/dotc/inlines/Inlines.scala | 3 ++- .../dotc/inlines/PrepareInlineable.scala | 7 ++++--- .../tools/dotc/staging/StagingLevel.scala | 15 +++++++++++++ .../dotty/tools/dotc/transform/Inlining.scala | 13 ++++++------ .../dotty/tools/dotc/transform/Splicing.scala | 1 + .../tools/dotc/typer/QuotesAndSplices.scala | 5 +++-- .../src/dotty/tools/dotc/typer/Typer.scala | 3 ++- 9 files changed, 40 insertions(+), 33 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StagingContext.scala b/compiler/src/dotty/tools/dotc/core/StagingContext.scala index f6d81582dc97..6fb8a190921f 100644 --- a/compiler/src/dotty/tools/dotc/core/StagingContext.scala +++ b/compiler/src/dotty/tools/dotc/core/StagingContext.scala @@ -5,12 +5,10 @@ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.util.Property import dotty.tools.dotc.staging.PCPCheckAndHeal +import dotty.tools.dotc.staging.StagingLevel.* object StagingContext { - /** A key to be used in a context property that tracks the quotation level */ - private val QuotationLevel = new Property.Key[Int] - /** A key to be used in a context property that tracks the quotation stack. * Stack containing the Quotes references received by the surrounding quotes. */ @@ -18,23 +16,10 @@ object StagingContext { private val TaggedTypes = new Property.Key[PCPCheckAndHeal.QuoteTypeTags] - /** All enclosing calls that are currently inlined, from innermost to outermost. */ - def level(using Context): Int = - ctx.property(QuotationLevel).getOrElse(0) - - /** Context with an incremented quotation level. */ - def quoteContext(using Context): Context = - ctx.fresh.setProperty(QuotationLevel, level + 1) - /** Context with an incremented quotation level and pushes a reference to a Quotes on the quote context stack */ def pushQuotes(qctxRef: tpd.Tree)(using Context): Context = val old = ctx.property(QuotesStack).getOrElse(List.empty) - ctx.fresh.setProperty(QuotationLevel, level + 1) - .setProperty(QuotesStack, qctxRef :: old) - - /** Context with a decremented quotation level. */ - def spliceContext(using Context): Context = - ctx.fresh.setProperty(QuotationLevel, level - 1) + quoteContext.setProperty(QuotesStack, qctxRef :: old) def contextWithQuoteTypeTags(taggedTypes: PCPCheckAndHeal.QuoteTypeTags)(using Context) = ctx.fresh.setProperty(TaggedTypes, taggedTypes) @@ -46,7 +31,7 @@ object StagingContext { * The quotation stack could be empty if we are in a top level splice or an erroneous splice directly within a top level splice. */ def popQuotes()(using Context): (Option[tpd.Tree], Context) = - val ctx1 = ctx.fresh.setProperty(QuotationLevel, level - 1) + val ctx1 = spliceContext val head = ctx.property(QuotesStack) match case Some(x :: xs) => diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index f2a0d5f03e38..872dc7793ff4 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -23,6 +23,7 @@ import util.Spans.Span import dotty.tools.dotc.transform.Splicer import dotty.tools.dotc.transform.BetaReduce import quoted.QuoteUtils +import staging.StagingLevel import scala.annotation.constructorOnly /** General support for inlining */ @@ -814,7 +815,7 @@ class Inliner(val call: tpd.Tree)(using Context): val locked = ctx.typerState.ownedVars val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match { case res: Apply if res.symbol == defn.QuotedRuntime_exprSplice - && StagingContext.level == 0 + && StagingLevel.level == 0 && !hasInliningErrors => val expanded = expandMacro(res.args.head, tree.srcPos) transform.TreeChecker.checkMacroGeneratedTree(res, expanded) @@ -1026,7 +1027,7 @@ class Inliner(val call: tpd.Tree)(using Context): } private def expandMacro(body: Tree, splicePos: SrcPos)(using Context) = { - assert(StagingContext.level == 0) + assert(StagingLevel.level == 0) val inlinedFrom = enclosingInlineds.last val dependencies = macroDependencies(body) val suspendable = ctx.compilationUnit.isSuspendable diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index 8110fd2de195..f504a4034631 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -14,6 +14,7 @@ import ErrorReporting.errorTree import dotty.tools.dotc.util.{SourceFile, SourcePosition, SrcPos} import parsing.Parsers.Parser import transform.{PostTyper, Inlining, CrossVersionChecks} +import staging.StagingLevel import collection.mutable import reporting.trace @@ -56,7 +57,7 @@ object Inlines: case _ => isInlineable(tree.symbol) && !tree.tpe.widenTermRefExpr.isInstanceOf[MethodOrPoly] - && StagingContext.level == 0 + && StagingLevel.level == 0 && ( ctx.phase == Phases.inliningPhase || (ctx.phase == Phases.typerPhase && needsTransparentInlining(tree)) diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 619e36d942b3..eb1a97ab93c0 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -22,6 +22,7 @@ import staging.PCPCheckAndHeal import transform.SymUtils.* import config.Printers.inlining import util.Property +import staging.StagingLevel object PrepareInlineable { import tpd._ @@ -73,7 +74,7 @@ object PrepareInlineable { !sym.isContainedIn(inlineSym) && !(sym.isStableMember && sym.info.widenTermRefExpr.isInstanceOf[ConstantType]) && !sym.isInlineMethod && - (Inlines.inInlineMethod || StagingContext.level > 0) + (Inlines.inInlineMethod || StagingLevel.level > 0) def preTransform(tree: Tree)(using Context): Tree @@ -90,8 +91,8 @@ object PrepareInlineable { } private def stagingContext(tree: Tree)(using Context): Context = tree match - case tree: Apply if tree.symbol.isQuote => StagingContext.quoteContext - case tree: Apply if tree.symbol.isExprSplice => StagingContext.spliceContext + case tree: Apply if tree.symbol.isQuote => StagingLevel.quoteContext + case tree: Apply if tree.symbol.isExprSplice => StagingLevel.spliceContext case _ => ctx } diff --git a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala index 8067f5c9aa18..f29019a14e47 100644 --- a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala +++ b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala @@ -14,9 +14,24 @@ import scala.collection.mutable object StagingLevel { + /** A key to be used in a context property that tracks the staging level */ + private val LevelKey = new Property.Key[Int] + /** A key to be used in a context property that caches the `levelOf` mapping */ private val LevelOfKey = new Property.Key[Map[Symbol, Int]] + /** All enclosing calls that are currently inlined, from innermost to outermost. */ + def level(using Context): Int = + ctx.property(LevelKey).getOrElse(0) + + /** Context with an incremented staging level. */ + def quoteContext(using Context): FreshContext = + ctx.fresh.setProperty(LevelKey, level + 1) + + /** Context with a decremented staging level. */ + def spliceContext(using Context): FreshContext = + ctx.fresh.setProperty(LevelKey, level - 1) + /** The quotation level of the definition of the locally defined symbol */ def levelOf(sym: Symbol)(using Context): Int = ctx.property(LevelOfKey) match diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index f0ed7026ee91..ed85746a78b5 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -13,6 +13,7 @@ import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.inlines.Inlines import dotty.tools.dotc.ast.TreeMapWithImplicits import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer +import dotty.tools.dotc.staging.StagingLevel import scala.collection.mutable.ListBuffer @@ -46,10 +47,10 @@ class Inlining extends MacroTransform { def traverse(tree: Tree)(using Context): Unit = tree match case _: GenericApply if tree.symbol.isQuote => - traverseChildren(tree)(using StagingContext.quoteContext) + traverseChildren(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol.isExprSplice => - traverseChildren(tree)(using StagingContext.spliceContext) - case tree: RefTree if !Inlines.inInlineMethod && StagingContext.level == 0 => + traverseChildren(tree)(using StagingLevel.spliceContext) + case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) case _ => traverseChildren(tree) @@ -76,7 +77,7 @@ class Inlining extends MacroTransform { else if tree.symbol.is(Param) then super.transform(tree) else if !tree.symbol.isPrimaryConstructor - && StagingContext.level == 0 + && StagingLevel.level == 0 && MacroAnnotations.hasMacroAnnotation(tree.symbol) then val trees = (new MacroAnnotations).expandAnnotations(tree) @@ -98,9 +99,9 @@ class Inlining extends MacroTransform { if tree1.tpe.isError then tree1 else Inlines.inlineCall(tree1) case _: GenericApply if tree.symbol.isQuote => - super.transform(tree)(using StagingContext.quoteContext) + super.transform(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol.isExprSplice => - super.transform(tree)(using StagingContext.spliceContext) + super.transform(tree)(using StagingLevel.spliceContext) case _: PackageDef => super.transform(tree) match case tree1: PackageDef => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 22c95d56c688..39cd059a78ab 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -23,6 +23,7 @@ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.config.ScalaRelease.* import dotty.tools.dotc.staging.PCPCheckAndHeal +import dotty.tools.dotc.staging.StagingLevel.* import scala.annotation.constructorOnly diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 65d8abfdf6a7..75874d1d1060 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -11,11 +11,12 @@ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.NameKinds.PatMatGivenVarName import dotty.tools.dotc.core.Names._ -import dotty.tools.dotc.core.StagingContext._ +import dotty.tools.dotc.core.StagingContext.* import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.inlines.PrepareInlineable +import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.typer.Implicits._ import dotty.tools.dotc.typer.Inferencing._ @@ -91,7 +92,7 @@ trait QuotesAndSplices { tree.withType(UnspecifiedErrorType) } else { - if (StagingContext.level == 0) { + if (level == 0) { // Mark the first inline method from the context as a macro def markAsMacro(c: Context): Unit = if (c.owner eq c.outer.owner) markAsMacro(c.outer) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 96149caf215d..8e8c8b2da1ca 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -44,6 +44,7 @@ import config.Feature import config.Feature.{sourceVersion, migrateTo3} import config.SourceVersion._ import rewrites.Rewrites.patch +import staging.StagingLevel import transform.SymUtils._ import transform.TypeUtils._ import reporting._ @@ -2410,7 +2411,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else typedExpr(ddef.rhs, tpt1.tpe.widenExpr)(using rhsCtx)) if sym.isInlineMethod then - if StagingContext.level > 0 then + if StagingLevel.level > 0 then report.error("inline def cannot be within quotes", sym.sourcePos) if sym.is(Given) && untpd.stripBlock(untpd.unsplice(ddef.rhs)).isInstanceOf[untpd.Function] From 9cbc14c99905b7127bdff657433c5e18de8b6cf8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 21:20:38 +0100 Subject: [PATCH 269/657] Move StagingContext to dotty.tools.dotc.staging --- compiler/src/dotty/tools/dotc/quoted/Interpreter.scala | 2 +- compiler/src/dotty/tools/dotc/staging/HealType.scala | 2 +- compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala | 2 +- .../src/dotty/tools/dotc/{core => staging}/StagingContext.scala | 2 +- compiler/src/dotty/tools/dotc/staging/StagingLevel.scala | 1 - compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala | 1 - compiler/src/dotty/tools/dotc/transform/Inlining.scala | 2 +- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 2 +- compiler/src/dotty/tools/dotc/transform/Staging.scala | 2 +- compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 2 +- 10 files changed, 8 insertions(+), 10 deletions(-) rename compiler/src/dotty/tools/dotc/{core => staging}/StagingContext.scala (98%) diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index 7153c17e30ed..0252e042441b 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -19,12 +19,12 @@ import dotty.tools.dotc.core.Denotations.staticRef import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.NameKinds.FlatName import dotty.tools.dotc.core.Names._ -import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.TypeErasure import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.quoted._ +import dotty.tools.dotc.staging.StagingContext.* import dotty.tools.dotc.typer.ImportInfo.withRootImports import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.reporting.Message diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index ad266e257d7e..f62f20081b3a 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -4,9 +4,9 @@ package staging import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ -import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.staging.StagingContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.typer.Implicits.SearchFailureType diff --git a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala index 59039278ffe1..194a5414a72c 100644 --- a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala @@ -7,10 +7,10 @@ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.NameKinds._ -import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.staging.StagingContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.util.Property import dotty.tools.dotc.util.Spans._ diff --git a/compiler/src/dotty/tools/dotc/core/StagingContext.scala b/compiler/src/dotty/tools/dotc/staging/StagingContext.scala similarity index 98% rename from compiler/src/dotty/tools/dotc/core/StagingContext.scala rename to compiler/src/dotty/tools/dotc/staging/StagingContext.scala index 6fb8a190921f..37e25862206c 100644 --- a/compiler/src/dotty/tools/dotc/core/StagingContext.scala +++ b/compiler/src/dotty/tools/dotc/staging/StagingContext.scala @@ -1,4 +1,4 @@ -package dotty.tools.dotc.core +package dotty.tools.dotc.staging import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts._ diff --git a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala index f29019a14e47..4704501e38ff 100644 --- a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala +++ b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala @@ -4,7 +4,6 @@ package staging import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ -import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.util.Property diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 49c5b4c12747..adaacafa764a 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -5,7 +5,6 @@ import dotty.tools.dotc.ast.{TreeMapWithImplicits, tpd} import dotty.tools.dotc.config.Printers.staging import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Contexts._ -import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.util.Property import dotty.tools.dotc.staging.StagingLevel.* diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index ed85746a78b5..1967a0298504 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -9,10 +9,10 @@ import SymUtils._ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.quoted._ -import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.inlines.Inlines import dotty.tools.dotc.ast.TreeMapWithImplicits import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer +import dotty.tools.dotc.staging.StagingContext.* import dotty.tools.dotc.staging.StagingLevel import scala.collection.mutable.ListBuffer diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 39cd059a78ab..0105e6c55253 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -14,7 +14,6 @@ import util.Spans._ import SymUtils._ import NameKinds._ import dotty.tools.dotc.ast.tpd -import StagingContext._ import scala.collection.mutable import dotty.tools.dotc.core.Annotations._ @@ -23,6 +22,7 @@ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.config.ScalaRelease.* import dotty.tools.dotc.staging.PCPCheckAndHeal +import dotty.tools.dotc.staging.StagingContext.* import dotty.tools.dotc.staging.StagingLevel.* import scala.annotation.constructorOnly diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index f8e2c86169f2..b0c3b8086971 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -6,11 +6,11 @@ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Phases._ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ -import dotty.tools.dotc.core.StagingContext._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.staging.StagingContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.PCPCheckAndHeal import dotty.tools.dotc.staging.HealType diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 75874d1d1060..e1f91a46f6bc 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -11,11 +11,11 @@ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.NameKinds.PatMatGivenVarName import dotty.tools.dotc.core.Names._ -import dotty.tools.dotc.core.StagingContext.* import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.inlines.PrepareInlineable +import dotty.tools.dotc.staging.StagingContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.typer.Implicits._ From 6d02874b5f6af07728fa377ae97746be4f084e18 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 21:32:06 +0100 Subject: [PATCH 270/657] Extract QuoteTypeTags --- .../dotty/tools/dotc/staging/HealType.scala | 1 + .../tools/dotc/staging/PCPCheckAndHeal.scala | 35 +------------ .../tools/dotc/staging/QuoteTypeTags.scala | 52 +++++++++++++++++++ .../tools/dotc/staging/StagingContext.scala | 8 --- .../dotty/tools/dotc/transform/Splicing.scala | 7 +-- 5 files changed, 59 insertions(+), 44 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index f62f20081b3a..deffa98fef53 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -8,6 +8,7 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.staging.StagingContext.* import dotty.tools.dotc.staging.StagingLevel.* +import dotty.tools.dotc.staging.QuoteTypeTags.* import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.typer.Implicits.SearchFailureType import dotty.tools.dotc.util.SrcPos diff --git a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala index 194a5414a72c..751dfcbf1b68 100644 --- a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala @@ -12,6 +12,7 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.staging.StagingContext.* import dotty.tools.dotc.staging.StagingLevel.* +import dotty.tools.dotc.staging.QuoteTypeTags.* import dotty.tools.dotc.util.Property import dotty.tools.dotc.util.Spans._ import dotty.tools.dotc.util.SrcPos @@ -98,7 +99,7 @@ class PCPCheckAndHeal extends TreeMapWithStages { /** Transform quoted trees while maintaining phase correctness */ override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = { - val taggedTypes = new PCPCheckAndHeal.QuoteTypeTags(quote.span) + val taggedTypes = new QuoteTypeTags(quote.span) if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) @@ -236,35 +237,3 @@ class PCPCheckAndHeal extends TreeMapWithStages { tp } } - -object PCPCheckAndHeal { - import tpd._ - - class QuoteTypeTags(span: Span)(using Context) { - - private val tags = collection.mutable.LinkedHashMap.empty[Symbol, TypeDef] - - def getTagRef(spliced: TermRef): TypeRef = { - val typeDef = tags.getOrElseUpdate(spliced.symbol, mkTagSymbolAndAssignType(spliced)) - typeDef.symbol.typeRef - } - - def getTypeTags: List[TypeDef] = tags.valuesIterator.toList - - private def mkTagSymbolAndAssignType(spliced: TermRef): TypeDef = { - val splicedTree = tpd.ref(spliced).withSpan(span) - val rhs = splicedTree.select(tpnme.Underlying).withSpan(span) - val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs, EmptyTree) - val local = newSymbol( - owner = ctx.owner, - name = UniqueName.fresh((splicedTree.symbol.name.toString + "$_").toTermName).toTypeName, - flags = Synthetic, - info = TypeAlias(splicedTree.tpe.select(tpnme.Underlying)), - coord = span).asType - local.addAnnotation(Annotation(defn.QuotedRuntime_SplicedTypeAnnot, span)) - ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local) - } - - } - -} diff --git a/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala b/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala new file mode 100644 index 000000000000..c4c9c611be3a --- /dev/null +++ b/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala @@ -0,0 +1,52 @@ +package dotty.tools.dotc.staging + +import dotty.tools.dotc.ast.{tpd, untpd} +import dotty.tools.dotc.core.Annotations._ +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.NameKinds._ +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.staging.StagingLevel.* +import dotty.tools.dotc.util.Property +import dotty.tools.dotc.util.Spans._ + +object QuoteTypeTags { + + private val TaggedTypes = new Property.Key[QuoteTypeTags] + + def contextWithQuoteTypeTags(taggedTypes: QuoteTypeTags)(using Context) = + ctx.fresh.setProperty(TaggedTypes, taggedTypes) + + def getQuoteTypeTags(using Context): QuoteTypeTags = + ctx.property(TaggedTypes).get +} + +class QuoteTypeTags(span: Span)(using Context) { + import tpd.* + + private val tags = collection.mutable.LinkedHashMap.empty[Symbol, TypeDef] + + def getTagRef(spliced: TermRef): TypeRef = { + val typeDef = tags.getOrElseUpdate(spliced.symbol, mkTagSymbolAndAssignType(spliced)) + typeDef.symbol.typeRef + } + + def getTypeTags: List[TypeDef] = tags.valuesIterator.toList + + private def mkTagSymbolAndAssignType(spliced: TermRef): TypeDef = { + val splicedTree = tpd.ref(spliced).withSpan(span) + val rhs = splicedTree.select(tpnme.Underlying).withSpan(span) + val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs, EmptyTree) + val local = newSymbol( + owner = ctx.owner, + name = UniqueName.fresh((splicedTree.symbol.name.toString + "$_").toTermName).toTypeName, + flags = Synthetic, + info = TypeAlias(splicedTree.tpe.select(tpnme.Underlying)), + coord = span).asType + local.addAnnotation(Annotation(defn.QuotedRuntime_SplicedTypeAnnot, span)) + ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local) + } +} diff --git a/compiler/src/dotty/tools/dotc/staging/StagingContext.scala b/compiler/src/dotty/tools/dotc/staging/StagingContext.scala index 37e25862206c..839eabeaafeb 100644 --- a/compiler/src/dotty/tools/dotc/staging/StagingContext.scala +++ b/compiler/src/dotty/tools/dotc/staging/StagingContext.scala @@ -14,19 +14,11 @@ object StagingContext { */ private val QuotesStack = new Property.Key[List[tpd.Tree]] - private val TaggedTypes = new Property.Key[PCPCheckAndHeal.QuoteTypeTags] - /** Context with an incremented quotation level and pushes a reference to a Quotes on the quote context stack */ def pushQuotes(qctxRef: tpd.Tree)(using Context): Context = val old = ctx.property(QuotesStack).getOrElse(List.empty) quoteContext.setProperty(QuotesStack, qctxRef :: old) - def contextWithQuoteTypeTags(taggedTypes: PCPCheckAndHeal.QuoteTypeTags)(using Context) = - ctx.fresh.setProperty(TaggedTypes, taggedTypes) - - def getQuoteTypeTags(using Context): PCPCheckAndHeal.QuoteTypeTags = - ctx.property(TaggedTypes).get - /** Context with a decremented quotation level and pops the Some of top of the quote context stack or None if the stack is empty. * The quotation stack could be empty if we are in a top level splice or an erroneous splice directly within a top level splice. */ diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 0105e6c55253..3aec4ceb156c 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -24,6 +24,7 @@ import dotty.tools.dotc.config.ScalaRelease.* import dotty.tools.dotc.staging.PCPCheckAndHeal import dotty.tools.dotc.staging.StagingContext.* import dotty.tools.dotc.staging.StagingLevel.* +import dotty.tools.dotc.staging.QuoteTypeTags import scala.annotation.constructorOnly @@ -191,7 +192,7 @@ class Splicing extends MacroTransform: private var refBindingMap = mutable.Map.empty[Symbol, (Tree, Symbol)] /** Reference to the `Quotes` instance of the current level 1 splice */ private var quotes: Tree | Null = null // TODO: add to the context - private var healedTypes: PCPCheckAndHeal.QuoteTypeTags | Null = null // TODO: add to the context + private var healedTypes: QuoteTypeTags | Null = null // TODO: add to the context def transformSplice(tree: tpd.Tree, tpe: Type, holeIdx: Int)(using Context): tpd.Tree = assert(level == 0) @@ -261,7 +262,7 @@ class Splicing extends MacroTransform: private def transformLevel0QuoteContent(tree: Tree)(using Context): Tree = // transform and collect new healed types val old = healedTypes - healedTypes = new PCPCheckAndHeal.QuoteTypeTags(tree.span) + healedTypes = new QuoteTypeTags(tree.span) val tree1 = transform(tree) val newHealedTypes = healedTypes.nn.getTypeTags healedTypes = old @@ -359,7 +360,7 @@ class Splicing extends MacroTransform: private def capturedPartTypes(tpt: Tree)(using Context): Tree = val old = healedTypes - healedTypes = PCPCheckAndHeal.QuoteTypeTags(tpt.span) + healedTypes = QuoteTypeTags(tpt.span) val capturePartTypes = new TypeMap { def apply(tp: Type) = tp match { case typeRef @ TypeRef(prefix, _) if isCaptured(prefix.typeSymbol) || isCaptured(prefix.termSymbol) => From 3cbc635f861eb2e2d9fe1aa6a3251761b2f55663 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Mar 2023 21:39:37 +0100 Subject: [PATCH 271/657] Rename StagingContext to QuoteContext --- compiler/src/dotty/tools/dotc/quoted/Interpreter.scala | 2 +- compiler/src/dotty/tools/dotc/staging/HealType.scala | 2 +- compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala | 6 +++--- .../staging/{StagingContext.scala => QuoteContext.scala} | 6 +++--- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 2 +- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 2 +- compiler/src/dotty/tools/dotc/transform/Staging.scala | 2 +- compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) rename compiler/src/dotty/tools/dotc/staging/{StagingContext.scala => QuoteContext.scala} (89%) diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index 0252e042441b..a98284f4078d 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -24,7 +24,7 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.TypeErasure import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.quoted._ -import dotty.tools.dotc.staging.StagingContext.* +import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.typer.ImportInfo.withRootImports import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.reporting.Message diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index deffa98fef53..645bde42eb29 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -6,7 +6,7 @@ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.staging.StagingContext.* +import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags.* import dotty.tools.dotc.transform.SymUtils._ diff --git a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala index 751dfcbf1b68..2a1371d8eb0d 100644 --- a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.core.NameKinds._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.staging.StagingContext.* +import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags.* import dotty.tools.dotc.util.Property @@ -146,11 +146,11 @@ class PCPCheckAndHeal extends TreeMapWithStages { // `quoted.runtime.Expr.quote[F[T]](... T ...)` --> `internal.Quoted.expr[F[$t]](... T ...)` val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), body1 :: Nil) - case f @ Apply(fun @ TypeApply(_, _), qctx :: Nil) => + case f @ Apply(fun @ TypeApply(_, _), quotes :: Nil) => // Type of the splice itself must also be healed // `quoted.runtime.Expr.quote[F[T]](... T ...)` --> `internal.Quoted.expr[F[$t]](... T ...)` val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) - cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), qctx :: Nil), body1 :: Nil) + cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), quotes :: Nil), body1 :: Nil) } } diff --git a/compiler/src/dotty/tools/dotc/staging/StagingContext.scala b/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala similarity index 89% rename from compiler/src/dotty/tools/dotc/staging/StagingContext.scala rename to compiler/src/dotty/tools/dotc/staging/QuoteContext.scala index 839eabeaafeb..542e8ecc2b9f 100644 --- a/compiler/src/dotty/tools/dotc/staging/StagingContext.scala +++ b/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala @@ -7,7 +7,7 @@ import dotty.tools.dotc.util.Property import dotty.tools.dotc.staging.PCPCheckAndHeal import dotty.tools.dotc.staging.StagingLevel.* -object StagingContext { +object QuoteContext { /** A key to be used in a context property that tracks the quotation stack. * Stack containing the Quotes references received by the surrounding quotes. @@ -15,9 +15,9 @@ object StagingContext { private val QuotesStack = new Property.Key[List[tpd.Tree]] /** Context with an incremented quotation level and pushes a reference to a Quotes on the quote context stack */ - def pushQuotes(qctxRef: tpd.Tree)(using Context): Context = + def pushQuotes(quotes: tpd.Tree)(using Context): Context = val old = ctx.property(QuotesStack).getOrElse(List.empty) - quoteContext.setProperty(QuotesStack, qctxRef :: old) + quoteContext.setProperty(QuotesStack, quotes :: old) /** Context with a decremented quotation level and pops the Some of top of the quote context stack or None if the stack is empty. * The quotation stack could be empty if we are in a top level splice or an erroneous splice directly within a top level splice. diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 1967a0298504..d6b7f3141b96 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -12,7 +12,7 @@ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.inlines.Inlines import dotty.tools.dotc.ast.TreeMapWithImplicits import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer -import dotty.tools.dotc.staging.StagingContext.* +import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel import scala.collection.mutable.ListBuffer diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 3aec4ceb156c..2b35188425fb 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -22,7 +22,7 @@ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.config.ScalaRelease.* import dotty.tools.dotc.staging.PCPCheckAndHeal -import dotty.tools.dotc.staging.StagingContext.* +import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index b0c3b8086971..5ebfa25eeacd 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.transform.SymUtils._ -import dotty.tools.dotc.staging.StagingContext.* +import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.PCPCheckAndHeal import dotty.tools.dotc.staging.HealType diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index e1f91a46f6bc..5d04f27a1305 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -15,7 +15,7 @@ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.inlines.PrepareInlineable -import dotty.tools.dotc.staging.StagingContext.* +import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.typer.Implicits._ From 8bb74d7e80df4c436a02741d9392a9a608552767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 20 Feb 2023 11:00:37 +0100 Subject: [PATCH 272/657] Add InlineNumeric prototype skeleton --- InlineNumeric.scala | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 InlineNumeric.scala diff --git a/InlineNumeric.scala b/InlineNumeric.scala new file mode 100644 index 000000000000..e044998e8d70 --- /dev/null +++ b/InlineNumeric.scala @@ -0,0 +1,50 @@ +// community-build/community-projects/stdLib213/src/library/scala/math/Numeric.scala +// community-build/community-projects/stdLib213/src/library/scala/math/Integral.scala +// community-build/community-projects/stdLib213/src/library/scala/math/Fractional.scala + + +trait InlineNumeric[T]: // extends Numeric[T] // TODO can we do this? + transparent inline def plus(inline x: T, inline y: T): T + transparent inline def times(inline x: T, inline y: T): T + // TODO add missing methods +object InlineNumeric: + extension [T](inline x: T)(using inline num: InlineNumeric[T]) + transparent inline def +(inline y: T): T = num.plus(x, y) + transparent inline def *(inline y: T): T = num.times(x, y) + // TODO add missing methods + +trait InlineIntegral[T] extends InlineNumeric[T]: + transparent inline def quot(inline x: T, inline y: T): T + // TODO add missing methods +object InlineIntegral: + // TODO: how are these imported/composed with Numeric/Integral. Should the extension methods be defined in trait InlineIntegral? + extension [T](inline lhs: T)(using inline int: InlineIntegral[T]) + transparent inline def /(inline rhs: T) = int.quot(lhs, rhs) + // TODO add missing methods + +// TODO: InlineFractional + +given IntIsInlineIntegral: InlineIntegral[Int] with + transparent inline def plus(inline x: Int, inline y: Int): Int = x + y + transparent inline def times(inline x: Int, inline y: Int): Int = x * y + transparent inline def quot(inline x: Int, inline y: Int): Int = x / y + // TODO add missing methods + +given ShortIsInlineIntegral: InlineIntegral[Short] with + transparent inline def plus(inline x: Short, inline y: Short): Short = (x + y).toShort + transparent inline def times(inline x: Short, inline y: Short): Short = (x * y).toShort + transparent inline def quot(inline x: Short, inline y: Short): Short = (x / y).toShort + // TODO add missing methods + +// TODO add missing primitive types + +object tests: + import InlineNumeric.* + + // A generic inline operation that inlines/specializes primitive operations + inline def foo[T: InlineNumeric](a: T, b: T) = + a + b * b + + def test(a: Int, b: Int) = + foo(a, b) // should be a + b * b // can check with -Xprint:inlining + foo(a.toShort, b.toShort) // should be a + b * b From a1b4cc3a1a9784cae6d736ac3e2eb0d29595a0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Mon, 20 Feb 2023 14:34:47 +0100 Subject: [PATCH 273/657] Implement missing methods, add examples Add methods missing in the skeleton Add given for all primitive types Add test methods for division, modulo, sign and toInt --- InlineNumeric.scala | 203 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 192 insertions(+), 11 deletions(-) diff --git a/InlineNumeric.scala b/InlineNumeric.scala index e044998e8d70..c011923e1fa2 100644 --- a/InlineNumeric.scala +++ b/InlineNumeric.scala @@ -2,41 +2,200 @@ // community-build/community-projects/stdLib213/src/library/scala/math/Integral.scala // community-build/community-projects/stdLib213/src/library/scala/math/Fractional.scala +import scala.util.Try -trait InlineNumeric[T]: // extends Numeric[T] // TODO can we do this? +trait InlineNumeric[T] extends Ordering[T]: // extends Numeric[T] // TODO can we do this? transparent inline def plus(inline x: T, inline y: T): T + transparent inline def minus(inline x: T, inline y: T): T transparent inline def times(inline x: T, inline y: T): T - // TODO add missing methods + transparent inline def negate(inline x: T): T + transparent inline def fromInt(inline x: Int): T + transparent inline def parseString(inline str: String): Option[T] + transparent inline def toInt(inline x: T): Int + transparent inline def toLong(inline x: T): Long + transparent inline def toFloat(inline x: T): Float + transparent inline def toDouble(inline x: T): Double + + transparent inline def zero = fromInt(0) + transparent inline def one = fromInt(1) + + transparent inline def abs(inline x: T): T = if lt(x, zero) then negate(x) else x + + transparent inline def sign(inline x: T): T = + if lt(x, zero) then negate(one) + else if gt(x, zero) then one + else zero + object InlineNumeric: extension [T](inline x: T)(using inline num: InlineNumeric[T]) transparent inline def +(inline y: T): T = num.plus(x, y) + transparent inline def -(inline y: T) = num.minus(x, y) transparent inline def *(inline y: T): T = num.times(x, y) - // TODO add missing methods + transparent inline def unary_- = num.negate(x) + transparent inline def toInt: Int = num.toInt(x) + transparent inline def toLong: Long = num.toLong(x) + transparent inline def toFloat: Float = num.toFloat(x) + transparent inline def toDouble: Double = num.toDouble(x) + + transparent inline def abs: T = num.abs(x) + + transparent inline def sign: T = num.sign(x) trait InlineIntegral[T] extends InlineNumeric[T]: transparent inline def quot(inline x: T, inline y: T): T - // TODO add missing methods + transparent inline def rem(inline x: T, inline y: T): T + object InlineIntegral: // TODO: how are these imported/composed with Numeric/Integral. Should the extension methods be defined in trait InlineIntegral? extension [T](inline lhs: T)(using inline int: InlineIntegral[T]) transparent inline def /(inline rhs: T) = int.quot(lhs, rhs) - // TODO add missing methods + transparent inline def %(inline rhs: T) = int.rem(lhs, rhs) + transparent inline def /%(inline rhs: T) = (int.quot(lhs, rhs), int.rem(lhs, rhs)) + +trait InlineFractional[T] extends InlineNumeric[T]: + transparent inline def div(inline x: T, inline y: T): T -// TODO: InlineFractional +object InlineFractional: + // TODO: how are these imported/composed with Numeric/Fractional. Should the extension methods be defined in trait InlineFractional? + extension [T](inline lhs: T)(using inline frac: InlineFractional[T]) + transparent inline def /(inline rhs: T) = frac.div(lhs, rhs) -given IntIsInlineIntegral: InlineIntegral[Int] with +given IntIsInlineIntegral: InlineIntegral[Int] with Ordering.IntOrdering with transparent inline def plus(inline x: Int, inline y: Int): Int = x + y + transparent inline def minus(inline x: Int, inline y: Int): Int = x - y transparent inline def times(inline x: Int, inline y: Int): Int = x * y + transparent inline def negate(inline x: Int): Int = -x + transparent inline def fromInt(inline x: Int): Int = x + transparent inline def parseString(inline str: String): Option[Int] = str.toIntOption + transparent inline def toInt(inline x: Int): Int = x + transparent inline def toLong(inline x: Int): Long = x.toLong + transparent inline def toFloat(inline x: Int): Float = x.toFloat + transparent inline def toDouble(inline x: Int): Double = x.toDouble + transparent inline def quot(inline x: Int, inline y: Int): Int = x / y - // TODO add missing methods + transparent inline def rem(inline x: Int, inline y: Int): Int = x % y + +given BigIntIsInlineIntegral: InlineIntegral[BigInt] with Ordering.BigIntOrdering with + transparent inline def plus(inline x: BigInt, inline y: BigInt): BigInt = x + y + transparent inline def minus(inline x: BigInt, inline y: BigInt): BigInt = x - y + transparent inline def times(inline x: BigInt, inline y: BigInt): BigInt = x * y + transparent inline def negate(inline x: BigInt): BigInt = -x + transparent inline def fromInt(inline x: Int): BigInt = BigInt(x) + transparent inline def parseString(inline str: String): Option[BigInt] = Try(BigInt(str)).toOption + transparent inline def toInt(inline x: BigInt): Int = x.intValue + transparent inline def toLong(inline x: BigInt): Long = x.longValue + transparent inline def toFloat(inline x: BigInt): Float = x.floatValue + transparent inline def toDouble(inline x: BigInt): Double = x.doubleValue -given ShortIsInlineIntegral: InlineIntegral[Short] with + transparent inline def quot(inline x: BigInt, inline y: BigInt): BigInt = x / y + transparent inline def rem(inline x: BigInt, inline y: BigInt): BigInt = x % y + +given ShortIsInlineIntegral: InlineIntegral[Short] with Ordering.ShortOrdering with transparent inline def plus(inline x: Short, inline y: Short): Short = (x + y).toShort + transparent inline def minus(inline x: Short, inline y: Short): Short = (x - y).toShort transparent inline def times(inline x: Short, inline y: Short): Short = (x * y).toShort + transparent inline def negate(inline x: Short): Short = (-x).toShort + transparent inline def fromInt(inline x: Int): Short = x.toShort + transparent inline def parseString(inline str: String): Option[Short] = str.toShortOption + transparent inline def toInt(inline x: Short): Int = x.toInt + transparent inline def toLong(inline x: Short): Long = x.toLong + transparent inline def toFloat(inline x: Short): Float = x.toFloat + transparent inline def toDouble(inline x: Short): Double = x.toDouble + transparent inline def quot(inline x: Short, inline y: Short): Short = (x / y).toShort - // TODO add missing methods + transparent inline def rem(inline x: Short, inline y: Short): Short = (x % y).toShort + +given ByteIsInlineIntegral: InlineIntegral[Byte] with Ordering.ByteOrdering with + transparent inline def plus(inline x: Byte, inline y: Byte): Byte = (x + y).toByte + transparent inline def minus(inline x: Byte, inline y: Byte): Byte = (x - y).toByte + transparent inline def times(inline x: Byte, inline y: Byte): Byte = (x * y).toByte + transparent inline def negate(inline x: Byte): Byte = (-x).toByte + transparent inline def fromInt(inline x: Int): Byte = x.toByte + transparent inline def parseString(inline str: String): Option[Byte] = str.toByteOption + transparent inline def toInt(inline x: Byte): Int = x.toInt + transparent inline def toLong(inline x: Byte): Long = x.toLong + transparent inline def toFloat(inline x: Byte): Float = x.toFloat + transparent inline def toDouble(inline x: Byte): Double = x.toDouble + + transparent inline def quot(inline x: Byte, inline y: Byte): Byte = (x / y).toByte + transparent inline def rem(inline x: Byte, inline y: Byte): Byte = (x % y).toByte + +given CharIsInlineIntegral: InlineIntegral[Char] with Ordering.CharOrdering with + transparent inline def plus(inline x: Char, inline y: Char): Char = (x + y).toChar + transparent inline def minus(inline x: Char, inline y: Char): Char = (x - y).toChar + transparent inline def times(inline x: Char, inline y: Char): Char = (x * y).toChar + transparent inline def negate(inline x: Char): Char = (-x).toChar + transparent inline def fromInt(inline x: Int): Char = x.toChar + transparent inline def parseString(inline str: String): Option[Char] = Try(str.toInt.toChar).toOption + transparent inline def toInt(inline x: Char): Int = x.toInt + transparent inline def toLong(inline x: Char): Long = x.toLong + transparent inline def toFloat(inline x: Char): Float = x.toFloat + transparent inline def toDouble(inline x: Char): Double = x.toDouble + + transparent inline def quot(inline x: Char, inline y: Char): Char = (x / y).toChar + transparent inline def rem(inline x: Char, inline y: Char): Char = (x % y).toChar -// TODO add missing primitive types +given LongIsInlineIntegral: InlineIntegral[Long] with Ordering.LongOrdering with + transparent inline def plus(inline x: Long, inline y: Long): Long = x + y + transparent inline def minus(inline x: Long, inline y: Long): Long = x - y + transparent inline def times(inline x: Long, inline y: Long): Long = x * y + transparent inline def negate(inline x: Long): Long = -x + transparent inline def fromInt(inline x: Int): Long = x.toLong + transparent inline def parseString(inline str: String): Option[Long] = str.toLongOption + transparent inline def toInt(inline x: Long): Int = x.toInt + transparent inline def toLong(inline x: Long): Long = x + transparent inline def toFloat(inline x: Long): Float = x.toFloat + transparent inline def toDouble(inline x: Long): Double = x.toDouble + + transparent inline def quot(inline x: Long, inline y: Long): Long = (x / y).toLong + transparent inline def rem(inline x: Long, inline y: Long): Long = (x % y).toLong + +given FloatIsInlineFractional: InlineFractional[Float] with Ordering.Float.IeeeOrdering with + transparent inline def plus(inline x: Float, inline y: Float): Float = x + y + transparent inline def minus(inline x: Float, inline y: Float): Float = x - y + transparent inline def times(inline x: Float, inline y: Float): Float = x * y + transparent inline def negate(inline x: Float): Float = -x + transparent inline def fromInt(inline x: Int): Float = x.toFloat + transparent inline def parseString(inline str: String): Option[Float] = str.toFloatOption + transparent inline def toInt(inline x: Float): Int = x.toInt + transparent inline def toLong(inline x: Float): Long = x.toLong + transparent inline def toFloat(inline x: Float): Float = x + transparent inline def toDouble(inline x: Float): Double = x.toDouble + + transparent inline def div(inline x: Float, inline y: Float): Float = x / y + +given DoubleIsInlineFractional: InlineFractional[Double] with Ordering.Double.IeeeOrdering with + transparent inline def plus(inline x: Double, inline y: Double): Double = x + y + transparent inline def minus(inline x: Double, inline y: Double): Double = x - y + transparent inline def times(inline x: Double, inline y: Double): Double = x * y + transparent inline def negate(inline x: Double): Double = -x + transparent inline def fromInt(inline x: Int): Double = x.toDouble + transparent inline def parseString(inline str: String): Option[Double] = str.toDoubleOption + transparent inline def toInt(inline x: Double): Int = x.toInt + transparent inline def toLong(inline x: Double): Long = x.toLong + transparent inline def toFloat(inline x: Double): Float = x.toFloat + transparent inline def toDouble(inline x: Double): Double = x + + transparent inline def div(inline x: Double, inline y: Double): Double = x / y + +trait BigDecimalIsConflicted extends InlineNumeric[BigDecimal] with Ordering.BigDecimalOrdering: + transparent inline def plus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x + y + transparent inline def minus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x - y + transparent inline def times(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x * y + transparent inline def negate(inline x: BigDecimal): BigDecimal = -x + transparent inline def fromInt(inline x: Int): BigDecimal = BigDecimal(x) + transparent inline def parseString(inline str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption + transparent inline def toInt(inline x: BigDecimal): Int = x.intValue + transparent inline def toLong(inline x: BigDecimal): Long = x.longValue + transparent inline def toFloat(inline x: BigDecimal): Float = x.floatValue + transparent inline def toDouble(inline x: BigDecimal): Double = x.doubleValue + +given BigDecimalIsInlineFractional: BigDecimalIsConflicted with InlineFractional[BigDecimal] with + transparent inline def div(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x / y + +given BigDecimalAsIfInlineIntegral: BigDecimalIsConflicted with InlineIntegral[BigDecimal] with + transparent inline def quot(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x quot y + transparent inline def rem(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x remainder y object tests: import InlineNumeric.* @@ -45,6 +204,28 @@ object tests: inline def foo[T: InlineNumeric](a: T, b: T) = a + b * b + inline def integDiv[T: InlineIntegral](a: T, b: T) = + import InlineIntegral.{/, %} + a / b % b + + inline def fracDiv[T: InlineFractional](a: T, b: T) = + import InlineFractional.{/} + a / b + a + + inline def bar[T: InlineNumeric](a: T) = a.toInt + + inline def sign[T: InlineNumeric](a: T) = a.sign + def test(a: Int, b: Int) = foo(a, b) // should be a + b * b // can check with -Xprint:inlining foo(a.toShort, b.toShort) // should be a + b * b + + integDiv(BigDecimal(a), BigDecimal(b)) // should be BigDecimal(a) quot BigDecimal(b) remainder BigDecimal(b) + fracDiv(BigDecimal(a), BigDecimal(b)) // should be BigDecimal(a) / BigDecimal(b) + BigDecimal(a) + + bar(a.toFloat) // should be a.toFloat.toInt + bar(a) // should be a + + sign(a) + sign(a.toChar) + sign(-7F) From 66db95a0ab9aa04731cdb20defda6224bbc72fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 21 Feb 2023 11:06:36 +0100 Subject: [PATCH 274/657] Fix mishandling of abs and sign for float and double --- InlineNumeric.scala | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/InlineNumeric.scala b/InlineNumeric.scala index c011923e1fa2..99bd447d6307 100644 --- a/InlineNumeric.scala +++ b/InlineNumeric.scala @@ -15,17 +15,12 @@ trait InlineNumeric[T] extends Ordering[T]: // extends Numeric[T] // TODO can we transparent inline def toLong(inline x: T): Long transparent inline def toFloat(inline x: T): Float transparent inline def toDouble(inline x: T): Double + transparent inline def abs(inline x: T): T + transparent inline def sign(inline x: T): T transparent inline def zero = fromInt(0) transparent inline def one = fromInt(1) - transparent inline def abs(inline x: T): T = if lt(x, zero) then negate(x) else x - - transparent inline def sign(inline x: T): T = - if lt(x, zero) then negate(one) - else if gt(x, zero) then one - else zero - object InlineNumeric: extension [T](inline x: T)(using inline num: InlineNumeric[T]) transparent inline def +(inline y: T): T = num.plus(x, y) @@ -36,12 +31,16 @@ object InlineNumeric: transparent inline def toLong: Long = num.toLong(x) transparent inline def toFloat: Float = num.toFloat(x) transparent inline def toDouble: Double = num.toDouble(x) - transparent inline def abs: T = num.abs(x) - transparent inline def sign: T = num.sign(x) trait InlineIntegral[T] extends InlineNumeric[T]: + transparent inline def abs(inline x: T): T = if lt(x, zero) then negate(x) else x + transparent inline def sign(inline x: T): T = + if lt(x, zero) then negate(one) + else if gt(x, zero) then one + else zero + transparent inline def quot(inline x: T, inline y: T): T transparent inline def rem(inline x: T, inline y: T): T @@ -53,7 +52,16 @@ object InlineIntegral: transparent inline def /%(inline rhs: T) = (int.quot(lhs, rhs), int.rem(lhs, rhs)) trait InlineFractional[T] extends InlineNumeric[T]: + transparent inline def abs(inline x: T): T = if lt(x, zero) || isNegZero(x) then negate(x) else x + transparent inline def sign(inline x: T): T = + if isNaN(x) || isNegZero(x) then x + else if lt(x, zero) then negate(one) + else if gt(x, zero) then one + else zero + transparent inline def div(inline x: T, inline y: T): T + protected transparent inline def isNaN(inline x: T): Boolean + protected transparent inline def isNegZero(inline x: T): Boolean object InlineFractional: // TODO: how are these imported/composed with Numeric/Fractional. Should the extension methods be defined in trait InlineFractional? @@ -163,6 +171,8 @@ given FloatIsInlineFractional: InlineFractional[Float] with Ordering.Float.IeeeO transparent inline def toDouble(inline x: Float): Double = x.toDouble transparent inline def div(inline x: Float, inline y: Float): Float = x / y + protected transparent inline def isNaN(inline x: Float): Boolean = x.isNaN + protected transparent inline def isNegZero(inline x: Float): Boolean = x.equals(-0f) given DoubleIsInlineFractional: InlineFractional[Double] with Ordering.Double.IeeeOrdering with transparent inline def plus(inline x: Double, inline y: Double): Double = x + y @@ -177,6 +187,8 @@ given DoubleIsInlineFractional: InlineFractional[Double] with Ordering.Double.Ie transparent inline def toDouble(inline x: Double): Double = x transparent inline def div(inline x: Double, inline y: Double): Double = x / y + protected transparent inline def isNaN(inline x: Double): Boolean = x.isNaN + protected transparent inline def isNegZero(inline x: Double): Boolean = x.equals(-0.0) trait BigDecimalIsConflicted extends InlineNumeric[BigDecimal] with Ordering.BigDecimalOrdering: transparent inline def plus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x + y @@ -192,6 +204,9 @@ trait BigDecimalIsConflicted extends InlineNumeric[BigDecimal] with Ordering.Big given BigDecimalIsInlineFractional: BigDecimalIsConflicted with InlineFractional[BigDecimal] with transparent inline def div(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x / y + protected transparent inline def isNaN(inline x: BigDecimal): Boolean = false + protected transparent inline def isNegZero(inline x: BigDecimal): Boolean = false + given BigDecimalAsIfInlineIntegral: BigDecimalIsConflicted with InlineIntegral[BigDecimal] with transparent inline def quot(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x quot y @@ -214,7 +229,8 @@ object tests: inline def bar[T: InlineNumeric](a: T) = a.toInt - inline def sign[T: InlineNumeric](a: T) = a.sign + inline def signInt[T: InlineIntegral](a: T) = a.sign + inline def signFrac[T: InlineFractional](a: T) = a.sign def test(a: Int, b: Int) = foo(a, b) // should be a + b * b // can check with -Xprint:inlining @@ -226,6 +242,9 @@ object tests: bar(a.toFloat) // should be a.toFloat.toInt bar(a) // should be a - sign(a) - sign(a.toChar) - sign(-7F) + signInt(a) + signInt(a.toChar) + signFrac(-7F) + + signInt(BigDecimal(a)) + signFrac(BigDecimal(a)) // the condition with isNan() should be removed From 28bf386ca3a654c0c2b9479dd0538dcc39cdad3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 21 Feb 2023 12:06:54 +0100 Subject: [PATCH 275/657] Use explicit InlineNumeric for BigDecimal --- InlineNumeric.scala | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/InlineNumeric.scala b/InlineNumeric.scala index 99bd447d6307..25a5185287f3 100644 --- a/InlineNumeric.scala +++ b/InlineNumeric.scala @@ -219,32 +219,33 @@ object tests: inline def foo[T: InlineNumeric](a: T, b: T) = a + b * b - inline def integDiv[T: InlineIntegral](a: T, b: T) = + inline def div[T: InlineIntegral](a: T, b: T) = import InlineIntegral.{/, %} a / b % b - inline def fracDiv[T: InlineFractional](a: T, b: T) = + inline def div[T: InlineFractional](a: T, b: T) = import InlineFractional.{/} a / b + a - inline def bar[T: InlineNumeric](a: T) = a.toInt + inline def bar[T: InlineNumeric](a: T) = + a.toInt - inline def signInt[T: InlineIntegral](a: T) = a.sign - inline def signFrac[T: InlineFractional](a: T) = a.sign + inline def sign[T: InlineNumeric](a: T) = + a.sign def test(a: Int, b: Int) = - foo(a, b) // should be a + b * b // can check with -Xprint:inlining - foo(a.toShort, b.toShort) // should be a + b * b + val v1 = foo(a, b) // should be a + b * b // can check with -Xprint:inlining + val v2 = foo(a.toShort, b.toShort) // should be a + b * b - integDiv(BigDecimal(a), BigDecimal(b)) // should be BigDecimal(a) quot BigDecimal(b) remainder BigDecimal(b) - fracDiv(BigDecimal(a), BigDecimal(b)) // should be BigDecimal(a) / BigDecimal(b) + BigDecimal(a) + val v3 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalAsIfInlineIntegral) // should be BigDecimal(a) quot BigDecimal(b) remainder BigDecimal(b) + val v4 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalIsInlineFractional) // should be BigDecimal(a) / BigDecimal(b) + BigDecimal(a) - bar(a.toFloat) // should be a.toFloat.toInt - bar(a) // should be a + val v5 = bar(a.toFloat) // should be a.toFloat.toInt + val v6 = bar(a) // should be a - signInt(a) - signInt(a.toChar) - signFrac(-7F) + val v7 = sign(a) + val v8 = sign(a.toChar) + val v9 = sign(-7F) - signInt(BigDecimal(a)) - signFrac(BigDecimal(a)) // the condition with isNan() should be removed + val v10 = sign(BigDecimal(a))(using BigDecimalAsIfInlineIntegral) + val v11 = sign(BigDecimal(a))(using BigDecimalIsInlineFractional) // the condition with isNan() should be removed, i.e. it should be equivalent to v10 \ No newline at end of file From c8251983a75e30b8135e22588353365fac44ed0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 21 Feb 2023 14:20:21 +0100 Subject: [PATCH 276/657] Inline parameters to shorten generated code --- InlineNumeric.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/InlineNumeric.scala b/InlineNumeric.scala index 25a5185287f3..061f8b11942d 100644 --- a/InlineNumeric.scala +++ b/InlineNumeric.scala @@ -214,23 +214,23 @@ given BigDecimalAsIfInlineIntegral: BigDecimalIsConflicted with InlineIntegral[B object tests: import InlineNumeric.* + import InlineIntegral.{/ => quot, %} + import InlineFractional.{/} // A generic inline operation that inlines/specializes primitive operations - inline def foo[T: InlineNumeric](a: T, b: T) = + inline def foo[T: InlineNumeric](inline a: T, inline b: T) = a + b * b - inline def div[T: InlineIntegral](a: T, b: T) = - import InlineIntegral.{/, %} - a / b % b + inline def div[T: InlineIntegral](inline a: T, inline b: T) = + a quot b % b - inline def div[T: InlineFractional](a: T, b: T) = - import InlineFractional.{/} + inline def div[T: InlineFractional](inline a: T, inline b: T) = a / b + a - inline def bar[T: InlineNumeric](a: T) = + inline def bar[T: InlineNumeric](inline a: T) = a.toInt - inline def sign[T: InlineNumeric](a: T) = + inline def sign[T: InlineNumeric](inline a: T) = a.sign def test(a: Int, b: Int) = From 563f2c5a755eba625abdc87852cb76191de7522a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 21 Feb 2023 14:37:19 +0100 Subject: [PATCH 277/657] Use symbol for integral division --- InlineNumeric.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InlineNumeric.scala b/InlineNumeric.scala index 061f8b11942d..265343dfc793 100644 --- a/InlineNumeric.scala +++ b/InlineNumeric.scala @@ -214,7 +214,7 @@ given BigDecimalAsIfInlineIntegral: BigDecimalIsConflicted with InlineIntegral[B object tests: import InlineNumeric.* - import InlineIntegral.{/ => quot, %} + import InlineIntegral.{/ => ¦, %} import InlineFractional.{/} // A generic inline operation that inlines/specializes primitive operations @@ -222,7 +222,7 @@ object tests: a + b * b inline def div[T: InlineIntegral](inline a: T, inline b: T) = - a quot b % b + a ¦ b % b inline def div[T: InlineFractional](inline a: T, inline b: T) = a / b + a From 2113507cca507c041dea67c385423865a4833689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 21 Feb 2023 16:54:05 +0100 Subject: [PATCH 278/657] Create two new versions of InlineNumeric Create version of InlineNumeric that extends Numeric. This requires: - dropping the inline keyword for parameters (cannot override non-inline parameter with inline parameter) - dropping abs, sign, zero and one from InlineNumeric (see code for the errors). Instead, override them in the given instances Create version of InlineNumeric with extension methods defined in the traits instead of in the companion objects. This allows the usage of said methods without having to import them. The inconvenient is that the overriding must be done with extension methods, but since an extension method object A: extension (x: T) def foo: U = ??? can be called both like x.foo and A.foo(x), this allows for both forms when performing operations such as toInt. --- InlineNumeric-extendsNumeric.scala | 242 ++++++++++++++++++++++++ InlineNumeric-extensionsInTrait.scala | 263 ++++++++++++++++++++++++++ 2 files changed, 505 insertions(+) create mode 100644 InlineNumeric-extendsNumeric.scala create mode 100644 InlineNumeric-extensionsInTrait.scala diff --git a/InlineNumeric-extendsNumeric.scala b/InlineNumeric-extendsNumeric.scala new file mode 100644 index 000000000000..0c0587c8c416 --- /dev/null +++ b/InlineNumeric-extendsNumeric.scala @@ -0,0 +1,242 @@ +import scala.util.Try + +trait InlineNumeric[T] extends Numeric[T]: + transparent inline def plus(x: T, y: T): T + transparent inline def minus(x: T, y: T): T + transparent inline def times(x: T, y: T): T + transparent inline def negate(x: T): T + transparent inline def fromInt(x: Int): T + transparent inline def parseString(str: String): Option[T] + transparent inline def toInt(x: T): Int + transparent inline def toLong(x: T): Long + transparent inline def toFloat(x: T): Float + transparent inline def toDouble(x: T): Double + + // error overriding method abs in trait InlineNumeric of type (x: T): T; + // method abs in trait Numeric of type (x: T): T is not inline, cannot implement an inline method + //transparent inline def abs(x: T): T + //transparent inline def sign(x: T): T + + // Deferred inline method fromInt in trait InlineNumeric cannot be invoked + //transparent inline def zero = fromInt(0) + //transparent inline def one = fromInt(1) + +object InlineNumeric: + extension [T](x: T)(using num: InlineNumeric[T]) + transparent inline def +(y: T): T = num.plus(x, y) + transparent inline def -(y: T) = num.minus(x, y) + transparent inline def *(y: T): T = num.times(x, y) + transparent inline def unary_- = num.negate(x) + transparent inline def toInt: Int = num.toInt(x) + transparent inline def toLong: Long = num.toLong(x) + transparent inline def toFloat: Float = num.toFloat(x) + transparent inline def toDouble: Double = num.toDouble(x) + transparent inline def abs: T = num.abs(x) + transparent inline def sign: T = num.sign(x) + +trait InlineIntegral[T] extends InlineNumeric[T]: + transparent inline def quot(x: T, y: T): T + transparent inline def rem(x: T, y: T): T + +object InlineIntegral: + extension [T](lhs: T)(using int: InlineIntegral[T]) + transparent inline def /(rhs: T) = int.quot(lhs, rhs) + transparent inline def %(rhs: T) = int.rem(lhs, rhs) + transparent inline def /%(rhs: T) = (int.quot(lhs, rhs), int.rem(lhs, rhs)) + +trait InlineFractional[T] extends InlineNumeric[T]: + transparent inline def div(x: T, y: T): T + +object InlineFractional: + extension [T](lhs: T)(using frac: InlineFractional[T]) + transparent inline def /(rhs: T) = frac.div(lhs, rhs) + +given IntIsInlineIntegral: InlineIntegral[Int] with Ordering.IntOrdering with + transparent inline def plus(x: Int, y: Int): Int = x + y + transparent inline def minus(x: Int, y: Int): Int = x - y + transparent inline def times(x: Int, y: Int): Int = x * y + transparent inline def negate(x: Int): Int = -x + transparent inline def fromInt(x: Int): Int = x + transparent inline def parseString(str: String): Option[Int] = str.toIntOption + transparent inline def toInt(x: Int): Int = x + transparent inline def toLong(x: Int): Long = x.toLong + transparent inline def toFloat(x: Int): Float = x.toFloat + transparent inline def toDouble(x: Int): Double = x.toDouble + transparent override inline def abs(x: Int): Int = math.abs(x) + transparent override inline def sign(x: Int): Int = math.signum(x) + + transparent inline def quot(x: Int, y: Int): Int = x / y + transparent inline def rem(x: Int, y: Int): Int = x % y + +given BigIntIsInlineIntegral: InlineIntegral[BigInt] with Ordering.BigIntOrdering with + transparent inline def plus(x: BigInt, y: BigInt): BigInt = x + y + transparent inline def minus(x: BigInt, y: BigInt): BigInt = x - y + transparent inline def times(x: BigInt, y: BigInt): BigInt = x * y + transparent inline def negate(x: BigInt): BigInt = -x + transparent inline def fromInt(x: Int): BigInt = BigInt(x) + transparent inline def parseString(str: String): Option[BigInt] = Try(BigInt(str)).toOption + transparent inline def toInt(x: BigInt): Int = x.intValue + transparent inline def toLong(x: BigInt): Long = x.longValue + transparent inline def toFloat(x: BigInt): Float = x.floatValue + transparent inline def toDouble(x: BigInt): Double = x.doubleValue + transparent override inline def abs(x: BigInt): BigInt = x.abs + transparent override inline def sign(x: BigInt): BigInt = x.sign + + transparent inline def quot(x: BigInt, y: BigInt): BigInt = x / y + transparent inline def rem(x: BigInt, y: BigInt): BigInt = x % y + +given ShortIsInlineIntegral: InlineIntegral[Short] with Ordering.ShortOrdering with + transparent inline def plus(x: Short, y: Short): Short = (x + y).toShort + transparent inline def minus(x: Short, y: Short): Short = (x - y).toShort + transparent inline def times(x: Short, y: Short): Short = (x * y).toShort + transparent inline def negate(x: Short): Short = (-x).toShort + transparent inline def fromInt(x: Int): Short = x.toShort + transparent inline def parseString(str: String): Option[Short] = str.toShortOption + transparent inline def toInt(x: Short): Int = x.toInt + transparent inline def toLong(x: Short): Long = x.toLong + transparent inline def toFloat(x: Short): Float = x.toFloat + transparent inline def toDouble(x: Short): Double = x.toDouble + transparent override inline def abs(x: Short): Short = math.abs(x).toShort + transparent override inline def sign(x: Short): Short = math.signum(x).toShort + + transparent inline def quot(x: Short, y: Short): Short = (x / y).toShort + transparent inline def rem(x: Short, y: Short): Short = (x % y).toShort + +given ByteIsInlineIntegral: InlineIntegral[Byte] with Ordering.ByteOrdering with + transparent inline def plus(x: Byte, y: Byte): Byte = (x + y).toByte + transparent inline def minus(x: Byte, y: Byte): Byte = (x - y).toByte + transparent inline def times(x: Byte, y: Byte): Byte = (x * y).toByte + transparent inline def negate(x: Byte): Byte = (-x).toByte + transparent inline def fromInt(x: Int): Byte = x.toByte + transparent inline def parseString(str: String): Option[Byte] = str.toByteOption + transparent inline def toInt(x: Byte): Int = x.toInt + transparent inline def toLong(x: Byte): Long = x.toLong + transparent inline def toFloat(x: Byte): Float = x.toFloat + transparent inline def toDouble(x: Byte): Double = x.toDouble + transparent override inline def abs(x: Byte): Byte = math.abs(x).toByte + transparent override inline def sign(x: Byte): Byte = math.signum(x).toByte + + transparent inline def quot(x: Byte, y: Byte): Byte = (x / y).toByte + transparent inline def rem(x: Byte, y: Byte): Byte = (x % y).toByte + +given CharIsInlineIntegral: InlineIntegral[Char] with Ordering.CharOrdering with + transparent inline def plus(x: Char, y: Char): Char = (x + y).toChar + transparent inline def minus(x: Char, y: Char): Char = (x - y).toChar + transparent inline def times(x: Char, y: Char): Char = (x * y).toChar + transparent inline def negate(x: Char): Char = (-x).toChar + transparent inline def fromInt(x: Int): Char = x.toChar + transparent inline def parseString(str: String): Option[Char] = Try(str.toInt.toChar).toOption + transparent inline def toInt(x: Char): Int = x.toInt + transparent inline def toLong(x: Char): Long = x.toLong + transparent inline def toFloat(x: Char): Float = x.toFloat + transparent inline def toDouble(x: Char): Double = x.toDouble + transparent override inline def abs(x: Char): Char = math.abs(x).toChar + transparent override inline def sign(x: Char): Char = math.signum(x).toChar + + transparent inline def quot(x: Char, y: Char): Char = (x / y).toChar + transparent inline def rem(x: Char, y: Char): Char = (x % y).toChar + +given LongIsInlineIntegral: InlineIntegral[Long] with Ordering.LongOrdering with + transparent inline def plus(x: Long, y: Long): Long = x + y + transparent inline def minus(x: Long, y: Long): Long = x - y + transparent inline def times(x: Long, y: Long): Long = x * y + transparent inline def negate(x: Long): Long = -x + transparent inline def fromInt(x: Int): Long = x.toLong + transparent inline def parseString(str: String): Option[Long] = str.toLongOption + transparent inline def toInt(x: Long): Int = x.toInt + transparent inline def toLong(x: Long): Long = x + transparent inline def toFloat(x: Long): Float = x.toFloat + transparent inline def toDouble(x: Long): Double = x.toDouble + transparent override inline def abs(x: Long): Long = math.abs(x) + transparent override inline def sign(x: Long): Long = math.signum(x) + + transparent inline def quot(x: Long, y: Long): Long = (x / y).toLong + transparent inline def rem(x: Long, y: Long): Long = (x % y).toLong + +given FloatIsInlineFractional: InlineFractional[Float] with Ordering.Float.IeeeOrdering with + transparent inline def plus(x: Float, y: Float): Float = x + y + transparent inline def minus(x: Float, y: Float): Float = x - y + transparent inline def times(x: Float, y: Float): Float = x * y + transparent inline def negate(x: Float): Float = -x + transparent inline def fromInt(x: Int): Float = x.toFloat + transparent inline def parseString(str: String): Option[Float] = str.toFloatOption + transparent inline def toInt(x: Float): Int = x.toInt + transparent inline def toLong(x: Float): Long = x.toLong + transparent inline def toFloat(x: Float): Float = x + transparent inline def toDouble(x: Float): Double = x.toDouble + transparent override inline def abs(x: Float): Float = math.abs(x) + transparent override inline def sign(x: Float): Float = math.signum(x) + + transparent inline def div(x: Float, y: Float): Float = x / y + +given DoubleIsInlineFractional: InlineFractional[Double] with Ordering.Double.IeeeOrdering with + transparent inline def plus(x: Double, y: Double): Double = x + y + transparent inline def minus(x: Double, y: Double): Double = x - y + transparent inline def times(x: Double, y: Double): Double = x * y + transparent inline def negate(x: Double): Double = -x + transparent inline def fromInt(x: Int): Double = x.toDouble + transparent inline def parseString(str: String): Option[Double] = str.toDoubleOption + transparent inline def toInt(x: Double): Int = x.toInt + transparent inline def toLong(x: Double): Long = x.toLong + transparent inline def toFloat(x: Double): Float = x.toFloat + transparent inline def toDouble(x: Double): Double = x + transparent override inline def abs(x: Double): Double = math.abs(x) + transparent override inline def sign(x: Double): Double = math.signum(x) + + transparent inline def div(x: Double, y: Double): Double = x / y + +trait BigDecimalIsConflicted extends InlineNumeric[BigDecimal] with Ordering.BigDecimalOrdering: + transparent inline def plus(x: BigDecimal, y: BigDecimal): BigDecimal = x + y + transparent inline def minus(x: BigDecimal, y: BigDecimal): BigDecimal = x - y + transparent inline def times(x: BigDecimal, y: BigDecimal): BigDecimal = x * y + transparent inline def negate(x: BigDecimal): BigDecimal = -x + transparent inline def fromInt(x: Int): BigDecimal = BigDecimal(x) + transparent inline def parseString(str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption + transparent inline def toInt(x: BigDecimal): Int = x.intValue + transparent inline def toLong(x: BigDecimal): Long = x.longValue + transparent inline def toFloat(x: BigDecimal): Float = x.floatValue + transparent inline def toDouble(x: BigDecimal): Double = x.doubleValue + transparent override inline def abs(x: BigDecimal): BigDecimal = x.abs + transparent override inline def sign(x: BigDecimal): BigDecimal = x.sign + +given BigDecimalIsInlineFractional: BigDecimalIsConflicted with InlineFractional[BigDecimal] with + transparent inline def div(x: BigDecimal, y: BigDecimal): BigDecimal = x / y + +given BigDecimalAsIfInlineIntegral: BigDecimalIsConflicted with InlineIntegral[BigDecimal] with + transparent inline def quot(x: BigDecimal, y: BigDecimal): BigDecimal = x quot y + transparent inline def rem(x: BigDecimal, y: BigDecimal): BigDecimal = x remainder y + +object tests: + import InlineNumeric.* + import InlineIntegral.{/ => ¦, %} + import InlineFractional./ + + inline def foo[T: InlineNumeric](a: T, b: T) = + a + b * b + + inline def div[T: InlineIntegral](a: T, b: T) = + a ¦ b % b + + inline def div[T: InlineFractional](a: T, b: T) = + a / b + a + + inline def bar[T: InlineNumeric](a: T) = a.toInt + + inline def sign[T: InlineNumeric](a: T) = a.sign + + def test(a: Int, b: Int) = + val v1 = foo(a, b) // should be a + b * b // can check with -Xprint:inlining + val v2 = foo(a.toShort, b.toShort) // should be a + b * b + + val v3 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalAsIfInlineIntegral) // should be BigDecimal(a) quot BigDecimal(b) remainder BigDecimal(b) + val v4 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalIsInlineFractional) // should be BigDecimal(a) / BigDecimal(b) + BigDecimal(a) + + val v5 = bar(a.toFloat) // should be a.toFloat.toInt + val v6 = bar(a) // should be a + + val v7 = sign(a) + val v8 = sign(a.toChar) + val v9 = sign(-7F) + + val v10 = sign(BigDecimal(a))(using BigDecimalAsIfInlineIntegral) + val v11 = sign(BigDecimal(a))(using BigDecimalIsInlineFractional) // the condition with isNan() should be removed, i.e. it should be equivalent to v10 \ No newline at end of file diff --git a/InlineNumeric-extensionsInTrait.scala b/InlineNumeric-extensionsInTrait.scala new file mode 100644 index 000000000000..bac390783e65 --- /dev/null +++ b/InlineNumeric-extensionsInTrait.scala @@ -0,0 +1,263 @@ +import scala.util.Try + +trait InlineNumeric[T] extends Ordering[T]: // extends Numeric[T] // TODO can we do this? + transparent inline def plus(inline x: T, inline y: T): T + transparent inline def minus(inline x: T, inline y: T): T + transparent inline def times(inline x: T, inline y: T): T + transparent inline def negate(inline x: T): T + transparent inline def fromInt(inline x: Int): T + transparent inline def parseString(inline str: String): Option[T] + + transparent inline def zero = fromInt(0) + transparent inline def one = fromInt(1) + + extension (inline x: T) + transparent inline def +(inline y: T): T = plus(x, y) + transparent inline def -(inline y: T) = minus(x, y) + transparent inline def *(inline y: T): T = times(x, y) + transparent inline def unary_- = negate(x) + transparent inline def toInt: Int + transparent inline def toLong: Long + transparent inline def toFloat: Float + transparent inline def toDouble: Double + transparent inline def abs: T + transparent inline def sign: T + +trait InlineIntegral[T] extends InlineNumeric[T]: + transparent inline def quot(inline x: T, inline y: T): T + transparent inline def rem(inline x: T, inline y: T): T + + extension (inline lhs: T) + transparent inline def /(inline rhs: T) = quot(lhs, rhs) + transparent inline def %(inline rhs: T) = rem(lhs, rhs) + transparent inline def /%(inline rhs: T) = (quot(lhs, rhs), rem(lhs, rhs)) + + transparent inline def abs: T = if lt(lhs, zero) then negate(lhs) else lhs + transparent inline def sign: T = + if lt(lhs, zero) then negate(one) + else if gt(lhs, zero) then one + else zero + +trait InlineFractional[T] extends InlineNumeric[T]: + transparent inline def div(inline x: T, inline y: T): T + protected transparent inline def isNaN(inline x: T): Boolean + protected transparent inline def isNegZero(inline x: T): Boolean + + extension (inline lhs: T) + transparent inline def /(inline rhs: T) = div(lhs, rhs) + transparent inline def abs: T = if lt(lhs, zero) || isNegZero(lhs) then negate(lhs) else lhs + transparent inline def sign: T = + if isNaN(lhs) || isNegZero(lhs) then lhs + else if lt(lhs, zero) then negate(one) + else if gt(lhs, zero) then one + else zero + +given IntIsInlineIntegral: InlineIntegral[Int] with Ordering.IntOrdering with + transparent inline def plus(inline x: Int, inline y: Int): Int = x + y + transparent inline def minus(inline x: Int, inline y: Int): Int = x - y + transparent inline def times(inline x: Int, inline y: Int): Int = x * y + transparent inline def negate(inline x: Int): Int = -x + transparent inline def fromInt(inline x: Int): Int = x + transparent inline def parseString(inline str: String): Option[Int] = str.toIntOption + + transparent inline def quot(inline x: Int, inline y: Int): Int = x / y + transparent inline def rem(inline x: Int, inline y: Int): Int = x % y + + extension (inline x: Int) + transparent inline def toInt: Int = x + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble + +given BigIntIsInlineIntegral: InlineIntegral[BigInt] with Ordering.BigIntOrdering with + transparent inline def plus(inline x: BigInt, inline y: BigInt): BigInt = x + y + transparent inline def minus(inline x: BigInt, inline y: BigInt): BigInt = x - y + transparent inline def times(inline x: BigInt, inline y: BigInt): BigInt = x * y + transparent inline def negate(inline x: BigInt): BigInt = -x + transparent inline def fromInt(inline x: Int): BigInt = BigInt(x) + transparent inline def parseString(inline str: String): Option[BigInt] = Try(BigInt(str)).toOption + + transparent inline def quot(inline x: BigInt, inline y: BigInt): BigInt = x / y + transparent inline def rem(inline x: BigInt, inline y: BigInt): BigInt = x % y + + extension (inline x: BigInt) + transparent inline def toInt: Int = x.intValue + transparent inline def toLong: Long = x.longValue + transparent inline def toFloat: Float = x.floatValue + transparent inline def toDouble: Double = x.doubleValue + +given ShortIsInlineIntegral: InlineIntegral[Short] with Ordering.ShortOrdering with + transparent inline def plus(inline x: Short, inline y: Short): Short = (x + y).toShort + transparent inline def minus(inline x: Short, inline y: Short): Short = (x - y).toShort + transparent inline def times(inline x: Short, inline y: Short): Short = (x * y).toShort + transparent inline def negate(inline x: Short): Short = (-x).toShort + transparent inline def fromInt(inline x: Int): Short = x.toShort + transparent inline def parseString(inline str: String): Option[Short] = str.toShortOption + + transparent inline def quot(inline x: Short, inline y: Short): Short = (x / y).toShort + transparent inline def rem(inline x: Short, inline y: Short): Short = (x % y).toShort + + extension (inline x: Short) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble + +given ByteIsInlineIntegral: InlineIntegral[Byte] with Ordering.ByteOrdering with + transparent inline def plus(inline x: Byte, inline y: Byte): Byte = (x + y).toByte + transparent inline def minus(inline x: Byte, inline y: Byte): Byte = (x - y).toByte + transparent inline def times(inline x: Byte, inline y: Byte): Byte = (x * y).toByte + transparent inline def negate(inline x: Byte): Byte = (-x).toByte + transparent inline def fromInt(inline x: Int): Byte = x.toByte + transparent inline def parseString(inline str: String): Option[Byte] = str.toByteOption + + transparent inline def quot(inline x: Byte, inline y: Byte): Byte = (x / y).toByte + transparent inline def rem(inline x: Byte, inline y: Byte): Byte = (x % y).toByte + + extension (inline x: Byte) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble + +given CharIsInlineIntegral: InlineIntegral[Char] with Ordering.CharOrdering with + transparent inline def plus(inline x: Char, inline y: Char): Char = (x + y).toChar + transparent inline def minus(inline x: Char, inline y: Char): Char = (x - y).toChar + transparent inline def times(inline x: Char, inline y: Char): Char = (x * y).toChar + transparent inline def negate(inline x: Char): Char = (-x).toChar + transparent inline def fromInt(inline x: Int): Char = x.toChar + transparent inline def parseString(inline str: String): Option[Char] = Try(str.toInt.toChar).toOption + + transparent inline def quot(inline x: Char, inline y: Char): Char = (x / y).toChar + transparent inline def rem(inline x: Char, inline y: Char): Char = (x % y).toChar + + extension (inline x: Char) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble + +given LongIsInlineIntegral: InlineIntegral[Long] with Ordering.LongOrdering with + transparent inline def plus(inline x: Long, inline y: Long): Long = x + y + transparent inline def minus(inline x: Long, inline y: Long): Long = x - y + transparent inline def times(inline x: Long, inline y: Long): Long = x * y + transparent inline def negate(inline x: Long): Long = -x + transparent inline def fromInt(inline x: Int): Long = x.toLong + transparent inline def parseString(inline str: String): Option[Long] = str.toLongOption + + transparent inline def quot(inline x: Long, inline y: Long): Long = (x / y).toLong + transparent inline def rem(inline x: Long, inline y: Long): Long = (x % y).toLong + + extension (inline x: Long) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble + +given FloatIsInlineFractional: InlineFractional[Float] with Ordering.Float.IeeeOrdering with + transparent inline def plus(inline x: Float, inline y: Float): Float = x + y + transparent inline def minus(inline x: Float, inline y: Float): Float = x - y + transparent inline def times(inline x: Float, inline y: Float): Float = x * y + transparent inline def negate(inline x: Float): Float = -x + transparent inline def fromInt(inline x: Int): Float = x.toFloat + transparent inline def parseString(inline str: String): Option[Float] = str.toFloatOption + + transparent inline def div(inline x: Float, inline y: Float): Float = x / y + protected transparent inline def isNaN(inline x: Float): Boolean = x.isNaN + protected transparent inline def isNegZero(inline x: Float): Boolean = x.equals(-0f) + + extension (inline x: Float) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x + transparent inline def toDouble: Double = x.toDouble + +given DoubleIsInlineFractional: InlineFractional[Double] with Ordering.Double.IeeeOrdering with + transparent inline def plus(inline x: Double, inline y: Double): Double = x + y + transparent inline def minus(inline x: Double, inline y: Double): Double = x - y + transparent inline def times(inline x: Double, inline y: Double): Double = x * y + transparent inline def negate(inline x: Double): Double = -x + transparent inline def fromInt(inline x: Int): Double = x.toDouble + transparent inline def parseString(inline str: String): Option[Double] = str.toDoubleOption + + transparent inline def div(inline x: Double, inline y: Double): Double = x / y + protected transparent inline def isNaN(inline x: Double): Boolean = x.isNaN + protected transparent inline def isNegZero(inline x: Double): Boolean = x.equals(-0.0) + + extension (inline x: Double) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble + +trait BigDecimalIsConflicted extends InlineNumeric[BigDecimal] with Ordering.BigDecimalOrdering: + transparent inline def plus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x + y + transparent inline def minus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x - y + transparent inline def times(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x * y + transparent inline def negate(inline x: BigDecimal): BigDecimal = -x + transparent inline def fromInt(inline x: Int): BigDecimal = BigDecimal(x) + transparent inline def parseString(inline str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption + + extension (inline x: BigDecimal) + transparent inline def toInt: Int = x.intValue + transparent inline def toLong: Long = x.longValue + transparent inline def toFloat: Float = x.floatValue + transparent inline def toDouble: Double = x.doubleValue + +given BigDecimalIsInlineFractional: BigDecimalIsConflicted with InlineFractional[BigDecimal] with + transparent inline def div(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x / y + protected transparent inline def isNaN(inline x: BigDecimal): Boolean = false + protected transparent inline def isNegZero(inline x: BigDecimal): Boolean = false + + +given BigDecimalAsIfInlineIntegral: BigDecimalIsConflicted with InlineIntegral[BigDecimal] with + transparent inline def quot(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x quot y + transparent inline def rem(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x remainder y + +object tests: + // A generic inline operation that inlines/specializes primitive operations + inline def foo[T: InlineNumeric](inline a: T, inline b: T) = + a + b * b + + inline def div[T: InlineIntegral](inline a: T, inline b: T) = + a / b % b + + inline def div[T: InlineFractional](inline a: T, inline b: T) = + a / b + a + + inline def toInt[T: InlineNumeric](inline a: T) = + a.toInt + + inline def explicitToInt[T](inline a: T)(using n: InlineNumeric[T]) = + n.toInt(a) + + inline def sign[T: InlineNumeric](inline a: T) = + a.sign + + inline def explicitPlus[T](inline a: T, inline b: T)(using n: InlineNumeric[T]) = + n.plus(a, b) + + def test(a: Int, b: Int) = + val v1 = foo(a, b) // should be a + b * b // can check with -Xprint:inlining + val v2 = foo(a.toShort, b.toShort) // should be a + b * b + + val v3 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalAsIfInlineIntegral) // should be BigDecimal(a) quot BigDecimal(b) remainder BigDecimal(b) + val v4 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalIsInlineFractional) // should be BigDecimal(a) / BigDecimal(b) + BigDecimal(a) + + val v5 = toInt(a.toFloat) // should be a.toFloat.toInt + val v6 = toInt(a) // should be a + + val v7 = sign(a) + val v8 = sign(a.toChar) + val v9 = sign(-7F) + + val v10 = sign(BigDecimal(a))(using BigDecimalAsIfInlineIntegral) + val v11 = sign(BigDecimal(a))(using BigDecimalIsInlineFractional) // the condition with isNan() should be removed, i.e. it should be equivalent to v10 + + val v12 = explicitPlus(3, 5) // should be 8 + val v13 = explicitPlus(a, b) // should be a + b + + val v14 = explicitToInt(3.2) // should be (3.2).toInt + val v15 = explicitToInt(3) // should be 3 + val v16 = explicitToInt(a) // should be a + val v17 = explicitToInt(a.toShort) // should be a.toShort.toInt \ No newline at end of file From 4e27a3acf829d36f35e37249c52af0b4b60be5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Fri, 24 Feb 2023 10:31:38 +0100 Subject: [PATCH 279/657] Create clean test for inline Numeric Split code into separate files for clarity Reimplement Numeric and Ordering as inline Place symbols in the trait --- InlineNumeric-extendsNumeric.scala | 242 -------------------- InlineNumeric-extensionsInTrait.scala | 263 ---------------------- InlineNumeric.scala | 251 --------------------- tests/run/inline-numeric/Fractional.scala | 62 +++++ tests/run/inline-numeric/Integral.scala | 132 +++++++++++ tests/run/inline-numeric/Numeric.scala | 43 ++++ tests/run/inline-numeric/Ordering.scala | 56 +++++ tests/run/inline-numeric/test.scala | 54 +++++ 8 files changed, 347 insertions(+), 756 deletions(-) delete mode 100644 InlineNumeric-extendsNumeric.scala delete mode 100644 InlineNumeric-extensionsInTrait.scala delete mode 100644 InlineNumeric.scala create mode 100644 tests/run/inline-numeric/Fractional.scala create mode 100644 tests/run/inline-numeric/Integral.scala create mode 100644 tests/run/inline-numeric/Numeric.scala create mode 100644 tests/run/inline-numeric/Ordering.scala create mode 100644 tests/run/inline-numeric/test.scala diff --git a/InlineNumeric-extendsNumeric.scala b/InlineNumeric-extendsNumeric.scala deleted file mode 100644 index 0c0587c8c416..000000000000 --- a/InlineNumeric-extendsNumeric.scala +++ /dev/null @@ -1,242 +0,0 @@ -import scala.util.Try - -trait InlineNumeric[T] extends Numeric[T]: - transparent inline def plus(x: T, y: T): T - transparent inline def minus(x: T, y: T): T - transparent inline def times(x: T, y: T): T - transparent inline def negate(x: T): T - transparent inline def fromInt(x: Int): T - transparent inline def parseString(str: String): Option[T] - transparent inline def toInt(x: T): Int - transparent inline def toLong(x: T): Long - transparent inline def toFloat(x: T): Float - transparent inline def toDouble(x: T): Double - - // error overriding method abs in trait InlineNumeric of type (x: T): T; - // method abs in trait Numeric of type (x: T): T is not inline, cannot implement an inline method - //transparent inline def abs(x: T): T - //transparent inline def sign(x: T): T - - // Deferred inline method fromInt in trait InlineNumeric cannot be invoked - //transparent inline def zero = fromInt(0) - //transparent inline def one = fromInt(1) - -object InlineNumeric: - extension [T](x: T)(using num: InlineNumeric[T]) - transparent inline def +(y: T): T = num.plus(x, y) - transparent inline def -(y: T) = num.minus(x, y) - transparent inline def *(y: T): T = num.times(x, y) - transparent inline def unary_- = num.negate(x) - transparent inline def toInt: Int = num.toInt(x) - transparent inline def toLong: Long = num.toLong(x) - transparent inline def toFloat: Float = num.toFloat(x) - transparent inline def toDouble: Double = num.toDouble(x) - transparent inline def abs: T = num.abs(x) - transparent inline def sign: T = num.sign(x) - -trait InlineIntegral[T] extends InlineNumeric[T]: - transparent inline def quot(x: T, y: T): T - transparent inline def rem(x: T, y: T): T - -object InlineIntegral: - extension [T](lhs: T)(using int: InlineIntegral[T]) - transparent inline def /(rhs: T) = int.quot(lhs, rhs) - transparent inline def %(rhs: T) = int.rem(lhs, rhs) - transparent inline def /%(rhs: T) = (int.quot(lhs, rhs), int.rem(lhs, rhs)) - -trait InlineFractional[T] extends InlineNumeric[T]: - transparent inline def div(x: T, y: T): T - -object InlineFractional: - extension [T](lhs: T)(using frac: InlineFractional[T]) - transparent inline def /(rhs: T) = frac.div(lhs, rhs) - -given IntIsInlineIntegral: InlineIntegral[Int] with Ordering.IntOrdering with - transparent inline def plus(x: Int, y: Int): Int = x + y - transparent inline def minus(x: Int, y: Int): Int = x - y - transparent inline def times(x: Int, y: Int): Int = x * y - transparent inline def negate(x: Int): Int = -x - transparent inline def fromInt(x: Int): Int = x - transparent inline def parseString(str: String): Option[Int] = str.toIntOption - transparent inline def toInt(x: Int): Int = x - transparent inline def toLong(x: Int): Long = x.toLong - transparent inline def toFloat(x: Int): Float = x.toFloat - transparent inline def toDouble(x: Int): Double = x.toDouble - transparent override inline def abs(x: Int): Int = math.abs(x) - transparent override inline def sign(x: Int): Int = math.signum(x) - - transparent inline def quot(x: Int, y: Int): Int = x / y - transparent inline def rem(x: Int, y: Int): Int = x % y - -given BigIntIsInlineIntegral: InlineIntegral[BigInt] with Ordering.BigIntOrdering with - transparent inline def plus(x: BigInt, y: BigInt): BigInt = x + y - transparent inline def minus(x: BigInt, y: BigInt): BigInt = x - y - transparent inline def times(x: BigInt, y: BigInt): BigInt = x * y - transparent inline def negate(x: BigInt): BigInt = -x - transparent inline def fromInt(x: Int): BigInt = BigInt(x) - transparent inline def parseString(str: String): Option[BigInt] = Try(BigInt(str)).toOption - transparent inline def toInt(x: BigInt): Int = x.intValue - transparent inline def toLong(x: BigInt): Long = x.longValue - transparent inline def toFloat(x: BigInt): Float = x.floatValue - transparent inline def toDouble(x: BigInt): Double = x.doubleValue - transparent override inline def abs(x: BigInt): BigInt = x.abs - transparent override inline def sign(x: BigInt): BigInt = x.sign - - transparent inline def quot(x: BigInt, y: BigInt): BigInt = x / y - transparent inline def rem(x: BigInt, y: BigInt): BigInt = x % y - -given ShortIsInlineIntegral: InlineIntegral[Short] with Ordering.ShortOrdering with - transparent inline def plus(x: Short, y: Short): Short = (x + y).toShort - transparent inline def minus(x: Short, y: Short): Short = (x - y).toShort - transparent inline def times(x: Short, y: Short): Short = (x * y).toShort - transparent inline def negate(x: Short): Short = (-x).toShort - transparent inline def fromInt(x: Int): Short = x.toShort - transparent inline def parseString(str: String): Option[Short] = str.toShortOption - transparent inline def toInt(x: Short): Int = x.toInt - transparent inline def toLong(x: Short): Long = x.toLong - transparent inline def toFloat(x: Short): Float = x.toFloat - transparent inline def toDouble(x: Short): Double = x.toDouble - transparent override inline def abs(x: Short): Short = math.abs(x).toShort - transparent override inline def sign(x: Short): Short = math.signum(x).toShort - - transparent inline def quot(x: Short, y: Short): Short = (x / y).toShort - transparent inline def rem(x: Short, y: Short): Short = (x % y).toShort - -given ByteIsInlineIntegral: InlineIntegral[Byte] with Ordering.ByteOrdering with - transparent inline def plus(x: Byte, y: Byte): Byte = (x + y).toByte - transparent inline def minus(x: Byte, y: Byte): Byte = (x - y).toByte - transparent inline def times(x: Byte, y: Byte): Byte = (x * y).toByte - transparent inline def negate(x: Byte): Byte = (-x).toByte - transparent inline def fromInt(x: Int): Byte = x.toByte - transparent inline def parseString(str: String): Option[Byte] = str.toByteOption - transparent inline def toInt(x: Byte): Int = x.toInt - transparent inline def toLong(x: Byte): Long = x.toLong - transparent inline def toFloat(x: Byte): Float = x.toFloat - transparent inline def toDouble(x: Byte): Double = x.toDouble - transparent override inline def abs(x: Byte): Byte = math.abs(x).toByte - transparent override inline def sign(x: Byte): Byte = math.signum(x).toByte - - transparent inline def quot(x: Byte, y: Byte): Byte = (x / y).toByte - transparent inline def rem(x: Byte, y: Byte): Byte = (x % y).toByte - -given CharIsInlineIntegral: InlineIntegral[Char] with Ordering.CharOrdering with - transparent inline def plus(x: Char, y: Char): Char = (x + y).toChar - transparent inline def minus(x: Char, y: Char): Char = (x - y).toChar - transparent inline def times(x: Char, y: Char): Char = (x * y).toChar - transparent inline def negate(x: Char): Char = (-x).toChar - transparent inline def fromInt(x: Int): Char = x.toChar - transparent inline def parseString(str: String): Option[Char] = Try(str.toInt.toChar).toOption - transparent inline def toInt(x: Char): Int = x.toInt - transparent inline def toLong(x: Char): Long = x.toLong - transparent inline def toFloat(x: Char): Float = x.toFloat - transparent inline def toDouble(x: Char): Double = x.toDouble - transparent override inline def abs(x: Char): Char = math.abs(x).toChar - transparent override inline def sign(x: Char): Char = math.signum(x).toChar - - transparent inline def quot(x: Char, y: Char): Char = (x / y).toChar - transparent inline def rem(x: Char, y: Char): Char = (x % y).toChar - -given LongIsInlineIntegral: InlineIntegral[Long] with Ordering.LongOrdering with - transparent inline def plus(x: Long, y: Long): Long = x + y - transparent inline def minus(x: Long, y: Long): Long = x - y - transparent inline def times(x: Long, y: Long): Long = x * y - transparent inline def negate(x: Long): Long = -x - transparent inline def fromInt(x: Int): Long = x.toLong - transparent inline def parseString(str: String): Option[Long] = str.toLongOption - transparent inline def toInt(x: Long): Int = x.toInt - transparent inline def toLong(x: Long): Long = x - transparent inline def toFloat(x: Long): Float = x.toFloat - transparent inline def toDouble(x: Long): Double = x.toDouble - transparent override inline def abs(x: Long): Long = math.abs(x) - transparent override inline def sign(x: Long): Long = math.signum(x) - - transparent inline def quot(x: Long, y: Long): Long = (x / y).toLong - transparent inline def rem(x: Long, y: Long): Long = (x % y).toLong - -given FloatIsInlineFractional: InlineFractional[Float] with Ordering.Float.IeeeOrdering with - transparent inline def plus(x: Float, y: Float): Float = x + y - transparent inline def minus(x: Float, y: Float): Float = x - y - transparent inline def times(x: Float, y: Float): Float = x * y - transparent inline def negate(x: Float): Float = -x - transparent inline def fromInt(x: Int): Float = x.toFloat - transparent inline def parseString(str: String): Option[Float] = str.toFloatOption - transparent inline def toInt(x: Float): Int = x.toInt - transparent inline def toLong(x: Float): Long = x.toLong - transparent inline def toFloat(x: Float): Float = x - transparent inline def toDouble(x: Float): Double = x.toDouble - transparent override inline def abs(x: Float): Float = math.abs(x) - transparent override inline def sign(x: Float): Float = math.signum(x) - - transparent inline def div(x: Float, y: Float): Float = x / y - -given DoubleIsInlineFractional: InlineFractional[Double] with Ordering.Double.IeeeOrdering with - transparent inline def plus(x: Double, y: Double): Double = x + y - transparent inline def minus(x: Double, y: Double): Double = x - y - transparent inline def times(x: Double, y: Double): Double = x * y - transparent inline def negate(x: Double): Double = -x - transparent inline def fromInt(x: Int): Double = x.toDouble - transparent inline def parseString(str: String): Option[Double] = str.toDoubleOption - transparent inline def toInt(x: Double): Int = x.toInt - transparent inline def toLong(x: Double): Long = x.toLong - transparent inline def toFloat(x: Double): Float = x.toFloat - transparent inline def toDouble(x: Double): Double = x - transparent override inline def abs(x: Double): Double = math.abs(x) - transparent override inline def sign(x: Double): Double = math.signum(x) - - transparent inline def div(x: Double, y: Double): Double = x / y - -trait BigDecimalIsConflicted extends InlineNumeric[BigDecimal] with Ordering.BigDecimalOrdering: - transparent inline def plus(x: BigDecimal, y: BigDecimal): BigDecimal = x + y - transparent inline def minus(x: BigDecimal, y: BigDecimal): BigDecimal = x - y - transparent inline def times(x: BigDecimal, y: BigDecimal): BigDecimal = x * y - transparent inline def negate(x: BigDecimal): BigDecimal = -x - transparent inline def fromInt(x: Int): BigDecimal = BigDecimal(x) - transparent inline def parseString(str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption - transparent inline def toInt(x: BigDecimal): Int = x.intValue - transparent inline def toLong(x: BigDecimal): Long = x.longValue - transparent inline def toFloat(x: BigDecimal): Float = x.floatValue - transparent inline def toDouble(x: BigDecimal): Double = x.doubleValue - transparent override inline def abs(x: BigDecimal): BigDecimal = x.abs - transparent override inline def sign(x: BigDecimal): BigDecimal = x.sign - -given BigDecimalIsInlineFractional: BigDecimalIsConflicted with InlineFractional[BigDecimal] with - transparent inline def div(x: BigDecimal, y: BigDecimal): BigDecimal = x / y - -given BigDecimalAsIfInlineIntegral: BigDecimalIsConflicted with InlineIntegral[BigDecimal] with - transparent inline def quot(x: BigDecimal, y: BigDecimal): BigDecimal = x quot y - transparent inline def rem(x: BigDecimal, y: BigDecimal): BigDecimal = x remainder y - -object tests: - import InlineNumeric.* - import InlineIntegral.{/ => ¦, %} - import InlineFractional./ - - inline def foo[T: InlineNumeric](a: T, b: T) = - a + b * b - - inline def div[T: InlineIntegral](a: T, b: T) = - a ¦ b % b - - inline def div[T: InlineFractional](a: T, b: T) = - a / b + a - - inline def bar[T: InlineNumeric](a: T) = a.toInt - - inline def sign[T: InlineNumeric](a: T) = a.sign - - def test(a: Int, b: Int) = - val v1 = foo(a, b) // should be a + b * b // can check with -Xprint:inlining - val v2 = foo(a.toShort, b.toShort) // should be a + b * b - - val v3 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalAsIfInlineIntegral) // should be BigDecimal(a) quot BigDecimal(b) remainder BigDecimal(b) - val v4 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalIsInlineFractional) // should be BigDecimal(a) / BigDecimal(b) + BigDecimal(a) - - val v5 = bar(a.toFloat) // should be a.toFloat.toInt - val v6 = bar(a) // should be a - - val v7 = sign(a) - val v8 = sign(a.toChar) - val v9 = sign(-7F) - - val v10 = sign(BigDecimal(a))(using BigDecimalAsIfInlineIntegral) - val v11 = sign(BigDecimal(a))(using BigDecimalIsInlineFractional) // the condition with isNan() should be removed, i.e. it should be equivalent to v10 \ No newline at end of file diff --git a/InlineNumeric-extensionsInTrait.scala b/InlineNumeric-extensionsInTrait.scala deleted file mode 100644 index bac390783e65..000000000000 --- a/InlineNumeric-extensionsInTrait.scala +++ /dev/null @@ -1,263 +0,0 @@ -import scala.util.Try - -trait InlineNumeric[T] extends Ordering[T]: // extends Numeric[T] // TODO can we do this? - transparent inline def plus(inline x: T, inline y: T): T - transparent inline def minus(inline x: T, inline y: T): T - transparent inline def times(inline x: T, inline y: T): T - transparent inline def negate(inline x: T): T - transparent inline def fromInt(inline x: Int): T - transparent inline def parseString(inline str: String): Option[T] - - transparent inline def zero = fromInt(0) - transparent inline def one = fromInt(1) - - extension (inline x: T) - transparent inline def +(inline y: T): T = plus(x, y) - transparent inline def -(inline y: T) = minus(x, y) - transparent inline def *(inline y: T): T = times(x, y) - transparent inline def unary_- = negate(x) - transparent inline def toInt: Int - transparent inline def toLong: Long - transparent inline def toFloat: Float - transparent inline def toDouble: Double - transparent inline def abs: T - transparent inline def sign: T - -trait InlineIntegral[T] extends InlineNumeric[T]: - transparent inline def quot(inline x: T, inline y: T): T - transparent inline def rem(inline x: T, inline y: T): T - - extension (inline lhs: T) - transparent inline def /(inline rhs: T) = quot(lhs, rhs) - transparent inline def %(inline rhs: T) = rem(lhs, rhs) - transparent inline def /%(inline rhs: T) = (quot(lhs, rhs), rem(lhs, rhs)) - - transparent inline def abs: T = if lt(lhs, zero) then negate(lhs) else lhs - transparent inline def sign: T = - if lt(lhs, zero) then negate(one) - else if gt(lhs, zero) then one - else zero - -trait InlineFractional[T] extends InlineNumeric[T]: - transparent inline def div(inline x: T, inline y: T): T - protected transparent inline def isNaN(inline x: T): Boolean - protected transparent inline def isNegZero(inline x: T): Boolean - - extension (inline lhs: T) - transparent inline def /(inline rhs: T) = div(lhs, rhs) - transparent inline def abs: T = if lt(lhs, zero) || isNegZero(lhs) then negate(lhs) else lhs - transparent inline def sign: T = - if isNaN(lhs) || isNegZero(lhs) then lhs - else if lt(lhs, zero) then negate(one) - else if gt(lhs, zero) then one - else zero - -given IntIsInlineIntegral: InlineIntegral[Int] with Ordering.IntOrdering with - transparent inline def plus(inline x: Int, inline y: Int): Int = x + y - transparent inline def minus(inline x: Int, inline y: Int): Int = x - y - transparent inline def times(inline x: Int, inline y: Int): Int = x * y - transparent inline def negate(inline x: Int): Int = -x - transparent inline def fromInt(inline x: Int): Int = x - transparent inline def parseString(inline str: String): Option[Int] = str.toIntOption - - transparent inline def quot(inline x: Int, inline y: Int): Int = x / y - transparent inline def rem(inline x: Int, inline y: Int): Int = x % y - - extension (inline x: Int) - transparent inline def toInt: Int = x - transparent inline def toLong: Long = x.toLong - transparent inline def toFloat: Float = x.toFloat - transparent inline def toDouble: Double = x.toDouble - -given BigIntIsInlineIntegral: InlineIntegral[BigInt] with Ordering.BigIntOrdering with - transparent inline def plus(inline x: BigInt, inline y: BigInt): BigInt = x + y - transparent inline def minus(inline x: BigInt, inline y: BigInt): BigInt = x - y - transparent inline def times(inline x: BigInt, inline y: BigInt): BigInt = x * y - transparent inline def negate(inline x: BigInt): BigInt = -x - transparent inline def fromInt(inline x: Int): BigInt = BigInt(x) - transparent inline def parseString(inline str: String): Option[BigInt] = Try(BigInt(str)).toOption - - transparent inline def quot(inline x: BigInt, inline y: BigInt): BigInt = x / y - transparent inline def rem(inline x: BigInt, inline y: BigInt): BigInt = x % y - - extension (inline x: BigInt) - transparent inline def toInt: Int = x.intValue - transparent inline def toLong: Long = x.longValue - transparent inline def toFloat: Float = x.floatValue - transparent inline def toDouble: Double = x.doubleValue - -given ShortIsInlineIntegral: InlineIntegral[Short] with Ordering.ShortOrdering with - transparent inline def plus(inline x: Short, inline y: Short): Short = (x + y).toShort - transparent inline def minus(inline x: Short, inline y: Short): Short = (x - y).toShort - transparent inline def times(inline x: Short, inline y: Short): Short = (x * y).toShort - transparent inline def negate(inline x: Short): Short = (-x).toShort - transparent inline def fromInt(inline x: Int): Short = x.toShort - transparent inline def parseString(inline str: String): Option[Short] = str.toShortOption - - transparent inline def quot(inline x: Short, inline y: Short): Short = (x / y).toShort - transparent inline def rem(inline x: Short, inline y: Short): Short = (x % y).toShort - - extension (inline x: Short) - transparent inline def toInt: Int = x.toInt - transparent inline def toLong: Long = x.toLong - transparent inline def toFloat: Float = x.toFloat - transparent inline def toDouble: Double = x.toDouble - -given ByteIsInlineIntegral: InlineIntegral[Byte] with Ordering.ByteOrdering with - transparent inline def plus(inline x: Byte, inline y: Byte): Byte = (x + y).toByte - transparent inline def minus(inline x: Byte, inline y: Byte): Byte = (x - y).toByte - transparent inline def times(inline x: Byte, inline y: Byte): Byte = (x * y).toByte - transparent inline def negate(inline x: Byte): Byte = (-x).toByte - transparent inline def fromInt(inline x: Int): Byte = x.toByte - transparent inline def parseString(inline str: String): Option[Byte] = str.toByteOption - - transparent inline def quot(inline x: Byte, inline y: Byte): Byte = (x / y).toByte - transparent inline def rem(inline x: Byte, inline y: Byte): Byte = (x % y).toByte - - extension (inline x: Byte) - transparent inline def toInt: Int = x.toInt - transparent inline def toLong: Long = x.toLong - transparent inline def toFloat: Float = x.toFloat - transparent inline def toDouble: Double = x.toDouble - -given CharIsInlineIntegral: InlineIntegral[Char] with Ordering.CharOrdering with - transparent inline def plus(inline x: Char, inline y: Char): Char = (x + y).toChar - transparent inline def minus(inline x: Char, inline y: Char): Char = (x - y).toChar - transparent inline def times(inline x: Char, inline y: Char): Char = (x * y).toChar - transparent inline def negate(inline x: Char): Char = (-x).toChar - transparent inline def fromInt(inline x: Int): Char = x.toChar - transparent inline def parseString(inline str: String): Option[Char] = Try(str.toInt.toChar).toOption - - transparent inline def quot(inline x: Char, inline y: Char): Char = (x / y).toChar - transparent inline def rem(inline x: Char, inline y: Char): Char = (x % y).toChar - - extension (inline x: Char) - transparent inline def toInt: Int = x.toInt - transparent inline def toLong: Long = x.toLong - transparent inline def toFloat: Float = x.toFloat - transparent inline def toDouble: Double = x.toDouble - -given LongIsInlineIntegral: InlineIntegral[Long] with Ordering.LongOrdering with - transparent inline def plus(inline x: Long, inline y: Long): Long = x + y - transparent inline def minus(inline x: Long, inline y: Long): Long = x - y - transparent inline def times(inline x: Long, inline y: Long): Long = x * y - transparent inline def negate(inline x: Long): Long = -x - transparent inline def fromInt(inline x: Int): Long = x.toLong - transparent inline def parseString(inline str: String): Option[Long] = str.toLongOption - - transparent inline def quot(inline x: Long, inline y: Long): Long = (x / y).toLong - transparent inline def rem(inline x: Long, inline y: Long): Long = (x % y).toLong - - extension (inline x: Long) - transparent inline def toInt: Int = x.toInt - transparent inline def toLong: Long = x - transparent inline def toFloat: Float = x.toFloat - transparent inline def toDouble: Double = x.toDouble - -given FloatIsInlineFractional: InlineFractional[Float] with Ordering.Float.IeeeOrdering with - transparent inline def plus(inline x: Float, inline y: Float): Float = x + y - transparent inline def minus(inline x: Float, inline y: Float): Float = x - y - transparent inline def times(inline x: Float, inline y: Float): Float = x * y - transparent inline def negate(inline x: Float): Float = -x - transparent inline def fromInt(inline x: Int): Float = x.toFloat - transparent inline def parseString(inline str: String): Option[Float] = str.toFloatOption - - transparent inline def div(inline x: Float, inline y: Float): Float = x / y - protected transparent inline def isNaN(inline x: Float): Boolean = x.isNaN - protected transparent inline def isNegZero(inline x: Float): Boolean = x.equals(-0f) - - extension (inline x: Float) - transparent inline def toInt: Int = x.toInt - transparent inline def toLong: Long = x.toLong - transparent inline def toFloat: Float = x - transparent inline def toDouble: Double = x.toDouble - -given DoubleIsInlineFractional: InlineFractional[Double] with Ordering.Double.IeeeOrdering with - transparent inline def plus(inline x: Double, inline y: Double): Double = x + y - transparent inline def minus(inline x: Double, inline y: Double): Double = x - y - transparent inline def times(inline x: Double, inline y: Double): Double = x * y - transparent inline def negate(inline x: Double): Double = -x - transparent inline def fromInt(inline x: Int): Double = x.toDouble - transparent inline def parseString(inline str: String): Option[Double] = str.toDoubleOption - - transparent inline def div(inline x: Double, inline y: Double): Double = x / y - protected transparent inline def isNaN(inline x: Double): Boolean = x.isNaN - protected transparent inline def isNegZero(inline x: Double): Boolean = x.equals(-0.0) - - extension (inline x: Double) - transparent inline def toInt: Int = x.toInt - transparent inline def toLong: Long = x.toLong - transparent inline def toFloat: Float = x.toFloat - transparent inline def toDouble: Double = x.toDouble - -trait BigDecimalIsConflicted extends InlineNumeric[BigDecimal] with Ordering.BigDecimalOrdering: - transparent inline def plus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x + y - transparent inline def minus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x - y - transparent inline def times(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x * y - transparent inline def negate(inline x: BigDecimal): BigDecimal = -x - transparent inline def fromInt(inline x: Int): BigDecimal = BigDecimal(x) - transparent inline def parseString(inline str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption - - extension (inline x: BigDecimal) - transparent inline def toInt: Int = x.intValue - transparent inline def toLong: Long = x.longValue - transparent inline def toFloat: Float = x.floatValue - transparent inline def toDouble: Double = x.doubleValue - -given BigDecimalIsInlineFractional: BigDecimalIsConflicted with InlineFractional[BigDecimal] with - transparent inline def div(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x / y - protected transparent inline def isNaN(inline x: BigDecimal): Boolean = false - protected transparent inline def isNegZero(inline x: BigDecimal): Boolean = false - - -given BigDecimalAsIfInlineIntegral: BigDecimalIsConflicted with InlineIntegral[BigDecimal] with - transparent inline def quot(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x quot y - transparent inline def rem(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x remainder y - -object tests: - // A generic inline operation that inlines/specializes primitive operations - inline def foo[T: InlineNumeric](inline a: T, inline b: T) = - a + b * b - - inline def div[T: InlineIntegral](inline a: T, inline b: T) = - a / b % b - - inline def div[T: InlineFractional](inline a: T, inline b: T) = - a / b + a - - inline def toInt[T: InlineNumeric](inline a: T) = - a.toInt - - inline def explicitToInt[T](inline a: T)(using n: InlineNumeric[T]) = - n.toInt(a) - - inline def sign[T: InlineNumeric](inline a: T) = - a.sign - - inline def explicitPlus[T](inline a: T, inline b: T)(using n: InlineNumeric[T]) = - n.plus(a, b) - - def test(a: Int, b: Int) = - val v1 = foo(a, b) // should be a + b * b // can check with -Xprint:inlining - val v2 = foo(a.toShort, b.toShort) // should be a + b * b - - val v3 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalAsIfInlineIntegral) // should be BigDecimal(a) quot BigDecimal(b) remainder BigDecimal(b) - val v4 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalIsInlineFractional) // should be BigDecimal(a) / BigDecimal(b) + BigDecimal(a) - - val v5 = toInt(a.toFloat) // should be a.toFloat.toInt - val v6 = toInt(a) // should be a - - val v7 = sign(a) - val v8 = sign(a.toChar) - val v9 = sign(-7F) - - val v10 = sign(BigDecimal(a))(using BigDecimalAsIfInlineIntegral) - val v11 = sign(BigDecimal(a))(using BigDecimalIsInlineFractional) // the condition with isNan() should be removed, i.e. it should be equivalent to v10 - - val v12 = explicitPlus(3, 5) // should be 8 - val v13 = explicitPlus(a, b) // should be a + b - - val v14 = explicitToInt(3.2) // should be (3.2).toInt - val v15 = explicitToInt(3) // should be 3 - val v16 = explicitToInt(a) // should be a - val v17 = explicitToInt(a.toShort) // should be a.toShort.toInt \ No newline at end of file diff --git a/InlineNumeric.scala b/InlineNumeric.scala deleted file mode 100644 index 265343dfc793..000000000000 --- a/InlineNumeric.scala +++ /dev/null @@ -1,251 +0,0 @@ -// community-build/community-projects/stdLib213/src/library/scala/math/Numeric.scala -// community-build/community-projects/stdLib213/src/library/scala/math/Integral.scala -// community-build/community-projects/stdLib213/src/library/scala/math/Fractional.scala - -import scala.util.Try - -trait InlineNumeric[T] extends Ordering[T]: // extends Numeric[T] // TODO can we do this? - transparent inline def plus(inline x: T, inline y: T): T - transparent inline def minus(inline x: T, inline y: T): T - transparent inline def times(inline x: T, inline y: T): T - transparent inline def negate(inline x: T): T - transparent inline def fromInt(inline x: Int): T - transparent inline def parseString(inline str: String): Option[T] - transparent inline def toInt(inline x: T): Int - transparent inline def toLong(inline x: T): Long - transparent inline def toFloat(inline x: T): Float - transparent inline def toDouble(inline x: T): Double - transparent inline def abs(inline x: T): T - transparent inline def sign(inline x: T): T - - transparent inline def zero = fromInt(0) - transparent inline def one = fromInt(1) - -object InlineNumeric: - extension [T](inline x: T)(using inline num: InlineNumeric[T]) - transparent inline def +(inline y: T): T = num.plus(x, y) - transparent inline def -(inline y: T) = num.minus(x, y) - transparent inline def *(inline y: T): T = num.times(x, y) - transparent inline def unary_- = num.negate(x) - transparent inline def toInt: Int = num.toInt(x) - transparent inline def toLong: Long = num.toLong(x) - transparent inline def toFloat: Float = num.toFloat(x) - transparent inline def toDouble: Double = num.toDouble(x) - transparent inline def abs: T = num.abs(x) - transparent inline def sign: T = num.sign(x) - -trait InlineIntegral[T] extends InlineNumeric[T]: - transparent inline def abs(inline x: T): T = if lt(x, zero) then negate(x) else x - transparent inline def sign(inline x: T): T = - if lt(x, zero) then negate(one) - else if gt(x, zero) then one - else zero - - transparent inline def quot(inline x: T, inline y: T): T - transparent inline def rem(inline x: T, inline y: T): T - -object InlineIntegral: - // TODO: how are these imported/composed with Numeric/Integral. Should the extension methods be defined in trait InlineIntegral? - extension [T](inline lhs: T)(using inline int: InlineIntegral[T]) - transparent inline def /(inline rhs: T) = int.quot(lhs, rhs) - transparent inline def %(inline rhs: T) = int.rem(lhs, rhs) - transparent inline def /%(inline rhs: T) = (int.quot(lhs, rhs), int.rem(lhs, rhs)) - -trait InlineFractional[T] extends InlineNumeric[T]: - transparent inline def abs(inline x: T): T = if lt(x, zero) || isNegZero(x) then negate(x) else x - transparent inline def sign(inline x: T): T = - if isNaN(x) || isNegZero(x) then x - else if lt(x, zero) then negate(one) - else if gt(x, zero) then one - else zero - - transparent inline def div(inline x: T, inline y: T): T - protected transparent inline def isNaN(inline x: T): Boolean - protected transparent inline def isNegZero(inline x: T): Boolean - -object InlineFractional: - // TODO: how are these imported/composed with Numeric/Fractional. Should the extension methods be defined in trait InlineFractional? - extension [T](inline lhs: T)(using inline frac: InlineFractional[T]) - transparent inline def /(inline rhs: T) = frac.div(lhs, rhs) - -given IntIsInlineIntegral: InlineIntegral[Int] with Ordering.IntOrdering with - transparent inline def plus(inline x: Int, inline y: Int): Int = x + y - transparent inline def minus(inline x: Int, inline y: Int): Int = x - y - transparent inline def times(inline x: Int, inline y: Int): Int = x * y - transparent inline def negate(inline x: Int): Int = -x - transparent inline def fromInt(inline x: Int): Int = x - transparent inline def parseString(inline str: String): Option[Int] = str.toIntOption - transparent inline def toInt(inline x: Int): Int = x - transparent inline def toLong(inline x: Int): Long = x.toLong - transparent inline def toFloat(inline x: Int): Float = x.toFloat - transparent inline def toDouble(inline x: Int): Double = x.toDouble - - transparent inline def quot(inline x: Int, inline y: Int): Int = x / y - transparent inline def rem(inline x: Int, inline y: Int): Int = x % y - -given BigIntIsInlineIntegral: InlineIntegral[BigInt] with Ordering.BigIntOrdering with - transparent inline def plus(inline x: BigInt, inline y: BigInt): BigInt = x + y - transparent inline def minus(inline x: BigInt, inline y: BigInt): BigInt = x - y - transparent inline def times(inline x: BigInt, inline y: BigInt): BigInt = x * y - transparent inline def negate(inline x: BigInt): BigInt = -x - transparent inline def fromInt(inline x: Int): BigInt = BigInt(x) - transparent inline def parseString(inline str: String): Option[BigInt] = Try(BigInt(str)).toOption - transparent inline def toInt(inline x: BigInt): Int = x.intValue - transparent inline def toLong(inline x: BigInt): Long = x.longValue - transparent inline def toFloat(inline x: BigInt): Float = x.floatValue - transparent inline def toDouble(inline x: BigInt): Double = x.doubleValue - - transparent inline def quot(inline x: BigInt, inline y: BigInt): BigInt = x / y - transparent inline def rem(inline x: BigInt, inline y: BigInt): BigInt = x % y - -given ShortIsInlineIntegral: InlineIntegral[Short] with Ordering.ShortOrdering with - transparent inline def plus(inline x: Short, inline y: Short): Short = (x + y).toShort - transparent inline def minus(inline x: Short, inline y: Short): Short = (x - y).toShort - transparent inline def times(inline x: Short, inline y: Short): Short = (x * y).toShort - transparent inline def negate(inline x: Short): Short = (-x).toShort - transparent inline def fromInt(inline x: Int): Short = x.toShort - transparent inline def parseString(inline str: String): Option[Short] = str.toShortOption - transparent inline def toInt(inline x: Short): Int = x.toInt - transparent inline def toLong(inline x: Short): Long = x.toLong - transparent inline def toFloat(inline x: Short): Float = x.toFloat - transparent inline def toDouble(inline x: Short): Double = x.toDouble - - transparent inline def quot(inline x: Short, inline y: Short): Short = (x / y).toShort - transparent inline def rem(inline x: Short, inline y: Short): Short = (x % y).toShort - -given ByteIsInlineIntegral: InlineIntegral[Byte] with Ordering.ByteOrdering with - transparent inline def plus(inline x: Byte, inline y: Byte): Byte = (x + y).toByte - transparent inline def minus(inline x: Byte, inline y: Byte): Byte = (x - y).toByte - transparent inline def times(inline x: Byte, inline y: Byte): Byte = (x * y).toByte - transparent inline def negate(inline x: Byte): Byte = (-x).toByte - transparent inline def fromInt(inline x: Int): Byte = x.toByte - transparent inline def parseString(inline str: String): Option[Byte] = str.toByteOption - transparent inline def toInt(inline x: Byte): Int = x.toInt - transparent inline def toLong(inline x: Byte): Long = x.toLong - transparent inline def toFloat(inline x: Byte): Float = x.toFloat - transparent inline def toDouble(inline x: Byte): Double = x.toDouble - - transparent inline def quot(inline x: Byte, inline y: Byte): Byte = (x / y).toByte - transparent inline def rem(inline x: Byte, inline y: Byte): Byte = (x % y).toByte - -given CharIsInlineIntegral: InlineIntegral[Char] with Ordering.CharOrdering with - transparent inline def plus(inline x: Char, inline y: Char): Char = (x + y).toChar - transparent inline def minus(inline x: Char, inline y: Char): Char = (x - y).toChar - transparent inline def times(inline x: Char, inline y: Char): Char = (x * y).toChar - transparent inline def negate(inline x: Char): Char = (-x).toChar - transparent inline def fromInt(inline x: Int): Char = x.toChar - transparent inline def parseString(inline str: String): Option[Char] = Try(str.toInt.toChar).toOption - transparent inline def toInt(inline x: Char): Int = x.toInt - transparent inline def toLong(inline x: Char): Long = x.toLong - transparent inline def toFloat(inline x: Char): Float = x.toFloat - transparent inline def toDouble(inline x: Char): Double = x.toDouble - - transparent inline def quot(inline x: Char, inline y: Char): Char = (x / y).toChar - transparent inline def rem(inline x: Char, inline y: Char): Char = (x % y).toChar - -given LongIsInlineIntegral: InlineIntegral[Long] with Ordering.LongOrdering with - transparent inline def plus(inline x: Long, inline y: Long): Long = x + y - transparent inline def minus(inline x: Long, inline y: Long): Long = x - y - transparent inline def times(inline x: Long, inline y: Long): Long = x * y - transparent inline def negate(inline x: Long): Long = -x - transparent inline def fromInt(inline x: Int): Long = x.toLong - transparent inline def parseString(inline str: String): Option[Long] = str.toLongOption - transparent inline def toInt(inline x: Long): Int = x.toInt - transparent inline def toLong(inline x: Long): Long = x - transparent inline def toFloat(inline x: Long): Float = x.toFloat - transparent inline def toDouble(inline x: Long): Double = x.toDouble - - transparent inline def quot(inline x: Long, inline y: Long): Long = (x / y).toLong - transparent inline def rem(inline x: Long, inline y: Long): Long = (x % y).toLong - -given FloatIsInlineFractional: InlineFractional[Float] with Ordering.Float.IeeeOrdering with - transparent inline def plus(inline x: Float, inline y: Float): Float = x + y - transparent inline def minus(inline x: Float, inline y: Float): Float = x - y - transparent inline def times(inline x: Float, inline y: Float): Float = x * y - transparent inline def negate(inline x: Float): Float = -x - transparent inline def fromInt(inline x: Int): Float = x.toFloat - transparent inline def parseString(inline str: String): Option[Float] = str.toFloatOption - transparent inline def toInt(inline x: Float): Int = x.toInt - transparent inline def toLong(inline x: Float): Long = x.toLong - transparent inline def toFloat(inline x: Float): Float = x - transparent inline def toDouble(inline x: Float): Double = x.toDouble - - transparent inline def div(inline x: Float, inline y: Float): Float = x / y - protected transparent inline def isNaN(inline x: Float): Boolean = x.isNaN - protected transparent inline def isNegZero(inline x: Float): Boolean = x.equals(-0f) - -given DoubleIsInlineFractional: InlineFractional[Double] with Ordering.Double.IeeeOrdering with - transparent inline def plus(inline x: Double, inline y: Double): Double = x + y - transparent inline def minus(inline x: Double, inline y: Double): Double = x - y - transparent inline def times(inline x: Double, inline y: Double): Double = x * y - transparent inline def negate(inline x: Double): Double = -x - transparent inline def fromInt(inline x: Int): Double = x.toDouble - transparent inline def parseString(inline str: String): Option[Double] = str.toDoubleOption - transparent inline def toInt(inline x: Double): Int = x.toInt - transparent inline def toLong(inline x: Double): Long = x.toLong - transparent inline def toFloat(inline x: Double): Float = x.toFloat - transparent inline def toDouble(inline x: Double): Double = x - - transparent inline def div(inline x: Double, inline y: Double): Double = x / y - protected transparent inline def isNaN(inline x: Double): Boolean = x.isNaN - protected transparent inline def isNegZero(inline x: Double): Boolean = x.equals(-0.0) - -trait BigDecimalIsConflicted extends InlineNumeric[BigDecimal] with Ordering.BigDecimalOrdering: - transparent inline def plus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x + y - transparent inline def minus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x - y - transparent inline def times(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x * y - transparent inline def negate(inline x: BigDecimal): BigDecimal = -x - transparent inline def fromInt(inline x: Int): BigDecimal = BigDecimal(x) - transparent inline def parseString(inline str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption - transparent inline def toInt(inline x: BigDecimal): Int = x.intValue - transparent inline def toLong(inline x: BigDecimal): Long = x.longValue - transparent inline def toFloat(inline x: BigDecimal): Float = x.floatValue - transparent inline def toDouble(inline x: BigDecimal): Double = x.doubleValue - -given BigDecimalIsInlineFractional: BigDecimalIsConflicted with InlineFractional[BigDecimal] with - transparent inline def div(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x / y - protected transparent inline def isNaN(inline x: BigDecimal): Boolean = false - protected transparent inline def isNegZero(inline x: BigDecimal): Boolean = false - - -given BigDecimalAsIfInlineIntegral: BigDecimalIsConflicted with InlineIntegral[BigDecimal] with - transparent inline def quot(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x quot y - transparent inline def rem(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x remainder y - -object tests: - import InlineNumeric.* - import InlineIntegral.{/ => ¦, %} - import InlineFractional.{/} - - // A generic inline operation that inlines/specializes primitive operations - inline def foo[T: InlineNumeric](inline a: T, inline b: T) = - a + b * b - - inline def div[T: InlineIntegral](inline a: T, inline b: T) = - a ¦ b % b - - inline def div[T: InlineFractional](inline a: T, inline b: T) = - a / b + a - - inline def bar[T: InlineNumeric](inline a: T) = - a.toInt - - inline def sign[T: InlineNumeric](inline a: T) = - a.sign - - def test(a: Int, b: Int) = - val v1 = foo(a, b) // should be a + b * b // can check with -Xprint:inlining - val v2 = foo(a.toShort, b.toShort) // should be a + b * b - - val v3 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalAsIfInlineIntegral) // should be BigDecimal(a) quot BigDecimal(b) remainder BigDecimal(b) - val v4 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalIsInlineFractional) // should be BigDecimal(a) / BigDecimal(b) + BigDecimal(a) - - val v5 = bar(a.toFloat) // should be a.toFloat.toInt - val v6 = bar(a) // should be a - - val v7 = sign(a) - val v8 = sign(a.toChar) - val v9 = sign(-7F) - - val v10 = sign(BigDecimal(a))(using BigDecimalAsIfInlineIntegral) - val v11 = sign(BigDecimal(a))(using BigDecimalIsInlineFractional) // the condition with isNan() should be removed, i.e. it should be equivalent to v10 \ No newline at end of file diff --git a/tests/run/inline-numeric/Fractional.scala b/tests/run/inline-numeric/Fractional.scala new file mode 100644 index 000000000000..a61c60063fd9 --- /dev/null +++ b/tests/run/inline-numeric/Fractional.scala @@ -0,0 +1,62 @@ +package scala.math +package inline + +trait Fractional[T] extends Numeric[T]: + transparent inline def div(inline x: T, inline y: T): T + protected transparent inline def isNaN(inline x: T): Boolean + protected transparent inline def isNegZero(inline x: T): Boolean + + extension (inline x: T) + transparent inline def abs: T = + if lt(x, zero) || isNegZero(x) then negate(x) else x + transparent inline def sign: T = + if isNaN(x) || isNegZero(x) then x + else if lt(x, zero) then negate(one) + else if gt(x, zero) then one + else zero + transparent inline def /(inline y: T) = div(x, y) + +object Fractional: + given BigDecimalIsFractional: BigDecimalIsConflicted with Fractional[BigDecimal] with + transparent inline def div(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x / y + + protected transparent inline def isNaN(inline x: BigDecimal): Boolean = false + protected transparent inline def isNegZero(inline x: BigDecimal): Boolean = false + + given DoubleIsFractional: Fractional[Double] with Ordering.DoubleIeeeOrdering with + transparent inline def plus(inline x: Double, inline y: Double): Double = x + y + transparent inline def minus(inline x: Double, inline y: Double): Double = x - y + transparent inline def times(inline x: Double, inline y: Double): Double = x * y + transparent inline def div(inline x: Double, inline y: Double): Double = x / y + transparent inline def negate(inline x: Double): Double = -x + + transparent inline def fromInt(x: Int): Double = x.toDouble + def parseString(str: String): Option[Double] = str.toDoubleOption + + protected transparent inline def isNaN(inline x: Double): Boolean = x.isNaN + protected transparent inline def isNegZero(inline x: Double): Boolean = x.equals(-0.0) + + extension (inline x: Double) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x + + given FloatIsFractional: Fractional[Float] with Ordering.FloatIeeeOrdering with + transparent inline def plus(inline x: Float, inline y: Float): Float = x + y + transparent inline def minus(inline x: Float, inline y: Float): Float = x - y + transparent inline def times(inline x: Float, inline y: Float): Float = x * y + transparent inline def div(inline x: Float, inline y: Float): Float = x / y + transparent inline def negate(inline x: Float): Float = -x + + transparent inline def fromInt(x: Int): Float = x.toFloat + def parseString(str: String): Option[Float] = str.toFloatOption + + protected transparent inline def isNaN(inline x: Float): Boolean = x.isNaN + protected transparent inline def isNegZero(inline x: Float): Boolean = x.equals(-0f) + + extension (inline x: Float) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x + transparent inline def toDouble: Double = x.toDouble \ No newline at end of file diff --git a/tests/run/inline-numeric/Integral.scala b/tests/run/inline-numeric/Integral.scala new file mode 100644 index 000000000000..4ef11738113c --- /dev/null +++ b/tests/run/inline-numeric/Integral.scala @@ -0,0 +1,132 @@ +package scala.math +package inline + +import scala.util.Try + +trait Integral[T] extends Numeric[T]: + inline def quot(inline x: T, inline y: T): T + inline def rem(inline x: T, inline y: T): T + + extension (inline x: T) + transparent inline def abs: T = + if lt(x, zero) then negate(x) else x + transparent inline def sign: T = + if lt(x, zero) then negate(one) + else if gt(x, zero) then one + else zero + transparent inline def /(inline y: T) = quot(x, y) + transparent inline def %(inline y: T) = rem(x, y) + transparent inline def /%(inline y: T) = (quot(x, y), rem(x, y)) + +object Integral: + given BigDecimalAsIfIntegral: Integral[BigDecimal] with BigDecimalIsConflicted with + transparent inline def quot(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x quot y + transparent inline def rem(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x remainder y + + given BigIntIsIntegral: Integral[BigInt] with Ordering.BigIntOrdering with + transparent inline def plus(inline x: BigInt, inline y: BigInt): BigInt = x + y + transparent inline def minus(inline x: BigInt, inline y: BigInt): BigInt = x - y + transparent inline def times(inline x: BigInt, inline y: BigInt): BigInt = x * y + transparent inline def negate(inline x: BigInt): BigInt = -x + + extension (inline x: BigInt) + transparent inline def toInt: Int = x.intValue + transparent inline def toLong: Long = x.longValue + transparent inline def toFloat: Float = x.floatValue + transparent inline def toDouble: Double = x.doubleValue + + transparent inline def fromInt(x: Int): BigInt = BigInt(x) + def parseString(str: String): Option[BigInt] = Try(BigInt(str)).toOption + + transparent inline def quot(inline x: BigInt, inline y: BigInt): BigInt = x / y + transparent inline def rem(inline x: BigInt, inline y: BigInt): BigInt = x % y + + given ByteIsIntegral: Integral[Byte] with Ordering.ByteOrdering with + transparent inline def plus(inline x: Byte, inline y: Byte): Byte = (x + y).toByte + transparent inline def minus(inline x: Byte, inline y: Byte): Byte = (x - y).toByte + transparent inline def times(inline x: Byte, inline y: Byte): Byte = (x * y).toByte + transparent inline def negate(inline x: Byte): Byte = (-x).toByte + + transparent inline def fromInt(x: Int): Byte = x.toByte + def parseString(str: String): Option[Byte] = str.toByteOption + + transparent inline def quot(inline x: Byte, inline y: Byte): Byte = (x / y).toByte + transparent inline def rem(inline x: Byte, inline y: Byte): Byte = (x % y).toByte + + extension (inline x: Byte) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble + + given CharIsIntegral: Integral[Char] with Ordering.CharOrdering with + transparent inline def plus(inline x: Char, inline y: Char): Char = (x + y).toChar + transparent inline def minus(inline x: Char, inline y: Char): Char = (x - y).toChar + transparent inline def times(inline x: Char, inline y: Char): Char = (x * y).toChar + transparent inline def negate(inline x: Char): Char = (-x).toChar + + transparent inline def fromInt(x: Int): Char = x.toChar + def parseString(str: String): Option[Char] = Try(str.toInt.toChar).toOption + + transparent inline def quot(inline x: Char, inline y: Char): Char = (x / y).toChar + transparent inline def rem(inline x: Char, inline y: Char): Char = (x % y).toChar + + extension (inline x: Char) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble + + given IntIsIntegral: Integral[Int] with Ordering.IntOrdering with + transparent inline def plus(inline x: Int, inline y: Int): Int = x + y + transparent inline def minus(inline x: Int, inline y: Int): Int = x - y + transparent inline def times(inline x: Int, inline y: Int): Int = x * y + transparent inline def negate(inline x: Int): Int = -x + + transparent inline def fromInt(x: Int): Int = x + def parseString(str: String): Option[Int] = str.toIntOption + + transparent inline def quot(inline x: Int, inline y: Int): Int = x / y + transparent inline def rem(inline x: Int, inline y: Int): Int = x % y + + extension (inline x: Int) + transparent inline def toInt: Int = x + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble + + given LongIsIntegral: Integral[Long] with Ordering.LongOrdering with + transparent inline def plus(inline x: Long, inline y: Long): Long = x + y + transparent inline def minus(inline x: Long, inline y: Long): Long = x - y + transparent inline def times(inline x: Long, inline y: Long): Long = x * y + transparent inline def negate(inline x: Long): Long = -x + + transparent inline def fromInt(x: Int): Long = x.toLong + def parseString(str: String): Option[Long] = str.toLongOption + + transparent inline def quot(inline x: Long, inline y: Long): Long = (x / y).toLong + transparent inline def rem(inline x: Long, inline y: Long): Long = (x % y).toLong + + extension (inline x: Long) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble + + given ShortIsIntegral: Integral[Short] with Ordering.ShortOrdering with + transparent inline def plus(inline x: Short, inline y: Short): Short = (x + y).toShort + transparent inline def minus(inline x: Short, inline y: Short): Short = (x - y).toShort + transparent inline def times(inline x: Short, inline y: Short): Short = (x * y).toShort + transparent inline def negate(inline x: Short): Short = (-x).toShort + + transparent inline def fromInt(x: Int): Short = x.toShort + def parseString(str: String): Option[Short] = str.toShortOption + + transparent inline def quot(inline x: Short, inline y: Short): Short = (x / y).toShort + transparent inline def rem(inline x: Short, inline y: Short): Short = (x % y).toShort + + extension (inline x: Short) + transparent inline def toInt: Int = x.toInt + transparent inline def toLong: Long = x.toLong + transparent inline def toFloat: Float = x.toFloat + transparent inline def toDouble: Double = x.toDouble \ No newline at end of file diff --git a/tests/run/inline-numeric/Numeric.scala b/tests/run/inline-numeric/Numeric.scala new file mode 100644 index 000000000000..2ab4e5cf5db0 --- /dev/null +++ b/tests/run/inline-numeric/Numeric.scala @@ -0,0 +1,43 @@ +package scala.math +package inline + +import scala.util.Try + +trait Numeric[T] extends Ordering[T]: + inline def plus(inline x: T, inline y: T): T + inline def minus(inline x: T, inline y: T): T + inline def times(inline x: T, inline y: T): T + inline def negate(inline x: T): T + + def fromInt(x: Int): T + def parseString(str: String): Option[T] + + transparent inline def zero = fromInt(0) + transparent inline def one = fromInt(1) + + extension (inline x: T) + transparent inline def +(inline y: T): T = plus(x, y) + transparent inline def -(inline y: T) = minus(x, y) + transparent inline def *(inline y: T): T = times(x, y) + transparent inline def unary_- = negate(x) + inline def toInt: Int + inline def toLong: Long + inline def toFloat: Float + inline def toDouble: Double + inline def abs: T + inline def sign: T + +trait BigDecimalIsConflicted extends Numeric[BigDecimal] with Ordering.BigDecimalOrdering: + transparent inline def plus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x + y + transparent inline def minus(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x - y + transparent inline def times(inline x: BigDecimal, inline y: BigDecimal): BigDecimal = x * y + transparent inline def negate(inline x: BigDecimal): BigDecimal = -x + + transparent inline def fromInt(x: Int): BigDecimal = BigDecimal(x) + def parseString(str: String): Option[BigDecimal] = Try(BigDecimal(str)).toOption + + extension (inline x: BigDecimal) + transparent inline def toInt: Int = x.intValue + transparent inline def toLong: Long = x.longValue + transparent inline def toFloat: Float = x.floatValue + transparent inline def toDouble: Double = x.doubleValue \ No newline at end of file diff --git a/tests/run/inline-numeric/Ordering.scala b/tests/run/inline-numeric/Ordering.scala new file mode 100644 index 000000000000..9be3377b8dc4 --- /dev/null +++ b/tests/run/inline-numeric/Ordering.scala @@ -0,0 +1,56 @@ +package scala.math +package inline + +import java.util.Comparator + +trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable: + outer => + + inline def tryCompare(x: T, y: T) = Some(compare(x, y)) + + def compare(x: T, y: T): Int + + override inline def lteq(x: T, y: T): Boolean = compare(x, y) <= 0 + override inline def gteq(x: T, y: T): Boolean = compare(x, y) >= 0 + override inline def lt(x: T, y: T): Boolean = compare(x, y) < 0 + override inline def gt(x: T, y: T): Boolean = compare(x, y) > 0 + override inline def equiv(x: T, y: T): Boolean = compare(x, y) == 0 + + inline def max(x: T, y: T): T = if gteq(x, y) then x else y + inline def min(x: T, y: T): T = if lteq(x, y) then x else y + + // This is made into a separate trait, because defining the reverse ordering + // anonymously results in an error: + // Implementation restriction: nested inline methods are not supported + inline def on[U](f: U => T): Ordering[U] = new ReverseOrdering(f) {} + + private trait ReverseOrdering[U](f: U => T) extends Ordering[U]: + inline def compare(x: U, y: U) = outer.compare(f(x), f(y)) + +object Ordering: + trait BigDecimalOrdering extends Ordering[BigDecimal]: + inline def compare(x: BigDecimal, y: BigDecimal) = x.compare(y) + + trait BigIntOrdering extends Ordering[BigInt]: + inline def compare(x: BigInt, y: BigInt) = x.compare(y) + + trait ByteOrdering extends Ordering[Byte]: + inline def compare(x: Byte, y: Byte) = java.lang.Byte.compare(x, y) + + trait CharOrdering extends Ordering[Char]: + inline def compare(x: Char, y: Char) = java.lang.Character.compare(x, y) + + trait IntOrdering extends Ordering[Int]: + inline def compare(x: Int, y: Int) = java.lang.Integer.compare(x, y) + + trait LongOrdering extends Ordering[Long]: + inline def compare(x: Long, y: Long) = java.lang.Long.compare(x, y) + + trait ShortOrdering extends Ordering[Short]: + inline def compare(x: Short, y: Short) = java.lang.Short.compare(x, y) + + trait FloatIeeeOrdering extends Ordering[Float]: + inline def compare(x: Float, y: Float) = java.lang.Float.compare(x, y) + + trait DoubleIeeeOrdering extends Ordering[Double]: + inline def compare(x: Double, y: Double) = java.lang.Double.compare(x, y) \ No newline at end of file diff --git a/tests/run/inline-numeric/test.scala b/tests/run/inline-numeric/test.scala new file mode 100644 index 000000000000..deab452bcce8 --- /dev/null +++ b/tests/run/inline-numeric/test.scala @@ -0,0 +1,54 @@ +import scala.math.inline.* +import scala.math.inline.Ordering.* +import scala.math.inline.Integral.given +import scala.math.inline.Fractional.given + +object tests: + inline def foo[T: Numeric](inline a: T, inline b: T) = + a + b * b + + inline def div[T: Integral](inline a: T, inline b: T) = + a / b % b + + inline def div[T: Fractional](inline a: T, inline b: T) = + a / b + a + + inline def toInt[T: Numeric](inline a: T) = + a.toInt + + inline def explicitToInt[T](inline a: T)(using n: Numeric[T]) = + n.toInt(a) + + inline def sign[T: Numeric](inline a: T) = + a.sign + + inline def explicitPlus[T](inline a: T, inline b: T)(using n: Numeric[T]) = + n.plus(a, b) + + @main def Test = + def a: Int = 0 + def b: Int = 1 + + val v1 = foo(a, b) // should be a + b * b // can check with -Xprint:inlining + val v2 = foo(a.toShort, b.toShort) // should be a + b * b + + val v3 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalAsIfIntegral) // should be BigDecimal(a) quot BigDecimal(b) remainder BigDecimal(b) + val v4 = div(BigDecimal(a), BigDecimal(b))(using BigDecimalIsFractional) // should be BigDecimal(a) / BigDecimal(b) + BigDecimal(a) + + val v5 = toInt(a.toFloat) // should be a.toFloat.toInt + val v6 = toInt(a) // should be a + + val v7 = sign(a) + val v8 = sign(a.toChar) + val v9 = sign(-7F) + + val v10 = sign(BigDecimal(a))(using BigDecimalAsIfIntegral) + val v11 = sign(BigDecimal(a))(using BigDecimalIsFractional) // the condition with isNan() should be removed, i.e. it should be equivalent to v10 + + val v12 = explicitPlus(3, 5) // should be 8 + val v13 = explicitPlus(a, b) // should be a + b + + val v14 = explicitToInt(3.2) // should be (3.2).toInt + val v15 = explicitToInt(3) // should be 3 + val v16 = explicitToInt(a) // should be a + val v17 = explicitToInt(a.toShort) // should be a.toShort.toInt \ No newline at end of file From 0dea6c0681d632f66689eae8ee126a1910cf7ecd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 14 Mar 2023 11:09:49 +0000 Subject: [PATCH 280/657] Fail the Java protected member case, rather than be wierd --- .../tools/dotc/transform/ProtectedAccessors.scala | 14 ++++++++++---- .../tools/dotc/transform/SuperAccessors.scala | 13 +++++++++++-- tests/neg/i17021.ext-java/A.java | 6 ++++++ tests/neg/i17021.ext-java/Test.scala | 14 ++++++++++++++ tests/run/i17021.ext-java/Test.scala | 6 +++--- 5 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 tests/neg/i17021.ext-java/A.java create mode 100644 tests/neg/i17021.ext-java/Test.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala index 2b150d321812..6d8f7bdb32cb 100644 --- a/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/ProtectedAccessors.scala @@ -33,12 +33,18 @@ object ProtectedAccessors { ctx.owner.isContainedIn(boundary) || ctx.owner.isContainedIn(boundary.linkedClass) } - /** Do we need a protected accessor for accessing sym from the current context's owner? */ - def needsAccessor(sym: Symbol)(using Context): Boolean = + /** Do we need a protected accessor if the current context's owner + * is not in a subclass or subtrait of `sym`? + */ + def needsAccessorIfNotInSubclass(sym: Symbol)(using Context): Boolean = sym.isTerm && sym.is(Protected) && !sym.owner.is(Trait) && // trait methods need to be handled specially, are currently always public - !insideBoundaryOf(sym) && - (sym.is(JavaDefined) || !ctx.owner.enclosingClass.derivesFrom(sym.owner)) + !insideBoundaryOf(sym) + + /** Do we need a protected accessor for accessing sym from the current context's owner? */ + def needsAccessor(sym: Symbol)(using Context): Boolean = + needsAccessorIfNotInSubclass(sym) && + !ctx.owner.enclosingClass.derivesFrom(sym.owner) } class ProtectedAccessors extends MiniPhase { diff --git a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala index 5d7210a59a96..b78c75d58340 100644 --- a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -175,7 +175,7 @@ class SuperAccessors(thisPhase: DenotTransformer) { val sym = sel.symbol def needsSuperAccessor = - ProtectedAccessors.needsAccessor(sym) && + ProtectedAccessors.needsAccessorIfNotInSubclass(sym) && AccessProxies.hostForAccessorOf(sym).is(Trait) qual match { case _: This if needsSuperAccessor => @@ -185,10 +185,19 @@ class SuperAccessors(thisPhase: DenotTransformer) { * If T extends C, then we can access it by casting * the qualifier of the select to C. * + * That's because the protected method is actually public, + * so we can call it. For truly protected methods, like from + * Java, we error instead of emitting the wrong code (i17021.ext-java). + * * Otherwise, we need to go through an accessor, * which the implementing class will provide an implementation for. */ - superAccessorCall(sel) + if ctx.owner.enclosingClass.derivesFrom(sym.owner) then + if sym.is(JavaDefined) then + report.error(em"${ctx.owner} accesses protected $sym inside a concrete trait method: use super.${sel.name} instead", sel.srcPos) + sel + else + superAccessorCall(sel) case Super(_, mix) => transformSuperSelect(sel) case _ => diff --git a/tests/neg/i17021.ext-java/A.java b/tests/neg/i17021.ext-java/A.java new file mode 100644 index 000000000000..536e9caa4a38 --- /dev/null +++ b/tests/neg/i17021.ext-java/A.java @@ -0,0 +1,6 @@ +// Derives from run/i17021.defs, but with a Java protected member +package p1; + +public class A { + protected int foo() { return 1; } +} diff --git a/tests/neg/i17021.ext-java/Test.scala b/tests/neg/i17021.ext-java/Test.scala new file mode 100644 index 000000000000..c700ed8138d7 --- /dev/null +++ b/tests/neg/i17021.ext-java/Test.scala @@ -0,0 +1,14 @@ +// Derives from run/i17021.defs +// but with a Java protected member +// which leads to a compile error +package p2: + trait B extends p1.A: + def bar: Int = foo // error: method bar accesses protected method foo inside a concrete trait method: use super.foo instead + + class C extends B: + override def foo: Int = 2 + +object Test: + def main(args: Array[String]): Unit = + val n = new p2.C().bar + assert(n == 2, n) diff --git a/tests/run/i17021.ext-java/Test.scala b/tests/run/i17021.ext-java/Test.scala index 80dbca9fdd5d..3741fa2dfadb 100644 --- a/tests/run/i17021.ext-java/Test.scala +++ b/tests/run/i17021.ext-java/Test.scala @@ -1,9 +1,9 @@ // Derives from run/i17021.defs // but with a Java protected member -// which changes the behaviour +// and fixed calling code, that uses super package p2: trait B extends p1.A: - def bar: Int = foo + def bar: Int = super.foo class C extends B: override def foo: Int = 2 @@ -11,4 +11,4 @@ package p2: object Test: def main(args: Array[String]): Unit = val n = new p2.C().bar - assert(n == 1, n) // B can only call super.foo + assert(n == 1, n) From 61165cca968ca28da834c3a47138530decd02a2b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 14 Mar 2023 12:17:13 +0100 Subject: [PATCH 281/657] Update compiler/src/dotty/tools/dotc/staging/HealType.scala Co-authored-by: Natsu Kagami --- compiler/src/dotty/tools/dotc/staging/HealType.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 645bde42eb29..44843dfa91ca 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -66,7 +66,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { /** Try to heal reference to type `T` used in a higher level than its definition. * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. - * Emits and error if `T` cannot be healed and returns `T`. + * Emits an error if `T` cannot be healed and returns `T`. */ protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = { val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp) From e9d87184cdbd40516f6bb2844c484bd6d12ce1e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Andres?= Date: Tue, 14 Mar 2023 13:45:45 +0100 Subject: [PATCH 282/657] Add newlines at end of test files --- tests/run/inline-numeric/Fractional.scala | 2 +- tests/run/inline-numeric/Integral.scala | 2 +- tests/run/inline-numeric/Numeric.scala | 2 +- tests/run/inline-numeric/Ordering.scala | 2 +- tests/run/inline-numeric/test.scala | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/run/inline-numeric/Fractional.scala b/tests/run/inline-numeric/Fractional.scala index a61c60063fd9..f1bc81246a43 100644 --- a/tests/run/inline-numeric/Fractional.scala +++ b/tests/run/inline-numeric/Fractional.scala @@ -59,4 +59,4 @@ object Fractional: transparent inline def toInt: Int = x.toInt transparent inline def toLong: Long = x.toLong transparent inline def toFloat: Float = x - transparent inline def toDouble: Double = x.toDouble \ No newline at end of file + transparent inline def toDouble: Double = x.toDouble diff --git a/tests/run/inline-numeric/Integral.scala b/tests/run/inline-numeric/Integral.scala index 4ef11738113c..1a740a3e1d99 100644 --- a/tests/run/inline-numeric/Integral.scala +++ b/tests/run/inline-numeric/Integral.scala @@ -129,4 +129,4 @@ object Integral: transparent inline def toInt: Int = x.toInt transparent inline def toLong: Long = x.toLong transparent inline def toFloat: Float = x.toFloat - transparent inline def toDouble: Double = x.toDouble \ No newline at end of file + transparent inline def toDouble: Double = x.toDouble diff --git a/tests/run/inline-numeric/Numeric.scala b/tests/run/inline-numeric/Numeric.scala index 2ab4e5cf5db0..99b46b05aa9d 100644 --- a/tests/run/inline-numeric/Numeric.scala +++ b/tests/run/inline-numeric/Numeric.scala @@ -40,4 +40,4 @@ trait BigDecimalIsConflicted extends Numeric[BigDecimal] with Ordering.BigDecima transparent inline def toInt: Int = x.intValue transparent inline def toLong: Long = x.longValue transparent inline def toFloat: Float = x.floatValue - transparent inline def toDouble: Double = x.doubleValue \ No newline at end of file + transparent inline def toDouble: Double = x.doubleValue diff --git a/tests/run/inline-numeric/Ordering.scala b/tests/run/inline-numeric/Ordering.scala index 9be3377b8dc4..714fa51b1226 100644 --- a/tests/run/inline-numeric/Ordering.scala +++ b/tests/run/inline-numeric/Ordering.scala @@ -53,4 +53,4 @@ object Ordering: inline def compare(x: Float, y: Float) = java.lang.Float.compare(x, y) trait DoubleIeeeOrdering extends Ordering[Double]: - inline def compare(x: Double, y: Double) = java.lang.Double.compare(x, y) \ No newline at end of file + inline def compare(x: Double, y: Double) = java.lang.Double.compare(x, y) diff --git a/tests/run/inline-numeric/test.scala b/tests/run/inline-numeric/test.scala index deab452bcce8..9ca88aee0374 100644 --- a/tests/run/inline-numeric/test.scala +++ b/tests/run/inline-numeric/test.scala @@ -51,4 +51,4 @@ object tests: val v14 = explicitToInt(3.2) // should be (3.2).toInt val v15 = explicitToInt(3) // should be 3 val v16 = explicitToInt(a) // should be a - val v17 = explicitToInt(a.toShort) // should be a.toShort.toInt \ No newline at end of file + val v17 = explicitToInt(a.toShort) // should be a.toShort.toInt From fab5e7b53b060ef8140c6801aa62684b515aa95c Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 14 Mar 2023 15:53:10 +0100 Subject: [PATCH 283/657] Fix for formatting and traverse call of inlined tree in wunused --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 66b0876668be..35153dbf66e9 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -1,7 +1,7 @@ package dotty.tools.dotc.transform import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.ast.tpd.TreeTraverser +import dotty.tools.dotc.ast.tpd.{Inlined, TreeTraverser} import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.ast.untpd.ImportSelector import dotty.tools.dotc.config.ScalaSettings @@ -59,6 +59,7 @@ class CheckUnused extends MiniPhase: // ========== SETUP ============ override def prepareForUnit(tree: tpd.Tree)(using Context): Context = + println(tree) val data = UnusedData() val fresh = ctx.fresh.setProperty(_key, data) fresh @@ -75,15 +76,16 @@ class CheckUnused extends MiniPhase: traverser.traverse(tree) ctx + def prepareForInlined(tree: Inlined)(using Context): Context = + traverser.traverse(tree.call) + ctx + override def prepareForIdent(tree: tpd.Ident)(using Context): Context = if tree.symbol.exists then val prefixes = LazyList.iterate(tree.typeOpt.normalizedPrefix)(_.normalizedPrefix).takeWhile(_ != NoType) .take(10) // Failsafe for the odd case if there was an infinite cycle - for { - prefix <- prefixes - } { + for prefix <- prefixes do unusedDataApply(_.registerUsed(prefix.classSymbol, None)) - } unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) From 43725233dc69ceabbcda6d498065949f4cc4f75f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 7 Mar 2023 14:23:30 +0100 Subject: [PATCH 284/657] Update cross-stage safety nomenclature Before `3.0` we experimented with the Phase Consistency Principle (PCP) which was described in [A practical unification of multi-stage programming and macros](https://dl.acm.org/doi/10.1145/3278122.3278139). This principle restricted term and types to be used at the same level. Now, and since `3.0` we use a more general version of cross-stage safety where types can be used at any level. Lower levels get erased and higher levels require a given `Type[T]`. The most updated reference is in [Scalable Metaprogramming in Scala 3](https://github.com/nicolasstucki/nicolasstucki/raw/main/Scalable%20Metaprogramming%20in%20Scala%203.pdf). We must remove the use of the term PCP to avoid confusion in the current implementation. --- .../tools/dotc/inlines/PrepareInlineable.scala | 4 ++-- ...CheckAndHeal.scala => CrossStageSafety.scala} | 10 +++++----- .../dotty/tools/dotc/staging/QuoteContext.scala | 1 - .../dotty/tools/dotc/transform/Splicing.scala | 1 - .../src/dotty/tools/dotc/transform/Staging.scala | 12 ++++++------ .../dotty/tools/dotc/transform/TreeChecker.scala | 2 +- docs/_docs/internals/overall-structure.md | 5 ++++- .../dotc/core/StagingContext.scala | 8 ++++---- .../dotc/inlines/PrepareInlineable.scala | 4 ++-- ...CheckAndHeal.scala => CrossStageSafety.scala} | 16 ++++++++-------- .../dotc/transform/Splicing.scala | 4 ++-- .../dotc/transform/Staging.scala | 10 +++++----- .../dotc/transform/TreeChecker.scala | 2 +- 13 files changed, 40 insertions(+), 39 deletions(-) rename compiler/src/dotty/tools/dotc/staging/{PCPCheckAndHeal.scala => CrossStageSafety.scala} (96%) rename tests/pos-with-compiler-cc/dotc/transform/{PCPCheckAndHeal.scala => CrossStageSafety.scala} (95%) diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index eb1a97ab93c0..7a0d3f61cb33 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -18,7 +18,7 @@ import inlines.Inlines import NameOps._ import Annotations._ import transform.{AccessProxies, Splicer} -import staging.PCPCheckAndHeal +import staging.CrossStageSafety import transform.SymUtils.* import config.Printers.inlining import util.Property @@ -294,7 +294,7 @@ object PrepareInlineable { if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) - (new PCPCheckAndHeal).transform(body) // Ignore output, only check PCP + (new CrossStageSafety).transform(body) // Ignore output, only check cross-stage safety case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat) case Block(Nil, expr) => checkMacro(expr) case Typed(expr, _) => checkMacro(expr) diff --git a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala similarity index 96% rename from compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala rename to compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 2a1371d8eb0d..219b428ca8d4 100644 --- a/compiler/src/dotty/tools/dotc/staging/PCPCheckAndHeal.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -17,14 +17,14 @@ import dotty.tools.dotc.util.Property import dotty.tools.dotc.util.Spans._ import dotty.tools.dotc.util.SrcPos -/** Checks that the Phase Consistency Principle (PCP) holds and heals types. +/** Checks that staging level consistency holds and heals staged types . * - * Local term references are phase consistent if and only if they are used at the same level as their definition. + * Local term references are level consistent if and only if they are used at the same level as their definition. * * Local type references can be used at the level of their definition or lower. If used used at a higher level, * it will be healed if possible, otherwise it is inconsistent. * - * Type healing consists in transforming a phase inconsistent type `T` into `summon[Type[T]].Underlying`. + * Type healing consists in transforming a level inconsistent type `T` into `summon[Type[T]].Underlying`. * * As references to types do not necessarily have an associated tree it is not always possible to replace the types directly. * Instead we always generate a type alias for it and place it at the start of the surrounding quote. This also avoids duplication. @@ -43,7 +43,7 @@ import dotty.tools.dotc.util.SrcPos * } * */ -class PCPCheckAndHeal extends TreeMapWithStages { +class CrossStageSafety extends TreeMapWithStages { import tpd._ private val InAnnotation = Property.Key[Unit]() @@ -97,7 +97,7 @@ class PCPCheckAndHeal extends TreeMapWithStages { super.transform(tree) } - /** Transform quoted trees while maintaining phase correctness */ + /** Transform quoted trees while maintaining level correctness */ override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = { val taggedTypes = new QuoteTypeTags(quote.span) diff --git a/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala b/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala index 542e8ecc2b9f..8e25bba7110c 100644 --- a/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala +++ b/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala @@ -4,7 +4,6 @@ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.util.Property -import dotty.tools.dotc.staging.PCPCheckAndHeal import dotty.tools.dotc.staging.StagingLevel.* object QuoteContext { diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 7ecde9400445..6067a0b4ff96 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -21,7 +21,6 @@ import dotty.tools.dotc.core.Names._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.config.ScalaRelease.* -import dotty.tools.dotc.staging.PCPCheckAndHeal import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 5ebfa25eeacd..83b2bcdbcaa6 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -12,12 +12,12 @@ import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* -import dotty.tools.dotc.staging.PCPCheckAndHeal +import dotty.tools.dotc.staging.CrossStageSafety import dotty.tools.dotc.staging.HealType -/** Checks that the Phase Consistency Principle (PCP) holds and heals types. +/** Checks that staging level consistency holds and heals types used in higher levels. * - * Type healing consists in transforming a phase inconsistent type `T` into `${ implicitly[Type[T]] }`. + * See `CrossStageSafety` */ class Staging extends MacroTransform { import tpd._ @@ -32,10 +32,10 @@ class Staging extends MacroTransform { override def checkPostCondition(tree: Tree)(using Context): Unit = if (ctx.phase <= splicingPhase) { - // Recheck that PCP holds but do not heal any inconsistent types as they should already have been heald + // Recheck that staging level consistency holds but do not heal any inconsistent types as they should already have been heald tree match { case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass => - val checker = new PCPCheckAndHeal { + val checker = new CrossStageSafety { override protected def healType(pos: SrcPos)(using Context) = new HealType(pos) { override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = { def symStr = @@ -72,7 +72,7 @@ class Staging extends MacroTransform { protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = - (new PCPCheckAndHeal).transform(tree) + (new CrossStageSafety).transform(tree) } } diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 05d3ce5679a3..6d904d1f3cc6 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -513,7 +513,7 @@ object TreeChecker { val inliningPhase = ctx.base.inliningPhase inliningPhase.exists && ctx.phase.id > inliningPhase.id if isAfterInlining then - // The staging phase destroys in PCPCheckAndHeal the property that + // The staging phase destroys in CrossStageSafety the property that // tree.expr.tpe <:< pt1. A test case where this arises is run-macros/enum-nat-macro. // We should follow up why this happens. If the problem is fixed, we can // drop the isAfterInlining special case. To reproduce the problem, just diff --git a/docs/_docs/internals/overall-structure.md b/docs/_docs/internals/overall-structure.md index f50ab6bf03a7..5bb43eb946a8 100644 --- a/docs/_docs/internals/overall-structure.md +++ b/docs/_docs/internals/overall-structure.md @@ -104,7 +104,6 @@ phases. The current list of phases is specified in class [Compiler] as follows: List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files List(new PostTyper) :: // Additional checks and cleanups after type checking List(new sjs.PrepJSInterop) :: // Additional checks and transformations for Scala.js (Scala.js only) - List(new Staging) :: // Check PCP, heal quoted types and expand macros List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks List(new SetRootTree) :: // Set the `rootTreeOrProvider` on class symbols Nil @@ -112,6 +111,10 @@ phases. The current list of phases is specified in class [Compiler] as follows: /** Phases dealing with TASTY tree pickling and unpickling */ protected def picklerPhases: List[List[Phase]] = List(new Pickler) :: // Generate TASTY info + List(new Inlining) :: // Inline and execute macros + List(new PostInlining) :: // Add mirror support for inlined code + List(new Staging) :: // Check staging levels and heal staged types + List(new Splicing) :: // Replace level 1 splices with holes List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures Nil diff --git a/tests/pos-with-compiler-cc/dotc/core/StagingContext.scala b/tests/pos-with-compiler-cc/dotc/core/StagingContext.scala index 9e0bb95394a3..4ca53e02a831 100644 --- a/tests/pos-with-compiler-cc/dotc/core/StagingContext.scala +++ b/tests/pos-with-compiler-cc/dotc/core/StagingContext.scala @@ -4,7 +4,7 @@ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.util.Property -import dotty.tools.dotc.transform.PCPCheckAndHeal +import dotty.tools.dotc.transform.CrossStageSafety object StagingContext { @@ -16,7 +16,7 @@ object StagingContext { */ private val QuotesStack = new Property.Key[List[tpd.Tree]] - private val TaggedTypes = new Property.Key[PCPCheckAndHeal.QuoteTypeTags] + private val TaggedTypes = new Property.Key[CrossStageSafety.QuoteTypeTags] /** All enclosing calls that are currently inlined, from innermost to outermost. */ def level(using Context): Int = @@ -36,10 +36,10 @@ object StagingContext { def spliceContext(using Context): Context = ctx.fresh.setProperty(QuotationLevel, level - 1) - def contextWithQuoteTypeTags(taggedTypes: PCPCheckAndHeal.QuoteTypeTags)(using Context) = + def contextWithQuoteTypeTags(taggedTypes: CrossStageSafety.QuoteTypeTags)(using Context) = ctx.fresh.setProperty(TaggedTypes, taggedTypes) - def getQuoteTypeTags(using Context): PCPCheckAndHeal.QuoteTypeTags = + def getQuoteTypeTags(using Context): CrossStageSafety.QuoteTypeTags = ctx.property(TaggedTypes).get /** Context with a decremented quotation level and pops the Some of top of the quote context stack or None if the stack is empty. diff --git a/tests/pos-with-compiler-cc/dotc/inlines/PrepareInlineable.scala b/tests/pos-with-compiler-cc/dotc/inlines/PrepareInlineable.scala index ecf24ff8264e..9bb0bacd7a78 100644 --- a/tests/pos-with-compiler-cc/dotc/inlines/PrepareInlineable.scala +++ b/tests/pos-with-compiler-cc/dotc/inlines/PrepareInlineable.scala @@ -17,7 +17,7 @@ import NameKinds.{InlineAccessorName, UniqueInlineName} import inlines.Inlines import NameOps._ import Annotations._ -import transform.{AccessProxies, PCPCheckAndHeal, Splicer} +import transform.{AccessProxies, CrossStageSafety, Splicer} import transform.SymUtils.* import config.Printers.inlining import util.Property @@ -294,7 +294,7 @@ object PrepareInlineable { if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) - new PCPCheckAndHeal(freshStagingContext).transform(body) // Ignore output, only check PCP + new CrossStageSafety(freshStagingContext).transform(body) // Ignore output, only check staging levels case Block(List(stat), Literal(Constants.Constant(()))) => checkMacro(stat) case Block(Nil, expr) => checkMacro(expr) case Typed(expr, _) => checkMacro(expr) diff --git a/tests/pos-with-compiler-cc/dotc/transform/PCPCheckAndHeal.scala b/tests/pos-with-compiler-cc/dotc/transform/CrossStageSafety.scala similarity index 95% rename from tests/pos-with-compiler-cc/dotc/transform/PCPCheckAndHeal.scala rename to tests/pos-with-compiler-cc/dotc/transform/CrossStageSafety.scala index 90128500374e..ca00c87161ef 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/PCPCheckAndHeal.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/CrossStageSafety.scala @@ -22,14 +22,14 @@ import dotty.tools.dotc.util.Property import scala.annotation.constructorOnly -/** Checks that the Phase Consistency Principle (PCP) holds and heals types. +/** Checks that staging level consistency holds and heals staged types . * - * Local term references are phase consistent if and only if they are used at the same level as their definition. + * Local term references are level consistent if and only if they are used at the same level as their definition. * * Local type references can be used at the level of their definition or lower. If used used at a higher level, * it will be healed if possible, otherwise it is inconsistent. * - * Type healing consists in transforming a phase inconsistent type `T` into `summon[Type[T]].Underlying`. + * Type healing consists in transforming a level inconsistent type `T` into `summon[Type[T]].Underlying`. * * As references to types do not necessarily have an associated tree it is not always possible to replace the types directly. * Instead we always generate a type alias for it and place it at the start of the surrounding quote. This also avoids duplication. @@ -48,7 +48,7 @@ import scala.annotation.constructorOnly * } * */ -class PCPCheckAndHeal(@constructorOnly ictx: DetachedContext) extends TreeMapWithStages(ictx), Checking, caps.Pure { +class CrossStageSafety(@constructorOnly ictx: DetachedContext) extends TreeMapWithStages(ictx), Checking, caps.Pure { import tpd._ private val InAnnotation = Property.Key[Unit]() @@ -96,9 +96,9 @@ class PCPCheckAndHeal(@constructorOnly ictx: DetachedContext) extends TreeMapWit super.transform(tree) } - /** Transform quoted trees while maintaining phase correctness */ + /** Transform quoted trees while maintaining level correctness */ override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = { - val taggedTypes = new PCPCheckAndHeal.QuoteTypeTags(quote.span) + val taggedTypes = new CrossStageSafety.QuoteTypeTags(quote.span) if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) @@ -215,7 +215,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: DetachedContext) extends TreeMapWit mapOver(tp) } - /** Check phase consistency of terms and heal inconsistent type references. */ + /** Check level consistency of terms and heal inconsistent type references. */ private def healTypeOfTerm(pos: SrcPos)(using Context) = new TypeMap { def apply(tp: Type): Type = tp match @@ -275,7 +275,7 @@ class PCPCheckAndHeal(@constructorOnly ictx: DetachedContext) extends TreeMapWit } -object PCPCheckAndHeal { +object CrossStageSafety { import tpd._ class QuoteTypeTags(span: Span)(using DetachedContext) extends caps.Pure { diff --git a/tests/pos-with-compiler-cc/dotc/transform/Splicing.scala b/tests/pos-with-compiler-cc/dotc/transform/Splicing.scala index df6128d249d2..3a3aa7e89445 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/Splicing.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/Splicing.scala @@ -191,7 +191,7 @@ class Splicing extends MacroTransform: private var refBindingMap = mutable.Map.empty[Symbol, (Tree, Symbol)] /** Reference to the `Quotes` instance of the current level 1 splice */ private var quotes: Tree | Null = null // TODO: add to the context - private var healedTypes: PCPCheckAndHeal.QuoteTypeTags | Null = null // TODO: add to the context + private var healedTypes: CrossStageSafety.QuoteTypeTags | Null = null // TODO: add to the context def transformSplice(tree: tpd.Tree, tpe: Type, holeIdx: Int)(using Context): tpd.Tree = assert(level == 0) @@ -254,7 +254,7 @@ class Splicing extends MacroTransform: private def transformLevel0QuoteContent(tree: Tree)(using Context): Tree = // transform and collect new healed types val old = healedTypes - healedTypes = new PCPCheckAndHeal.QuoteTypeTags(tree.span) + healedTypes = new CrossStageSafety.QuoteTypeTags(tree.span) val tree1 = transform(tree) val newHealedTypes = healedTypes.nn.getTypeTags healedTypes = old diff --git a/tests/pos-with-compiler-cc/dotc/transform/Staging.scala b/tests/pos-with-compiler-cc/dotc/transform/Staging.scala index 12c5c8215cad..c2c6f76cd0fc 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/Staging.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/Staging.scala @@ -15,7 +15,7 @@ import dotty.tools.dotc.transform.TreeMapWithStages._ -/** Checks that the Phase Consistency Principle (PCP) holds and heals types. +/** Checks that staging level consistency holds and heals staged types. * * Type healing consists in transforming a phase inconsistent type `T` into `${ implicitly[Type[T]] }`. */ @@ -32,12 +32,12 @@ class Staging extends MacroTransform { override def checkPostCondition(tree: Tree)(using Context): Unit = if (ctx.phase <= splicingPhase) { - // Recheck that PCP holds but do not heal any inconsistent types as they should already have been heald + // Recheck that staging levels hold but do not heal any inconsistent types as they should already have been heald tree match { case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass => val stagingCtx = freshStagingContext - val checker = new PCPCheckAndHeal(stagingCtx) { - // !cc! type error is checker is defined as val checker = new PCPCheckAndHeal { ... } + val checker = new CrossStageSafety(stagingCtx) { + // !cc! type error is checker is defined as val checker = new CrossStageSafety { ... } override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos)(using Context): TypeRef = { def symStr = if (sym.is(ModuleClass)) sym.sourceModule.show @@ -72,7 +72,7 @@ class Staging extends MacroTransform { protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = - new PCPCheckAndHeal(ctx.detach).transform(tree) + new CrossStageSafety(ctx.detach).transform(tree) } } diff --git a/tests/pos-with-compiler-cc/dotc/transform/TreeChecker.scala b/tests/pos-with-compiler-cc/dotc/transform/TreeChecker.scala index e7607d8e59c6..b0d7fb89985f 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/TreeChecker.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/TreeChecker.scala @@ -458,7 +458,7 @@ class TreeChecker extends Phase with SymTransformer { val inliningPhase = ctx.base.inliningPhase inliningPhase.exists && ctx.phase.id > inliningPhase.id if isAfterInlining then - // The staging phase destroys in PCPCheckAndHeal the property that + // The staging phase destroys in CrossStageSafety the property that // tree.expr.tpe <:< pt1. A test case where this arises is run-macros/enum-nat-macro. // We should follow up why this happens. If the problem is fixed, we can // drop the isAfterInlining special case. To reproduce the problem, just From d8c9714fd81de526d425054fb71fcff7b36c8740 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 8 Mar 2023 11:07:28 +0100 Subject: [PATCH 285/657] Update macros.md and macro-spec.md Copy pasted and adapted a bit the contents of chapter 3 of _Scalable Metaprogramming in Scala 3_. The previous version was based on _A practical unification of multi-stage programming and macros_. This version was extremely out of date. --- .../reference/metaprogramming/macros-spec.md | 848 ++++++++++--- .../_docs/reference/metaprogramming/macros.md | 1054 +++++++---------- .../reference/metaprogramming/staging.md | 4 +- .../metaprogramming/macros-spec.md | 842 ++++++++++--- .../TODOreference/metaprogramming/macros.md | 1028 +++++++--------- .../TODOreference/metaprogramming/staging.md | 2 +- 6 files changed, 2135 insertions(+), 1643 deletions(-) diff --git a/docs/_docs/reference/metaprogramming/macros-spec.md b/docs/_docs/reference/metaprogramming/macros-spec.md index d4221365454d..6045354fdbbc 100644 --- a/docs/_docs/reference/metaprogramming/macros-spec.md +++ b/docs/_docs/reference/metaprogramming/macros-spec.md @@ -4,251 +4,711 @@ title: "Macros Spec" nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/macros-spec.html --- +## Formalization + +* Multi-stage programming with generative and analytical macros[^2] +* Multi-Stage Macro Calculus, Chapter 4 of Scalable Metaprogramming in Scala 3[^1]. + Contains and extends the calculus of _Multi-stage programming with generative and analytical macros_ with type polymorphism. + +## Syntax + +The quotation syntax using `'` and `$` was chosen to mimic the string interpolation syntax of Scala. +Like a string double-quotation, a single-quote block can contain splices. +However, unlike strings, splices can contain quotes using the same rules. + +```scala +s" Hello $name" s" Hello ${name}" +'{ hello($name) } '{ hello(${name}) } +${ hello('name) } ${ hello('{name}) } +``` + +### Quotes +Quotes come in four flavors: quoted identifiers, quoted blocks, quoted block patterns and quoted type patterns. +Scala 2 used quoted identifiers to represent `Symbol` literals. They were deprecated in Scala 3, allowing to use them for quotation. +```scala +SimpleExpr ::= ... + | `'` alphaid // quoted identifier + | `'` `{` Block `}` // quoted block +Pattern ::= ... + | `'` `{` Block `}` // quoted block pattern + | `'` `[` Type `]` // quoted type pattern +``` + +Quoted blocks and quoted block patterns contain an expression equivalent to a normal block of code. +When entering either of those we track the fact that we are in a quoted block (`inQuoteBlock`) which is used for spliced identifiers. +When entering a quoted block pattern we additionally track the fact that we are in a quoted pattern (`inQuotePattern`) which is used to distinguish spliced blocks and splice patterns. +Lastly, the quoted type pattern simply contains a type. + +### Splices +Splices come in three flavors: spliced identifiers, spliced blocks and splice patterns. +Scala specifies identifiers containing `$` as valid identifiers but reserves them for compiler and standard library use only. +Unfortunately, many libraries have used such identifiers in Scala~2. Therefore to mitigate the cost of migration, we still support them. +We work around this by only allowing spliced identifiers[^3] within quoted blocks or quoted block patterns (`inQuoteBlock`). +Splice blocks and splice patterns can contain an arbitrary block or pattern respectively. +They are distinguished based on their surrounding quote (`inQuotePattern`), a quote block will contain spliced blocks, and a quote block pattern will contain splice patterns. + +```scala +SimpleExpr ::= ... + | `$` alphaid if inQuoteBlock // spliced identifier + | `$` `{` Block `}` if !inQuotePattern // spliced block + | `$` `{` Pattern `}` if inQuotePattern // splice pattern +``` + +### Quoted Pattern Type Variables +Quoted pattern type variables in quoted patterns and quoted type patterns do not require additional syntax. +Any type definition or reference with a name composed of lower cases is assumed to be a pattern type variable definition while typing. +A backticked type name with lower cases is interpreted as a reference to the type with that name. + + ## Implementation -### Syntax - -Compared to the [Scala 3 reference grammar](../syntax.md) -there are the following syntax changes: -``` -SimpleExpr ::= ... - | ‘'’ ‘{’ Block ‘}’ - | ‘'’ ‘[’ Type ‘]’ - | ‘$’ ‘{’ Block ‘}’ -SimpleType ::= ... - | ‘$’ ‘{’ Block ‘}’ -``` -In addition, an identifier `$x` starting with a `$` that appears inside -a quoted expression or type is treated as a splice `${x}` and a quoted identifier -`'x` that appears inside a splice is treated as a quote `'{x}` - -### Implementation in `scalac` - -Quotes and splices are primitive forms in the generated abstract syntax trees. -Top-level splices are eliminated during macro expansion while typing. On the -other hand, top-level quotes are eliminated in an expansion phase `PickleQuotes` -phase (after typing and pickling). PCP checking occurs while preparing the RHS -of an inline method for top-level splices and in the `Staging` phase (after -typing and before pickling). - -Macro-expansion works outside-in. If the outermost scope is a splice, -the spliced AST will be evaluated in an interpreter. A call to a -previously compiled method can be implemented as a reflective call to -that method. With the restrictions on splices that are currently in -place that’s all that’s needed. We might allow more interpretation in -splices in the future, which would allow us to loosen the -restriction. Quotes in spliced, interpreted code are kept as they -are, after splices nested in the quotes are expanded. - -If the outermost scope is a quote, we need to generate code that -constructs the quoted tree at run-time. We implement this by -serializing the tree as a TASTy structure, which is stored -in a string literal. At runtime, an unpickler method is called to -deserialize the string into a tree. - -Splices inside quoted code insert the spliced tree as is, after -expanding any quotes in the spliced code recursively. +### Run-Time Representation -## Formalization +The standard library defines the `Quotes` interface which contains all the logic and the abstract classes `Expr` and `Type`. +The compiler implements the `Quotes` interface and provides the implementation of `Expr` and `Type`. + +##### `class Expr` +Expressions of type `Expr[T]` are represented by the following abstract class: +```scala +abstract class Expr[+T] private[scala] +``` +The only implementation of `Expr` is in the compiler along with the implementation of `Quotes`. +It is a class that wraps a typed AST and a `Scope` object with no methods of its own. +The `Scope` object is used to track the current splice scope and detect scope extrusions. + +##### `object Expr` +The companion object of `Expr` contains a few useful static methods; +the `apply`/`unapply` methods to use `ToExpr`/`FromExpr` with ease; +the `betaReduce` and `summon` methods. +It also contains methods to create expressions out of lists or sequences of expressions: `block`, `ofSeq`, `ofList`, `ofTupleFromSeq` and `ofTuple`. + +```scala +object Expr: + def apply[T](x: T)(using ToExpr[T])(using Quotes): Expr[T] = ... + def unapply[T](x: Expr[T])(using FromExpr[T])(using Quotes): Option[T] = ... + def betaReduce[T](e: Expr[T])(using Quotes): Expr[T] = ... + def summon[T: Type](using Quotes): Option[Expr[T]] = ... + def block[T](stats: List[Expr[Any]], e: Expr[T])(using Quotes): Expr[T] = ... + def ofSeq[T: Type](xs: Seq[Expr[T]])(using Quotes): Expr[Seq[T]] = ... + def ofList[T: Type](xs: Seq[Expr[T]])(using Quotes): Expr[List[T]] = ... + def ofTupleFromSeq(xs: Seq[Expr[Any]])(using Quotes): Expr[Tuple] = ... + def ofTuple[T <: Tuple: Tuple.IsMappedBy[Expr]: Type](tup: T)(using Quotes): + Expr[Tuple.InverseMap[T, Expr]] = ... +``` + +##### `class Type` +Types of type `Type[T]` are represented by the following abstract class: +```scala +abstract class Type[T <: AnyKind] private[scala]: + type Underlying = T +``` + +The only implementation of `Type` is in the compiler along with the implementation of `Quotes`. +It is a class that wraps the AST of a type and a `Scope` object with no methods of its own. +The upper bound of `T` is `AnyKind` which implies that `T` may be a higher-kinded type. +The `Underlying` alias is used to select the type from an instance of `Type`. +Users never need to use this alias as they can always use `T` directly. +`Underlying` is used for internal encoding while compiling the code (see _Type Healing_). + +##### `object Type` +The companion object of `Type` contains a few useful static methods. +The first and most important one is the `Type.of` given definition. +This instance of `Type[T]` is summoned by default when no other instance is available. +The `of` operation is an intrinsic operation that the compiler will transform into code that will generate the `Type[T]` at run-time. +Secondly, the `Type.show[T]` operation will show a string representation of the type, which is often useful when debugging. +Finally, the object defines `valueOfConstant` (and `valueOfTuple`) which can transform singleton types (or tuples of singleton types) into their value. + + +```scala +object Type: + given of[T <: AnyKind](using Quotes): Type[T] = ... + def show[T <: AnyKind](using Type[T])(using Quotes): String = ... + def valueOfConstant[T](using Type[T])(using Quotes): Option[T] = ... + def valueOfTuple[T <: Tuple](using Type[T])(using Quotes): Option[T] = ... +``` + +##### `Quotes` +The `Quotes` interface is where most of the primitive operations of the quotation system are defined. + +Quotes define all the `Expr[T]` methods as extension methods. +`Type[T]` does not have methods and therefore does not appear here. +These methods are available as long as `Quotes` is implicitly given in the current scope. + +The `Quotes` instance is also the entry point to the [reflection API](./refelction.md) through the `reflect` object. + +Finally, `Quotes` provides the internal logic used in quote un-pickling (`QuoteUnpickler`) in quote pattern matching (`QuoteMatching`). +These interfaces are added to the self-type of the trait to make sure they are implemented on this object but not visible to users of `Quotes`. + +Internally, the implementation of `Quotes` will also track its current splicing scope `Scope`. +This scope will be attached to any expression that is created using this `Quotes` instance. + +```scala +trait Quotes: + this: runtime.QuoteUnpickler & runtime.QuoteMatching => + + extension [T](self: Expr[T]) + def show: String + def matches(that: Expr[Any]): Boolean + def value(using FromExpr[T]): Option[T] + def valueOrAbort(using FromExpr[T]): T + end extension + + extension (self: Expr[Any]) + def isExprOf[X](using Type[X]): Boolean + def asExprOf[X](using Type[X]): Expr[X] + end extension + + // abstract object reflect ... +``` + + +##### `Scope` +The splice context is represented as a stack (immutable list) of `Scope` objects. +Each `Scope` contains the position of the splice (used for error reporting) and a reference to the enclosing splice scope `Scope`. +A scope is a sub-scope of another if the other is contained in its parents. +This check is performed when an expression is spliced into another using the `Scope` provided in the current scope in `Quotes` and the one in the `Expr` or `Type`. + +### Entry Points +The two entry points for multi-stage programming are macros and the `run` operation. + +#### Macros +Inline macro definitions will inline a top-level splice (a splice not nested in a quote). +This splice needs to be evaluated at compile-time. +In _Avoiding a complete interpreter_[^1], we stated the following restrictions: + + * The top-level splice must contain a single call to a compiled static method. + * Arguments to the function are either literal constants, quoted expressions (parameters), `Type.of` for type parameters and a reference to `Quotes`. + +These restrictions make the implementation of the interpreter quite simple. +Java Reflection is used to call the single function call in the top-level splice. +The execution of that function is entirely done on compiled bytecode. +These are Scala static methods and may not always become Java static methods, they might be inside module objects. +As modules are encoded as class instances, we need to interpret the prefix of the method to instantiate it before we can invoke the method. + +The code of the arguments has not been compiled and therefore needs to be interpreted by the compiler. +Interpreting literal constants is as simple as extracting the constant from the AST that represents literals. +When interpreting a quoted expression, the contents of the quote is kept as an AST which is wrapped inside the implementation of `Expr`. +Calls to `Type.of[T]` also wrap the AST of the type inside the implementation of `Type`. +Finally, the reference to `Quotes` is supposed to be the reference to the quotes provided by the splice. +This reference is interpreted as a new instance of `Quotes` that contains a fresh initial `Scope` with no parents. + +The result of calling the method via Java Reflection will return an `Expr` containing a new AST that was generated by the implementation of that macro. +The scope of this `Expr` is checked to make sure it did not extrude from some splice or `run` operation. +Then the AST is extracted from the `Expr` and it is inserted as replacement for the AST that contained the top-level splice. + + +#### Run-time Multi-Stage Programming + +To be able to compile the code, the `scala.quoted.staging` library defines the `Compiler` trait. +An instance of `staging.Compiler` is a wrapper over the normal Scala~3 compiler. +To be instantiated it requires an instance of the JVM _classloader_ of the application. + +```scala +import scala.quoted.staging.* +given Compiler = Compiler.make(getClass.getClassLoader) +``` + +The classloader is needed for the compiler to know which dependencies have been loaded and to load the generated code using the same classloader. + +```scala +def mkPower2()(using Quotes): Expr[Double => Double] = ... + +run(mkPower2()) +``` +To run the previous example, the compiler will create code equivalent to the following class and compile it using a new `Scope` without parents. + +```scala +class RunInstance: + def exec(): Double => Double = ${ mkPower2() } +``` +Finally, `run` will interpret `(new RunInstance).exec()` to evaluate the contents of the quote. +To do this, the resulting `RunInstance` class is loaded in the JVM using Java Reflection, instantiated and then the `exec` method is invoked. + + +### Compilation + +Quotes and splices are primitive forms in the generated typed abstract syntax trees. +These need to be type-checked with some extra rules, e.g., staging levels need to be checked and the references to generic types need to be adapted. +Finally, quoted expressions that will be generated at run-time need to be encoded (serialized) and decoded (deserialized). + +#### Typing Quoted Expressions + +The typing process for quoted expressions and splices with `Expr` is relatively straightforward. +At its core, quotes are desugared into calls to `quote`, splices are desugared into calls to `splice`. +We track the quotation level when desugaring into these methods. + + +```scala +def quote[T](x: T): Quotes ?=> Expr[T] + +def splice[T](x: Quotes ?=> Expr[T]): T +``` + +It would be impossible to track the quotation levels if users wrote calls to these methods directly. +To know if it is a call to one of those methods we would need to type it first, but to type it we would need to know if it is one of these methods to update the quotation level. +Therefore these methods can only be used by the compiler. + +At run-time, the splice needs to have a reference to the `Quotes` that created its surrounding quote. +To simplify this for later phases, we track the current `Quotes` and encode a reference directly in the splice using `nestedSplice` instead of `splice`. + +```scala +def nestedSplice[T](q: Quotes)(x: q.Nested ?=> Expr[T]): T +``` +With this addition, the original `splice` is only used for top-level splices. + +The levels are mostly used to identify top-level splices that need to be evaluated while typing. +We do not use the quotation level to influence the typing process. +Level checking is performed at a later phase. +This ensures that a source expression in a quote will have the same elaboration as a source expression outside the quote. + + + +#### Quote Pattern Matching + +Pattern matching is defined in the trait `QuoteMatching`, which is part of the self type of `Quotes`. +It is implemented by `Quotes` but not available to users of `Quotes`. +To access it, the compiler generates a cast from `Quotes` to `QuoteMatching` and then selects one of its two members: `ExprMatch` or `TypeMatch`. +`ExprMatch` defines an `unapply` extractor method that is used to encode quote patterns and `TypeMatch` defines an `unapply` method for quoted type patterns. + +```scala +trait Quotes: + self: runtime.QuoteMatching & ... => + ... + +trait QuoteMatching: + object ExprMatch: + def unapply[TypeBindings <: Tuple, Tup <: Tuple] + (scrutinee: Expr[Any]) + (using pattern: Expr[Any]): Option[Tup] = ... + object TypeMatch: + ... +``` + +These extractor methods are only meant to be used in code generated by the compiler. +The call to the extractor that is generated has an already elaborated form that cannot be written in source, namely explicit type parameters and explicit contextual parameters. + +This extractor returns a tuple type `Tup` which cannot be inferred from the types in the method signature. +This type will be computed when typing the quote pattern and will be explicitly added to the extractor call. +To refer to type variables in arbitrary places of `Tup`, we need to define them all before their use, hence we have `TypeBindings`, which will contain all pattern type variable definitions. +The extractor also receives a given parameter of type `Expr[Any]` that will contain an expression that represents the pattern. +The compiler will explicitly add this pattern expression. +We use a given parameter because these are the only parameters we are allowed to add to the extractor call in a pattern position. + +This extractor is a bit convoluted, but it encodes away all the quotation-specific features. +It compiles the pattern down into a representation that the pattern matcher compiler phase understands. + +The quote patterns are encoded into two parts: a tuple pattern that is tasked with extracting the result of the match and a quoted expression representing the pattern. +For example, if the pattern has no `$` we will have an `EmptyTuple` as the pattern and `'{1}` to represent the pattern. + +```scala + case '{ 1 } => +// is elaborated to + case ExprMatch(EmptyTuple)(using '{1}) => +// ^^^^^^^^^^ ^^^^^^^^^^ +// pattern expression +``` +When extracting expressions, each pattern that is contained in a splice `${..}` will be placed in order in the tuple pattern. +In the following case, the `f` and `x` are placed in a tuple pattern `(f, x)`. +The type of the tuple is encoded in the `Tup` and not only in the tuple itself. +Otherwise, the extractor would return a tuple `Tuple` for which the types need to be tested which is in turn not possible due to type erasure. + +```scala + case '{ ((y: Int) => $f(y)).apply($x) } => +// is elaborated to + case ExprMatch[.., (Expr[Int => Int], Expr[Int])]((f, x))(using pattern) => +// pattern = '{ ((y: Int) => pat[Int](y)).apply(pat[Int]()) } +``` +The contents of the quote are transformed into a valid quote expression by replacing the splice with a marker expression `pat[T](..)`. +The type `T` is taken from the type of the splice and the arguments are the HOAS arguments. +This implies that a `pat[T]()` is a closed pattern and `pat[T](y)` is an HOAS pattern that can refer to `y`. + + +Type variables in quoted patterns are first normalized to have all definitions at the start of the pattern. +For each definition of a type variable `t` in the pattern we will add a type variable definition in `TypeBindings`. +Each one will have a corresponding `Type[t]` that will get extracted if the pattern matches. +These `Type[t]` are also listed in the `Tup` and added in the tuple pattern. +It is additionally marked as `using` in the pattern to make it implicitly available in this case branch. + + +```scala + case '{ type t; ($xs: List[t]).map[t](identity[t]) } => +// is elaborated to + case ExprMatch[(t), (Type[t], Expr[List[t]])]((using t, xs))(using p) => +// ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^ +// type bindings result type pattern expression +// p = '{ @patternType type u; pat[List[u]]().map[u](identity[u]) } +``` + +The contents of the quote are transformed into a valid quote expression by replacing type variables with fresh ones that do not escape the quote scope. +These are also annotated to be easily identifiable as pattern variables. + +#### Level Consistency Checking +Level consistency checking is performed after typing the program as a static check. +To check level consistency we traverse the tree top-down remembering the context staging level. +Each local definition in scope is recorded with its level and each term reference to a definition is checked against the current staging level. +```scala +// level 0 +'{ // level 1 + val x = ... // level 1 with (x -> 1) + ${ // level 0 (x -> 1) + val y = ... // level 0 with (x -> 1, y -> 0) + x // error: defined at level 1 but used in level 0 + } + // level 1 (x -> 1) + x // x is ok +} +``` + +#### Type Healing + +When using a generic type `T` in a future stage, it is necessary to have a given `Type[T]` in scope. +The compiler needs to identify those references and link them with the instance of `Type[T]`. +For instance consider the following example: + +```scala +def emptyList[T](using t: Type[T])(using Quotes): Expr[List[T]] = + '{ List.empty[T] } +``` + +For each reference to a generic type `T` that is defined at level 0 and used at level 1 or greater, the compiler will summon a `Type[T]`. +This is usually the given type that is provided as parameter, `t` in this case. +We can use the type `t.Underlying` to replace `T` as it is an alias of that type. +But `t.Underlying` contains the extra information that it is `t` that will be used in the evaluation of the quote. +In a sense, `Underlying` acts like a splice for types. + +```scala +def emptyList[T](using t: Type[T])(using Quotes): Expr[List[T]] = + '{ List.empty[t.Underlying] } +``` + +Due to some technical limitations, it is not always possible to replace the type reference with the AST containing `t.Underlying`. +To overcome this limitation, we can simply define a list of type aliases at the start of the quote and insert the `t.Underlying` there. +This has the added advantage that we do not have to repeatedly insert the `t.Underlying` in the quote. + +```scala +def emptyList[T](using t: Type[T])(using Quotes): Expr[List[T]] = + '{ type U = t.Underlying; List.empty[U] } +``` +These aliases can be used at any level within the quote and this transformation is only performed on quotes that are at level 0. + +```scala + '{ List.empty[T] ... '{ List.empty[T] } ... } +// becomes + '{ type U = t.Underlying; List.empty[U] ... '{ List.empty[U] } ... } +``` +If we define a generic type at level 1 or greater, it will not be subject to this transformation. +In some future compilation stage, when the definition of the generic type is at level 0, it will be subject to this transformation. +This simplifies the transformation logic and avoids leaking the encoding into code that a macro could inspect. + +```scala +'{ + def emptyList[T: Type](using Quotes): Expr[List[T]] = '{ List.empty[T] } + ... +} +``` +A similar transformation is performed on `Type.of[T]`. +Any generic type in `T` needs to have an implicitly given `Type[T]` in scope, which will also be used as a path. +The example: + +```scala +def empty[T](using t: Type[T])(using Quotes): Expr[T] = + Type.of[T] match ... +// becomes +def empty[T](using t: Type[T])(using Quotes): Expr[T] = + Type.of[t.Underlying] match ... +// then becomes +def empty[T](using t: Type[T])(using Quotes): Expr[T] = + t match ... +``` + +The operation `Type.of[t.Underlying]` can be optimized to just `t`. +But this is not always the case. +If the generic reference is nested in the type, we will need to keep the `Type.of`. -The phase consistency principle can be formalized in a calculus that -extends simply-typed lambda calculus with quotes and splices. +```scala +def matchOnList[T](using t: Type[T])(using Quotes): Expr[List[T]] = + Type.of[List[T]] match ... +// becomes +def matchOnList[T](using t: Type[T])(using Quotes): Expr[List[T]] = + Type.of[List[t.Underlying]] match ... +``` -### Syntax +By doing this transformation, we ensure that each abstract type `U` used in `Type.of` has an implicit `Type[U]` in scope. +This representation makes it simpler to identify parts of the type that are statically known from those that are known dynamically. +Type aliases are also added within the type of the `Type.of` though these are not valid source code. +These would look like `Type.of[{type U = t.Underlying; Map[U, U]}]` if written in source code. -The syntax of terms, values, and types is given as follows: -```ebnf -Terms t ::= x variable - (x: T) => t lambda - t t application - 't quote - $t splice -Values v ::= (x: T) => t lambda - 'u quote +#### Splice Normalization -Simple terms u ::= x | (x: T) => u | u u | 't +The contents of a splice may refer to variables defined in the enclosing quote. +This complicates the process of serialization of the contents of the quotes. +To make serialization simple, we first transform the contents of each level 1 splice. +Consider the following example: -Types T ::= A base type - T -> T function type - expr T quoted +```scala +def power5to(n: Expr[Int]): Expr[Double] = '{ + val x: Int = 5 + ${ powerCode('{x}, n) } +} ``` -Typing rules are formulated using a stack of environments -`Es`. Individual environments `E` consist as usual of variable -bindings `x: T`. Environments can be combined using the two -combinators `'` and `$`. -```ebnf -Environment E ::= () empty - E, x: T -Env. stack Es ::= () empty - E simple - Es * Es combined +The variable `x` is defined in the quote and used in the splice. +The normal form will extract all references to `x` and replace them with a staged version of `x`. +We will replace the reference to `x` of type `T` with a `$y` where `y` is of type `Expr[T]`. +Then we wrap the new contents of the splice in a lambda that defines `y` and apply it to the quoted version of `x`. +After this transformation we have 2 parts, a lambda without references to the quote, which knows how to compute the contents of the splice, and a sequence of quoted arguments that refer to variables defined in the lambda. -Separator * ::= ' - $ +```scala +def power5to(n: Expr[Int]): Expr[Double] = '{ + val x: Int = 5 + ${ ((y: Expr[Int]) => powerCode('{$y}, n)).apply('x) } +} ``` -The two environment combinators are both associative with left and -right identity `()`. -### Operational semantics +In general, the splice normal form has the shape `${ .apply(*) }` and the following constraints: + * `` a lambda expression that does not refer to variables defined in the outer quote + * `` sequence of quoted expressions or `Type.of` containing references to variables defined in the enclosing quote and no references to local variables defined outside the enclosing quote -We define a small step reduction relation `-->` with the following rules: + +##### Function references normalization +A reference to a function `f` that receives parameters is not a valid value in Scala. +Such a function reference `f` can be eta-expaned as `x => f(x)` to be used as a lambda value. +Therefore function references cannot be transformed by the normalization as directly as other expressions as we cannot represent `'{f}` with a method reference type. +We can use the eta-expanded form of `f` in the normalized form. +For example, consider the reference to `f` below. + +```scala +'{ + def f(a: Int)(b: Int, c: Int): Int = 2 + a + b + c + ${ '{ f(3)(4, 5) } } +} ``` - ((x: T) => t) v --> [x := v]t - ${'u} --> u +To normalize this code, we can eta-expand the reference to `f` and place it in a quote containing a proper expression. +Therefore the normalized form of the argument `'{f}` becomes the quoted lambda `'{ (a: Int) => (b: Int, c: Int) => f(a)(b, c) }` and is an expression of type `Expr[Int => (Int, Int) => Int]`. +The eta-expansion produces one curried lambda per parameter list. +The application `f(3)(4, 5)` does not become `$g(3)(4, 5)` but `$g.apply(3).apply(4, 5)`. +We add the `apply` because `g` is not a quoted reference to a function but a curried lambda. - t1 --> t2 - ----------------- - e[t1] --> e[t2] +```scala +'{ + def f(a: Int)(b: Int, c: Int): Int = 2 + a + b + c + ${ + ( + (g: Expr[Int => (Int, Int) => Int]) => '{$g.apply(3).apply(4, 5)} + ).apply('{ (a: Int) => (b: Int, c: Int) => f(a)(b, c) }) + } +} ``` -The first rule is standard call-by-value beta-reduction. The second -rule says that splice and quotes cancel each other out. The third rule -is a context rule; it says that reduction is allowed in the hole `[ ]` -position of an evaluation context. Evaluation contexts `e` and -splice evaluation context `e_s` are defined syntactically as follows: -```ebnf -Eval context e ::= [ ] | e t | v e | 'e_s[${e}] -Splice context e_s ::= [ ] | (x: T) => e_s | e_s t | u e_s + +Then we can apply it and beta-reduce the application when generating the code. + +```scala + (g: Expr[Int => Int => Int]) => betaReduce('{$g.apply(3).apply(4)}) ``` -### Typing rules -Typing judgments are of the form `Es |- t: T`. There are two -substructural rules which express the fact that quotes and splices -cancel each other out: +##### Variable assignment normalization +A reference to a mutable variable in the left-hand side of an assignment cannot be transformed directly as it is not in an expression position. +```scala +'{ + var x: Int = 5 + ${ g('{x = 2}) } +} ``` - Es1 * Es2 |- t: T - --------------------------- - Es1 $ E1 ' E2 * Es2 |- t: T +We can use the same strategy used for function references by eta-expanding the assignment operation `x = _` into `y => x = y`. - Es1 * Es2 |- t: T - --------------------------- - Es1 ' E1 $ E2 * Es2 |- t: T +```scala +'{ + var x: Int = 5 + ${ + g( + ( + (f: Expr[Int => Unit]) => betaReduce('{$f(2)}) + ).apply('{ (y: Int) => x = $y }) + ) + } +} ``` -The lambda calculus fragment of the rules is standard, except that we -use a stack of environments. The rules only interact with the topmost -environment of the stack. + + +##### Type normalization +Types defined in the quote are subject to a similar transformation. +In this example, `T` is defined within the quote at level 1 and used in the splice again at level 1. + +```scala +'{ def f[T] = ${ '{g[T]} } } ``` - x: T in E - -------------- - Es * E |- x: T +The normalization will add a `Type[T]` to the lambda, and we will insert this reference. +The difference is that it will add an alias similar to the one used in type healing. +In this example, we create a `type U` that aliases the staged type. + +```scala +'{ + def f[T] = ${ + ( + (t: Type[T]) => '{type U = t.Underling; g[U]} + ).apply(Type.of[T]) + } +} +``` - Es * E, x: T1 |- t: T2 - ------------------------------- - Es * E |- (x: T1) => t: T -> T2 +#### Serialization +Quoted code needs to be pickled to make it available at run-time in the next compilation phase. +We implement this by pickling the AST as a TASTy binary. - Es |- t1: T2 -> T Es |- t2: T2 - --------------------------------- - Es |- t1 t2: T +##### TASTy +The TASTy format is the typed abstract syntax tree serialization format of Scala 3. +It usually pickles the fully elaborated code after type-checking and is kept along the generated Java classfiles. + + +##### Pickling +We use TASTy as a serialization format for the contents of the quotes. +To show how serialization is performed, we will use the following example. +```scala +'{ + val (x, n): (Double, Int) = (5, 2) + ${ powerCode('{x}, '{n}) } * ${ powerCode('{2}, '{n}) } +} ``` -The rules for quotes and splices map between `expr T` and `T` by trading `'` and `$` between -environments and terms. + +This quote is transformed into the following code when normalizing the splices. + +```scala +'{ + val (x, n): (Double, Int) = (5, 2) + ${ + ((y: Expr[Double], m: Expr[Int]) => powerCode(y, m)).apply('x, 'n) + } * ${ + ((m: Expr[Int]) => powerCode('{2}, m)).apply('n) + } +} ``` - Es $ () |- t: expr T - -------------------- - Es |- $t: T +Splice normalization is a key part of the serialization process as it only allows references to variables defined in the quote in the arguments of the lambda in the splice. +This makes it possible to create a closed representation of the quote without much effort. +The first step is to remove all the splices and replace them with holes. +A hole is like a splice but it lacks the knowledge of how to compute the contents of the splice. +Instead, it knows the index of the hole and the contents of the arguments of the splice. +We can see this transformation in the following example where a hole is represented by `<< idx; holeType; args* >>`. - Es ' () |- t: T - ---------------- - Es |- 't: expr T +```scala + ${ ((y: Expr[Double], m: Expr[Int]) => powerCode(y, m)).apply('x, 'n) } +// becomes + << 0; Double; x, n >> ``` -The meta theory of a slightly simplified 2-stage variant of this calculus -is studied [separately](./simple-smp.md). -## Going Further +As this was the first hole it has index 0. +The hole type is `Double`, which needs to be remembered now that we cannot infer it from the contents of the splice. +The arguments of the splice are `x` and `n`; note that they do not require quoting because they were moved out of the splice. -The metaprogramming framework as presented and currently implemented is quite restrictive -in that it does not allow for the inspection of quoted expressions and -types. It’s possible to work around this by providing all necessary -information as normal, unquoted inline parameters. But we would gain -more flexibility by allowing for the inspection of quoted code with -pattern matching. This opens new possibilities. +References to healed types are handled in a similar way. +Consider the `emptyList` example, which shows the type aliases that are inserted into the quote. +```scala +'{ List.empty[T] } +// type healed to +'{ type U = t.Underlying; List.empty[U] } +``` +Instead of replacing a splice, we replace the `t.Underlying` type with a type hole. +The type hole is represented by `<< idx; bounds >>`. +```scala +'{ type U = << 0; Nothing..Any >>; List.empty[U] } +``` +Here, the bounds of `Nothing..Any` are the bounds of the original `T` type. +The types of a `Type.of` are transformed in the same way. + + +With these transformations, the contents of the quote or `Type.of` are guaranteed to be closed and therefore can be pickled. +The AST is pickled into TASTy, which is a sequence of bytes. +This sequence of bytes needs to be instantiated in the bytecode, but unfortunately it cannot be dumped into the classfile as bytes. +To reify it we encode the bytes into a Java `String`. +In the following examples we display this encoding in human readable form with the fictitious |tasty"..."| string literal. -For instance, here is a version of `power` that generates the multiplications -directly if the exponent is statically known and falls back to the dynamic -implementation of `power` otherwise. ```scala -import scala.quoted.* +// pickled AST bytes encoded in a base64 string +tasty""" + val (x, n): (Double, Int) = (5, 2) + << 0; Double; x, n >> * << 1; Double; n >> +""" +// or +tasty""" + type U = << 0; Nothing..Any; >> + List.empty[U] +""" +``` +The contents of a quote or `Type.of` are not always pickled. +In some cases it is better to generate equivalent (smaller and/or faster) code that will compute the expression. +Literal values are compiled into a call to `Expr()` using the implementation of `ToExpr` to create the quoted expression. +This is currently performed only on literal values, but can be extended to any value for which we have a `ToExpr` defined in the standard library. +Similarly, for non-generic types we can use their respective `java.lang.Class` and convert them into a `Type` using a primitive operation `typeConstructorOf` defined in the reflection API. -inline def power(x: Double, n: Int): Double = - ${ powerExpr('x, 'n) } +##### Unpickling -private def powerExpr(x: Expr[Double], n: Expr[Int]) - (using Quotes): Expr[Double] = - n.value match - case Some(m) => powerExpr(x, m) - case _ => '{ dynamicPower($x, $n) } +Now that we have seen how a quote is pickled, we can look at how to unpickle it. +We will continue with the previous example. -private def powerExpr(x: Expr[Double], n: Int) - (using Quotes): Expr[Double] = - if n == 0 then '{ 1.0 } - else if n == 1 then x - else if n % 2 == 0 then '{ val y = $x * $x; ${ powerExpr('y, n / 2) } } - else '{ $x * ${ powerExpr(x, n - 1) } } +Holes were used to replace the splices in the quote. +When we perform this transformation we also need to remember the lambdas from the splices and their hole index. +When unpickling a hole, the corresponding splice lambda will be used to compute the contents of the hole. +The lambda will receive as parameters quoted versions of the arguments of the hole. +For example to compute the contents of `<< 0; Double; x, n >>` we will evaluate the following code -private def dynamicPower(x: Double, n: Int): Double = - if n == 0 then 1.0 - else if n % 2 == 0 then dynamicPower(x * x, n / 2) - else x * dynamicPower(x, n - 1) +```scala + ((y: Expr[Double], m: Expr[Int]) => powerCode(y, m)).apply('x, 'n) ``` -In the above, the method `.value` maps a constant expression of the type -`Expr[T]` to its value of the type `T`. +The evaluation is not as trivial as it looks, because the lambda comes from compiled code and the rest is code that must be interpreted. +We put the AST of `x` and `n` into `Expr` objects to simulate the quotes and then we use Java Reflection to call the `apply` method. + +We may have many holes in a quote and therefore as many lambdas. +To avoid the instantiation of many lambdas, we can join them together into a single lambda. +Apart from the list of arguments, this lambda will also take the index of the hole that is being evaluated. +It will perform a switch match on the index and call the corresponding lambda in each branch. +Each branch will also extract the arguments depending on the definition of the lambda. +The application of the original lambdas are beta-reduced to avoid extra overhead. -With the right extractors, the "AsFunction" conversion -that maps expressions over functions to functions over expressions can -be implemented in user code: ```scala -given AsFunction1[T, U]: Conversion[Expr[T => U], Expr[T] => Expr[U]] with - def apply(f: Expr[T => U]): Expr[T] => Expr[U] = - (x: Expr[T]) => f match - case Lambda(g) => g(x) - case _ => '{ ($f)($x) } +(idx: Int, args: Seq[Any]) => + idx match + case 0 => // for << 0; Double; x, n >> + val x = args(0).asInstanceOf[Expr[Double]] + val n = args(1).asInstanceOf[Expr[Int]] + powerCode(x, n) + case 1 => // for << 1; Double; n >> + val n = args(0).asInstanceOf[Expr[Int]] + powerCode('{2}, n) ``` -This assumes an extractor + +This is similar to what we do for splices when we replace the type aliased with holes we keep track of the index of the hole. +Instead of lambdas, we will have a list of references to instances of `Type`. +From the following example we would extract `t`, `u`, ... . + ```scala -object Lambda: - def unapply[T, U](x: Expr[T => U]): Option[Expr[T] => Expr[U]] + '{ type T1 = t1.Underlying; type Tn = tn.Underlying; ... } +// with holes + '{ type T1 = << 0; ... >>; type Tn = << n-1; ... >>; ... } ``` -Once we allow inspection of code via extractors, it’s tempting to also -add constructors that create typed trees directly without going -through quotes. Most likely, those constructors would work over `Expr` -types which lack a known type argument. For instance, an `Apply` -constructor could be typed as follows: + +As the type holes are at the start of the quote, they will have the first `N` indices. +This implies that we can place the references in a sequence `Seq(t, u, ...)` where the index in the sequence is the same as the hole index. + +Lastly, the quote itself is replaced by a call to `QuoteUnpickler.unpickleExpr` which will unpickle the AST, evaluate the holes, i.e., splices, and wrap the resulting AST in an `Expr[Int]`. +This method takes takes the pickled |tasty"..."|, the types and the hole lambda. +Similarly, `Type.of` is replaced with a call to `QuoteUnpickler.unpickleType` but only receives the pickled |tasty"..."| and the types. +Because `QuoteUnpickler` is part of the self-type of the `Quotes` class, we have to cast the instance but know that this cast will always succeed. + ```scala -def Apply(fn: Expr[Any], args: List[Expr[Any]]): Expr[Any] +quotes.asInstanceOf[runtime.QuoteUnpickler].unpickleExpr[T]( + pickled = tasty"...", + types = Seq(...), + holes = (idx: Int, args: Seq[Any]) => idx match ... +) ``` -This would allow constructing applications from lists of arguments -without having to match the arguments one-by-one with the -corresponding formal parameter types of the function. We then need "at -the end" a method to convert an `Expr[Any]` to an `Expr[T]` where `T` is -given from the outside. For instance, if `code` yields a `Expr[Any]`, then -`code.atType[T]` yields an `Expr[T]`. The `atType` method has to be -implemented as a primitive; it would check that the computed type -structure of `Expr` is a subtype of the type structure representing -`T`. -Before going down that route, we should evaluate in detail the tradeoffs it -presents. Constructing trees that are only verified _a posteriori_ -to be type correct loses a lot of guidance for constructing the right -trees. So we should wait with this addition until we have more -use-cases that help us decide whether the loss in type-safety is worth -the gain in flexibility. In this context, it seems that deconstructing types is -less error-prone than deconstructing terms, so one might also -envisage a solution that allows the former but not the latter. - -## Conclusion - -Metaprogramming has a reputation of being difficult and confusing. -But with explicit `Expr/Type` types and quotes and splices it can become -downright pleasant. A simple strategy first defines the underlying quoted or unquoted -values using `Expr` and `Type` and then inserts quotes and splices to make the types -line up. Phase consistency is at the same time a great guideline -where to insert a splice or a quote and a vital sanity check that -the result makes sense. +[^1]: [Scalable Metaprogramming in Scala 3](https://infoscience.epfl.ch/record/299370) +[^2]: [Multi-stage programming with generative and analytical macros](https://dl.acm.org/doi/10.1145/3486609.3487203). +[^3]: In quotes, identifiers starting with `$` must be surrounded by backticks (`` `$` ``). For example `$conforms` from `scala.Predef`. diff --git a/docs/_docs/reference/metaprogramming/macros.md b/docs/_docs/reference/metaprogramming/macros.md index 0be48ef2baf8..1233d0007bc5 100644 --- a/docs/_docs/reference/metaprogramming/macros.md +++ b/docs/_docs/reference/metaprogramming/macros.md @@ -6,843 +6,617 @@ nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/macros.h > When developing macros enable `-Xcheck-macros` scalac option flag to have extra runtime checks. -## Macros: Quotes and Splices +## Multi-Staging -Macros are built on two well-known fundamental operations: quotation and splicing. -Quotation is expressed as `'{...}` for expressions and splicing is expressed as `${ ... }`. -Additionally, within a quote or a splice we can quote or splice identifiers directly (i.e. `'e` and `$e`). -Readers may notice the resemblance of the two aforementioned syntactic -schemes with the familiar string interpolation syntax. +#### Quoted expressions +Multi-stage programming in Scala 3 uses quotes `'{..}` to delay, i.e., stage, execution of code and splices `${..}` to evaluate and insert code into quotes. +Quoted expressions are typed as `Expr[T]` with a covariant type parameter `T`. +It is easy to write statically safe code generators with these two concepts. +The following example shows a naive implementation of the $x^n$ mathematical operation. ```scala -println(s"Hello, $name, here is the result of 1 + 1 = ${1 + 1}") +import scala.quoted.* +def unrolledPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = + if n == 0 then '{ 1.0 } + else if n == 1 then x + else '{ $x * ${ unrolledPowerCode(x, n-1) } } ``` -In string interpolation we _quoted_ a string and then we _spliced_ into it, two others. The first, `name`, is a reference to a value of type [`String`](https://scala-lang.org/api/3.x/scala/Predef$.html#String-0), and the second is an arithmetic expression that will be _evaluated_ followed by the splicing of its string representation. - -Quotes and splices in this section allow us to treat code in a similar way, -effectively supporting macros. The entry point for macros is an inline method -with a top-level splice. We call it a top-level because it is the only occasion -where we encounter a splice outside a quote (consider as a quote the -compilation-unit at the call-site). For example, the code below presents an -`inline` method `assert` which calls at compile-time a method `assertImpl` with -a boolean expression tree as argument. `assertImpl` evaluates the expression and -prints it again in an error message if it evaluates to `false`. - ```scala -import scala.quoted.* - -inline def assert(inline expr: Boolean): Unit = - ${ assertImpl('expr) } - -def assertImpl(expr: Expr[Boolean])(using Quotes) = '{ - if !$expr then - throw AssertionError(s"failed assertion: ${${ showExpr(expr) }}") +'{ + val x = ... + ${ unrolledPowerCode('{x}, 3) } // evaluates to: x * x * x } - -def showExpr(expr: Expr[Boolean])(using Quotes): Expr[String] = - '{ [actual implementation later in this document] } ``` -If `e` is an expression, then `'{e}` represents the typed -abstract syntax tree representing `e`. If `T` is a type, then `Type.of[T]` -represents the type structure representing `T`. The precise -definitions of "typed abstract syntax tree" or "type-structure" do not -matter for now, the terms are used only to give some -intuition. Conversely, `${e}` evaluates the expression `e`, which must -yield a typed abstract syntax tree or type structure, and embeds the -result as an expression (respectively, type) in the enclosing program. +Quotes and splices are duals of each other. +For an arbitrary expression `x` of type `T` we have `${'{x}} = x` and for an arbitrary expression `e` of type `Expr[T]` we have `'{${e}} = e`. -Quotations can have spliced parts in them; in this case the embedded -splices are evaluated and embedded as part of the formation of the -quotation. +#### Abstract types +Quotes can handle generic and abstract types using the type class `Type[T]`. +A quote that refers to a generic or abstract type `T` requires a given `Type[T]` to be provided in the implicit scope. +The following examples show how `T` is annotated with a context bound (`: Type`) to provide an implicit `Type[T]`, or the equivalent `using Type[T]` parameter. -Quotes and splices can also be applied directly to identifiers. An identifier -`$x` starting with a `$` that appears inside a quoted expression or type is treated as a -splice `${x}`. Analogously, an quoted identifier `'x` that appears inside a splice -is treated as a quote `'{x}`. See the Syntax section below for details. +```scala +import scala.quoted.* +def singletonListExpr[T: Type](x: Expr[T])(using Quotes): Expr[List[T]] = + '{ List[T]($x) } // generic T used within a quote -Quotes and splices are duals of each other. -For arbitrary expressions `e` we have: +def emptyListExpr[T](using Type[T], Quotes): Expr[List[T]] = + '{ List.empty[T] } // generic T used within a quote +``` +If no other instance is found, the default `Type.of[T]` is used. +The following example implicitly uses `Type.of[String]` and `Type.of[Option[U]]`. ```scala -${'{e}} = e -'{${e}} = e +val list1: Expr[List[String]] = + singletonListExpr('{"hello"}) // requires a given `Type[Sting]` +val list0: Expr[List[Option[T]]] = + emptyListExpr[Option[U]] // requires a given `Type[Option[U]]` ``` -## Types for Quotations - -The type signatures of quotes and splices can be described using -two fundamental types: -- `Expr[T]`: abstract syntax trees representing expressions of type `T` -- `Type[T]`: non erased representation of type `T`. - -Quoting takes expressions of type `T` to expressions of type `Expr[T]` -and it takes types `T` to expressions of type `Type[T]`. Splicing -takes expressions of type `Expr[T]` to expressions of type `T` and it -takes expressions of type `Type[T]` to types `T`. - -The two types can be defined in package [`scala.quoted`](https://scala-lang.org/api/3.x/scala/quoted.html) as follows: +The `Type.of[T]` method is a primitive operation that the compiler will handle specially. +It will provide the implicit if the type `T` is statically known, or if `T` contains some other types `Ui` for which we have an implicit `Type[Ui]`. +In the example, `Type.of[String]` has a statically known type and `Type.of[Option[U]]` requires an implicit `Type[U]` in scope. +#### Quote context +We also track the current quotation context using a given `Quotes` instance. +To create a quote `'{..}` we require a given `Quotes` context, which should be passed as a contextual parameter `(using Quotes)` to the function. +Each splice will provide a new `Quotes` context within the scope of the splice. +Therefore quotes and splices can be seen as methods with the following signatures, but with special semantics. ```scala -package scala.quoted +def '[T](x: T): Quotes ?=> Expr[T] // def '[T](x: T)(using Quotes): Expr[T] -sealed trait Expr[+T] -sealed trait Type[T] +def $[T](x: Quotes ?=> Expr[T]): T ``` -Both `Expr` and `Type` are abstract and sealed, so all constructors for -these types are provided by the system. One way to construct values of -these types is by quoting, the other is by type-specific lifting -operations that will be discussed later on. - -## The Phase Consistency Principle - -A fundamental *phase consistency principle* (PCP) regulates accesses -to free variables in quoted and spliced code: - -- _For any free variable reference `x`, the number of quoted scopes and the number of spliced scopes between the reference to `x` and the definition of `x` must be equal_. - -Here, `this`-references count as free variables. On the other -hand, we assume that all imports are fully expanded and that `_root_` is -not a free variable. So references to global definitions are -allowed everywhere. - -The phase consistency principle can be motivated as follows: First, -suppose the result of a program `P` is some quoted text `'{ ... x -... }` that refers to a free variable `x` in `P`. This can be -represented only by referring to the original variable `x`. Hence, the -result of the program will need to persist the program state itself as -one of its parts. We don’t want to do this, hence this situation -should be made illegal. Dually, suppose a top-level part of a program -is a spliced text `${ ... x ... }` that refers to a free variable `x` -in `P`. This would mean that we refer during _construction_ of `P` to -a value that is available only during _execution_ of `P`. This is of -course impossible and therefore needs to be ruled out. Now, the -small-step evaluation of a program will reduce quotes and splices in -equal measure using the cancellation rules above. But it will neither -create nor remove quotes or splices individually. So the PCP ensures -that program elaboration will lead to neither of the two unwanted -situations described above. - -In what concerns the range of features it covers, this form of macros introduces -a principled metaprogramming framework that is quite close to the MetaML family of -languages. One difference is that MetaML does not have an equivalent of the PCP - -quoted code in MetaML _can_ access variables in its immediately enclosing -environment, with some restrictions and caveats since such accesses involve -serialization. However, this does not constitute a fundamental gain in -expressiveness. +The lambda with a question mark `?=>` is a contextual function; it is a lambda that takes its argument implicitly and provides it implicitly in the implementation the lambda. +`Quotes` are used for a variety of purposes that will be mentioned when covering those topics. -## From `Expr`s to Functions and Back +## Quoted Values -It is possible to convert any `Expr[T => R]` into `Expr[T] => Expr[R]` and back. -These conversions can be implemented as follows: +#### Lifting +While it is not possible to use cross-stage persistence of local variables, it is possible to lift them to the next stage. +To this end, we provide the `Expr.apply` method, which can take a value and lift it into a quoted representation of the value. ```scala -def to[T: Type, R: Type](f: Expr[T] => Expr[R])(using Quotes): Expr[T => R] = - '{ (x: T) => ${ f('x) } } +val expr1plus1: Expr[Int] = '{ 1 + 1 } -def from[T: Type, R: Type](f: Expr[T => R])(using Quotes): Expr[T] => Expr[R] = - (x: Expr[T]) => '{ $f($x) } +val expr2: Expr[Int] = Expr(1 + 1) // lift 2 into '{ 2 } ``` -Note how the fundamental phase consistency principle works in two -different directions here for `f` and `x`. In the method `to`, the reference to `f` is -legal because it is quoted, then spliced, whereas the reference to `x` -is legal because it is spliced, then quoted. - -They can be used as follows: +While it looks type wise similar to `'{ 1 + 1 }`, the semantics of `Expr(1 + 1)` are quite different. +`Expr(1 + 1)` will not stage or delay any computation; the argument is evaluated to a value and then lifted into a quote. +The quote will contain code that will create a copy of this value in the next stage. +`Expr` is polymorphic and user-extensible via the `ToExpr` type class. ```scala -val f1: Expr[Int => String] = - to((x: Expr[Int]) => '{ $x.toString }) // '{ (x: Int) => x.toString } - -val f2: Expr[Int] => Expr[String] = - from('{ (x: Int) => x.toString }) // (x: Expr[Int]) => '{ ((x: Int) => x.toString)($x) } -f2('{2}) // '{ ((x: Int) => x.toString)(2) } +trait ToExpr[T]: + def apply(x: T)(using Quotes): Expr[T] ``` -One limitation of `from` is that it does not β-reduce when a lambda is called immediately, as evidenced in the code `{ ((x: Int) => x.toString)(2) }`. -In some cases we want to remove the lambda from the code, for this we provide the method `Expr.betaReduce` that turns a tree -describing a function into a function mapping trees to trees. +We can implement a `ToExpr` using a `given` definition that will add the definition to the implicits in scope. +In the following example we show how to implement a `ToExpr[Option[T]]` for any liftable type `T. ```scala -object Expr: - ... - def betaReduce[T](expr: Expr[T])(using Quotes): Expr[T] +given OptionToExpr[T: Type: ToExpr]: ToExpr[Option[T]] with + def apply(opt: Option[T])(using Quotes): Expr[Option[T]] = + opt match + case Some(x) => '{ Some[T]( ${Expr(x)} ) } + case None => '{ None } ``` -`Expr.betaReduce` returns an expression that is functionally equivalent to e, however if e is of the form `((y1, ..., yn) => e2)(e1, ..., en)` then it optimizes the top most call by returning the result of beta-reducing the application. Otherwise returns expr. +The `ToExpr` for primitive types must be implemented as primitive operations in the system. +In our case, we use the reflection API to implement them. -## Lifting Types - -Types are not directly affected by the phase consistency principle. -It is possible to use types defined at any level in any other level. -But, if a type is used in a subsequent stage it will need to be lifted to a `Type`. -Indeed, the definition of `to` above uses `T` in the next stage, there is a -quote but no splice between the parameter binding of `T` and its -usage. But the code can be rewritten by adding an explicit binding of a `Type[T]`: +#### Extracting values from quotes +To be able to generate optimized code using the method `unrolledPowerCode`, the macro implementation `powerCode` needs to first +determine whether the argument passed as parameter `n` is a known constant value. +This can be achieved via _unlifting_ using the `Expr.unapply` extractor from our library implementation, which will only match if `n` is a quoted constant and extracts its value. ```scala -def to[T, R](f: Expr[T] => Expr[R])(using t: Type[T], r: Type[R])(using Quotes): Expr[T => R] = - '{ (x: t.Underlying) => ${ f('x) } } +def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = + n match + case Expr(m) => // it is a constant: unlift code n='{m} into number m + unrolledPowerCode(x, m) + case _ => // not known: call power at run-time + '{ power($x, $n) } ``` -In this version of `to`, the type of `x` is now the result of -inserting the type `Type[T]` and selecting its `Underlying`. - -To avoid clutter, the compiler converts any type reference to -a type `T` in subsequent phases to `summon[Type[T]].Underlying`. - -And to avoid duplication it does it once per type, and creates -an alias for that type at the start of the quote. - -For instance, the user-level definition of `to`: - +Alternatively, the `n.value` method can be used to get an `Option[Int]` with the value or `n.valueOrAbort` to get the value directly. ```scala -def to[T, R](f: Expr[T] => Expr[R])(using t: Type[T], r: Type[R])(using Quotes): Expr[T => R] = - '{ (x: T) => ${ f('x) } } +def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = + // emits an error message if `n` is not a constant + unrolledPowerCode(x, n.valueOrAbort) ``` -would be rewritten to +`Expr.unapply` and all variants of `value` are polymorphic and user-extensible via a given `FromExpr` type class. ```scala -def to[T, R](f: Expr[T] => Expr[R])(using t: Type[T], r: Type[R])(using Quotes): Expr[T => R] = - '{ - type T = summon[Type[T]].Underlying - (x: T) => ${ f('x) } - } +trait FromExpr[T]: + def unapply(x: Expr[T])(using Quotes): Option[T] ``` -The `summon` query succeeds because there is a using parameter of -type `Type[T]`, and the reference to that value is -phase-correct. If that was not the case, the phase inconsistency for -`T` would be reported as an error. - -## Lifting Expressions +We can use `given` definitions to implement the `FromExpr` as we did for `ToExpr`. +The `FromExpr` for primitive types must be implemented as primitive operations in the system. +In our case, we use the reflection API to implement them. +To implement `FromExpr` for non-primitive types we use quote pattern matching (for example `OptionFromExpr`). -Consider the following implementation of a staged interpreter that implements -a compiler through staging. -```scala -import scala.quoted.* +## Macros and Multi-Stage Programming -enum Exp: - case Num(n: Int) - case Plus(e1: Exp, e2: Exp) - case Var(x: String) - case Let(x: String, e: Exp, in: Exp) +The system supports multi-stage macros and run-time multi-stage programming using the same quotation abstractions. -import Exp.* -``` +### Multi-Stage Macros -The interpreted language consists of numbers `Num`, addition `Plus`, and variables -`Var` which are bound by `Let`. Here are two sample expressions in the language: +#### Macros +We can generalize the splicing abstraction to express macros. +A macro consists of a top-level splice that is not nested in any quote. +Conceptually, the contents of the splice are evaluated one stage earlier than the program. +In other words, the contents are evaluated while compiling the program. The generated code resulting from the macro replaces the splice in the program. ```scala -val exp = Plus(Plus(Num(2), Var("x")), Num(4)) -val letExp = Let("x", Num(3), exp) +def power2(x: Double): Double = + ${ unrolledPowerCode('x, 2) } // x * x ``` -Here’s a compiler that maps an expression given in the interpreted -language to quoted Scala code of type `Expr[Int]`. -The compiler takes an environment that maps variable names to Scala `Expr`s. +#### Inline macros +Since using the splices in the middle of a program is not as ergonomic as calling a function; we hide the staging mechanism from end-users of macros. We have a uniform way of calling macros and normal functions. +For this, _we restrict the use of top-level splices to only appear in inline methods_[^1][^2]. ```scala -import scala.quoted.* +// inline macro definition +inline def powerMacro(x: Double, inline n: Int): Double = + ${ powerCode('x, 'n) } -def compile(e: Exp, env: Map[String, Expr[Int]])(using Quotes): Expr[Int] = - e match - case Num(n) => - Expr(n) - case Plus(e1, e2) => - '{ ${ compile(e1, env) } + ${ compile(e2, env) } } - case Var(x) => - env(x) - case Let(x, e, body) => - '{ val y = ${ compile(e, env) }; ${ compile(body, env + (x -> 'y)) } } +// user code +def power2(x: Double): Double = + powerMacro(x, 2) // x * x ``` -Running `compile(letExp, Map())` would yield the following Scala code: - -```scala -'{ val y = 3; (2 + y) + 4 } -``` - -The body of the first clause, `case Num(n) => Expr(n)`, looks suspicious. `n` -is declared as an `Int`, yet it is converted to an `Expr[Int]` with `Expr()`. -Shouldn’t `n` be quoted? In fact this would not -work since replacing `n` by `'n` in the clause would not be phase -correct. - -The `Expr.apply` method is defined in package `quoted`: - -```scala -package quoted - -object Expr: - ... - def apply[T: ToExpr](x: T)(using Quotes): Expr[T] = - summon[ToExpr[T]].toExpr(x) -``` +The evaluation of the macro will only happen when the code is inlined into `power2`. +When inlined, the code is equivalent to the previous definition of `power2`. +A consequence of using inline methods is that none of the arguments nor the return type of the macro will have to mention the `Expr` types; this hides all aspects of metaprogramming from the end-users. -This method says that values of types implementing the `ToExpr` type class can be -converted to `Expr` values using `Expr.apply`. +#### Avoiding a complete interpreter +When evaluating a top-level splice, the compiler needs to interpret the code that is within the splice. +Providing an interpreter for the entire language is quite tricky, and it is even more challenging to make that interpreter run efficiently. +To avoid needing a complete interpreter, we can impose the following restrictions on splices to simplify the evaluation of the code in top-level splices. + * The top-level splice must contain a single call to a compiled static method. + * Arguments to the function are literal constants, quoted expressions (parameters), calls to `Type.of` for type parameters and a reference to `Quotes`. -Scala 3 comes with given instances of `ToExpr` for -several types including `Boolean`, `String`, and all primitive number -types. For example, `Int` values can be converted to `Expr[Int]` -values by wrapping the value in a `Literal` tree node. This makes use -of the underlying tree representation in the compiler for -efficiency. But the `ToExpr` instances are nevertheless not _magic_ -in the sense that they could all be defined in a user program without -knowing anything about the representation of `Expr` trees. For -instance, here is a possible instance of `ToExpr[Boolean]`: +In particular, these restrictions disallow the use of splices in top-level splices. +Such a splice would require several stages of interpretation which would be unnecessarily inefficient. +#### Compilation stages +The macro implementation (i.e., the method called in the top-level splice) can come from any pre-compiled library. +This provides a clear difference between the stages of the compilation process. +Consider the following 3 source files defined in distinct libraries. ```scala -given ToExpr[Boolean] with - def toExpr(b: Boolean) = - if b then '{ true } else '{ false } +// Macro.scala +def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = ... +inline def powerMacro(x: Double, inline n: Int): Double = + ${ powerCode('x, 'n) } ``` -Once we can lift bits, we can work our way up. For instance, here is a -possible implementation of `ToExpr[Int]` that does not use the underlying -tree machinery: - ```scala -given ToExpr[Int] with - def toExpr(n: Int) = n match - case Int.MinValue => '{ Int.MinValue } - case _ if n < 0 => '{ - ${ toExpr(-n) } } - case 0 => '{ 0 } - case _ if n % 2 == 0 => '{ ${ toExpr(n / 2) } * 2 } - case _ => '{ ${ toExpr(n / 2) } * 2 + 1 } +// Lib.scala (depends on Macro.scala) +def power2(x: Double) = + ${ powerCode('x, '{2}) } // inlined from a call to: powerMacro(x, 2) ``` -Since `ToExpr` is a type class, its instances can be conditional. For example, -a `List` is liftable if its element type is: - ```scala -given [T: ToExpr : Type]: ToExpr[List[T]] with - def toExpr(xs: List[T]) = xs match - case head :: tail => '{ ${ Expr(head) } :: ${ toExpr(tail) } } - case Nil => '{ Nil: List[T] } +// App.scala (depends on Lib.scala) +@main def app() = power2(3.14) ``` - -In the end, `ToExpr` resembles very much a serialization -framework. Like the latter it can be derived systematically for all -collections, case classes and enums. Note also that the synthesis -of _type-tag_ values of type `Type[T]` is essentially the type-level -analogue of lifting. - -Using lifting, we can now give the missing definition of `showExpr` in the introductory example: +One way to syntactically visualize this is to put the application in a quote that delays the compilation of the application. +Then the application dependencies can be placed in an outer quote that contains the quoted application, and we repeat this recursively for dependencies of dependencies. ```scala -def showExpr[T](expr: Expr[T])(using Quotes): Expr[String] = - val code: String = expr.show - Expr(code) +'{ // macro library (compilation stage 1) + def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = + ... + inline def powerMacro(x: Double, inline n: Int): Double = + ${ powerCode('x, 'n) } + '{ // library using macros (compilation stage 2) + def power2(x: Double) = + ${ powerCode('x, '{2}) } // inlined from a call to: powerMacro(x, 2) + '{ power2(3.14) /* app (compilation stage 3) */ } + } +} ``` -That is, the `showExpr` method converts its `Expr` argument to a string (`code`), and lifts -the result back to an `Expr[String]` using `Expr.apply`. - -## Lifting Types +To make the system more versatile, we allow calling macros in the project where it is defined, with some restrictions. +For example, to compile `Macro.scala` and `Lib.scala` together in the same library. +To this end, we do not follow the simpler syntactic model and rely on semantic information from the source files. +When compiling a source, if we detect a call to a macro that is not compiled yet, we delay the compilation of this source to the following compilation stage. +In the example, we would delay the compilation of `Lib.scala` because it contains a compile-time call to `powerCode`. +Compilation stages are repeated until all sources are compiled, or no progress can be made. +If no progress is made, there was a cyclic dependency between the definition and the use of the macro. +We also need to detect if at runtime the macro depends on sources that have not been compiled yet. +These are detected by executing the macro and checking for JVM linking errors to classes that have not been compiled yet. -The previous section has shown that the metaprogramming framework has -to be able to take a type `T` and convert it to a type tree of type -`Type[T]` that can be reified. This means that all free variables of -the type tree refer to types and values defined in the current stage. +### Run-Time Multi-Stage Programming -For a reference to a global class, this is easy: Just issue the fully -qualified name of the class. Members of reifiable types are handled by -just reifying the containing type together with the member name. But -what to do for references to type parameters or local type definitions -that are not defined in the current stage? Here, we cannot construct -the `Type[T]` tree directly, so we need to get it from a recursive -implicit search. For instance, to implement +See [Run-Time Multi-Stage Programming](./staging.md) -```scala -summon[Type[List[T]]] -``` - -where `T` is not defined in the current stage, we construct the type constructor -of `List` applied to the splice of the result of searching for a given instance for `Type[T]`: - -```scala -Type.of[ List[ summon[Type[T]].Underlying ] ] -``` +## Safety -This is exactly the algorithm that Scala 2 uses to search for type tags. -In fact Scala 2's type tag feature can be understood as a more ad-hoc version of -`quoted.Type`. As was the case for type tags, the implicit search for a `quoted.Type` -is handled by the compiler, using the algorithm sketched above. +Multi-stage programming is by design statically safe and cross-stage safe. -## Relationship with `inline` +### Static Safety -Seen by itself, principled metaprogramming looks more like a framework for -runtime metaprogramming than one for compile-time metaprogramming with macros. -But combined with Scala 3’s `inline` feature it can be turned into a compile-time -system. The idea is that macro elaboration can be understood as a combination of -a macro library and a quoted program. For instance, here’s the `assert` macro -again together with a program that calls `assert`. - -```scala -object Macros: +#### Hygiene +All identifier names are interpreted as symbolic references to the corresponding variable in the context of the quote. +Therefore, while evaluating the quote, it is not possible to accidentally rebind a reference to a new variable with the same textual name. - inline def assert(inline expr: Boolean): Unit = - ${ assertImpl('expr) } +#### Well-typed +If a quote is well typed, then the generated code is well typed. +This is a simple consequence of tracking the type of each expression. +An `Expr[T]` can only be created from a quote that contains an expression of type `T. +Conversely, an `Expr[T]` can only be spliced in a location that expects a type `T. +As mentioned before, `Expr` is covariant in its type parameter. +This means that an `Expr[T]` can contain an expression of a subtype of `T. +When spliced in a location that expects a type `T, these expressions also have a valid type. - def assertImpl(expr: Expr[Boolean])(using Quotes) = - val failMsg: Expr[String] = Expr("failed assertion: " + expr.show) - '{ if !($expr) then throw new AssertionError($failMsg) } +### Cross-Stage Safety -@main def program = - val x = 1 - Macros.assert(x != 0) -``` +#### Level consistency +We define the _staging level_ of some code as the number of quotes minus the number of splices surrounding said code. +Local variables must be defined and used in the same staging level. -Inlining the `assert` function would give the following program: +It is never possible to access a local variable from a lower staging level as it does not yet exist. ```scala -@main def program = - val x = 1 - ${ Macros.assertImpl('{ x != 0}) } +def badPower(x: Double, n: Int): Double = + ${ unrolledPowerCode('x, n) } // error: value of `n` not known yet ``` -The example is only phase correct because `Macros` is a global value and -as such not subject to phase consistency checking. Conceptually that’s -a bit unsatisfactory. If the PCP is so fundamental, it should be -applicable without the global value exception. But in the example as -given this does not hold since both `assert` and `program` call -`assertImpl` with a splice but no quote. -However, one could argue that the example is really missing -an important aspect: The macro library has to be compiled in a phase -prior to the program using it, but in the code above, macro -and program are defined together. A more accurate view of -macros would be to have the user program be in a phase after the macro -definitions, reflecting the fact that macros have to be defined and -compiled before they are used. Hence, conceptually the program part -should be treated by the compiler as if it was quoted: +In the context of macros and _cross-platform portability_, that is, +macros compiled on one machine but potentially executed on another, +we cannot support cross-stage persistence of local variables. +Therefore, local variables can only be accessed at precisely the same staging level in our system. ```scala -@main def program = '{ - val x = 1 - ${ Macros.assertImpl('{ x != 0 }) } -} +def badPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = + // error: `n` potentially not available in the next execution environment + '{ power($x, n) } ``` -If `program` is treated as a quoted expression, the call to -`Macro.assertImpl` becomes phase correct even if macro library and -program are conceptualized as local definitions. - -But what about the call from `assert` to `assertImpl`? Here, we need a -tweak of the typing rules. An inline function such as `assert` that -contains a splice operation outside an enclosing quote is called a -_macro_. Macros are supposed to be expanded in a subsequent phase, -i.e. in a quoted context. Therefore, they are also type checked as if -they were in a quoted context. For instance, the definition of -`assert` is typechecked as if it appeared inside quotes. This makes -the call from `assert` to `assertImpl` phase-correct, even if we -assume that both definitions are local. - -The `inline` modifier is used to declare a `val` that is -either a constant or is a parameter that will be a constant when instantiated. This -aspect is also important for macro expansion. -To get values out of expressions containing constants `Expr` provides the method -`value` (or `valueOrError`). This will convert the `Expr[T]` into a `Some[T]` (or `T`) when the -expression contains value. Otherwise it will return `None` (or emit an error). -To avoid having incidental val bindings generated by the inlining of the `def` -it is recommended to use an inline parameter. To illustrate this, consider an -implementation of the `power` function that makes use of a statically known exponent: +The rules are slightly different for global definitions, such as `unrolledPowerCode`. +It is possible to generate code that contains a reference to a _global_ definition such as in `'{ power(2, 4) }`. +This is a limited form of cross-stage persistence that does not impede cross-platform portability, where we refer to the already compiled code for `power`. +Each compilation step will lower the staging level by one while keeping global definitions. +In consequence, we can refer to compiled definitions in macros such as `unrolledPowerCode` in `${ unrolledPowerCode('x, 2) }`. -```scala -inline def power(x: Double, inline n: Int) = ${ powerCode('x, 'n) } - -private def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = - n.value match - case Some(m) => powerCode(x, m) - case None => '{ Math.pow($x, $n.toDouble) } - -private def powerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = - if n == 0 then '{ 1.0 } - else if n == 1 then x - else if n % 2 == 0 then '{ val y = $x * $x; ${ powerCode('y, n / 2) } } - else '{ $x * ${ powerCode(x, n - 1) } } -``` +We can sumarize level consistency in two rules: + * Local variables can be used only at the same staging level as their definition + * Global variables can be used at any staging level -## Scope Extrusion -Quotes and splices are duals as far as the PCP is concerned. But there is an -additional restriction that needs to be imposed on splices to guarantee -soundness: code in splices must be free of side effects. The restriction -prevents code like this: +#### Type consistency +As Scala uses type erasure, generic types will be erased at run-time and hence in any following stage. +To ensure any quoted expression that refers to a generic type `T` does not lose the information it needs, we require a given `Type[T]` in scope. +The `Type[T]` will carry over the non-erased representation of the type into the next phase. +Therefore any generic type used at a higher staging level than its definition will require its `Type`. +#### Scope extrusion +Within the contents of a splice, it is possible to have a quote that refers to a local variable defined in the outer quote. +If this quote is used within the splice, the variable will be in scope. +However, if the quote is somehow _extruded_ outside the splice, then variables might not be in scope anymore. +Quoted expressions can be extruded using side effects such as mutable state and exceptions. +The following example shows how a quote can be extruded using mutable state. ```scala -var x: Expr[T] = ... +var x: Expr[T] = null '{ (y: T) => ${ x = 'y; 1 } } +x // has value '{y} but y is not in scope ``` -This code, if it was accepted, would _extrude_ a reference to a quoted variable -`y` from its scope. This would subsequently allow access to a variable outside the -scope where it is defined, which is likely problematic. The code is clearly -phase consistent, so we cannot use PCP to rule it out. Instead, we postulate a -future effect system that can guarantee that splices are pure. In the absence of -such a system we simply demand that spliced expressions are pure by convention, -and allow for undefined compiler behavior if they are not. This is analogous to -the status of pattern guards in Scala, which are also required, but not -verified, to be pure. - -[Multi-Stage Programming](./staging.md) introduces one additional method where -you can expand code at runtime with a method `run`. There is also a problem with -that invocation of `run` in splices. Consider the following expression: +A second way a variable can be extruded is through the `run` method. +If `run` consumes a quoted variable reference, it will not be in scope anymore. +The result will reference a variable that is defined in the next stage. ```scala -'{ (x: Int) => ${ run('x); 1 } } +'{ (x: Int) => ${ run('x); ... } } +// evaluates to: '{ (x: Int) => ${ x; ... } 1 ``` -This is again phase correct, but will lead us into trouble. Indeed, evaluating -the splice will reduce the expression `run('x)` to `x`. But then the result +To catch both scope extrusion scenarios, our system restricts the use of quotes by only allowing a quote to be spliced if it was not extruded from a splice scope. +Unlike level consistency, this is checked at run-time[^4] rather than compile-time to avoid making the static type system too complicated. -```scala -'{ (x: Int) => ${ x; 1 } } -``` +Each `Quotes` instance contains a unique scope identifier and refers to its parent scope, forming a stack of identifiers. +The parent of the scope of a `Quotes` is the scope of the `Quotes` used to create the enclosing quote. +Top-level splices and `run` create new scope stacks. +Every `Expr` knows in which scope it was created. +When it is spliced, we check that the quote scope is either the same as the splice scope, or a parent scope thereof. -is no longer phase correct. To prevent this soundness hole it seems easiest to -classify `run` as a side-effecting operation. It would thus be prevented from -appearing in splices. In a base language with side effects we would have to do this -anyway: Since `run` runs arbitrary code it can always produce a side effect if -the code it runs produces one. -## Example Expansion +## Staged Lambdas -Assume we have two methods, `foreach` that takes an `Expr[Array[T]]` and a -consumer `f`, and `sum` that performs a sum by delegating to `foreach`. +When staging programs in a functional language there are two fundamental abstractions: a staged lambda `Expr[T => U]` and a staging lambda `Expr[T] => Expr[U]`. +The first is a function that will exist in the next stage, whereas the second is a function that exists in the current stage. +It is often convenient to have a mechanism to go from `Expr[T => U]` to `Expr[T] => Expr[U]` and vice versa. ```scala -object Macros: - - def foreach[T](arr: Expr[Array[T]], f: Expr[T] => Expr[Unit]) - (using Type[T], Quotes): Expr[Unit] = '{ - var i: Int = 0 - while i < ($arr).length do - val element: T = ($arr)(i) - ${f('element)} - i += 1 - } - - def sum(arr: Expr[Array[Int]])(using Quotes): Expr[Int] = '{ - var sum = 0 - ${ foreach(arr, x => '{sum += $x}) } - sum - } - - inline def sum_m(arr: Array[Int]): Int = ${sum('arr)} +def later[T: Type, U: Type](f: Expr[T] => Expr[U]): Expr[T => U] = + '{ (x: T) => ${ f('x) } } -end Macros +def now[T: Type, U: Type](f: Expr[T => U]): Expr[T] => Expr[U] = + (x: Expr[T]) => '{ $f($x) } ``` -A call to `sum_m(Array(1, 2, 3))` will first inline `sum_m`: +Both conversions can be performed out of the box with quotes and splices. +But if `f` is a known lambda function, `'{ $f($x) }` will not beta-reduce the lambda in place. +This optimization is performed in a later phase of the compiler. +Not reducing the application immediately can simplify analysis of generated code. +Nevertheless, it is possible to beta-reduce the lambda in place using the `Expr.betaReduce` method. ```scala -val arr: Array[Int] = Array.apply(1, 2, 3) -${ _root_.Macros.sum('arr) } +def now[T: Type, U: Type](f: Expr[T => U]): Expr[T] => Expr[U] = + (x: Expr[T]) => Expr.betaReduce('{ $f($x) }) ``` -then it will call `sum`: +The `betaReduce` method will beta-reduce the outermost application of the expression if possible (regardless of arity). +If it is not possible to beta-reduce the expression, then it will return the original expression. +## Staged Constructors +To create new class instances in a later stage, we can create them using factory methods (usually `apply` methods of an `object`), or we can instantiate them with a `new`. +For example, we can write `Some(1)` or `new Some(1)`, creating the same value. +In Scala 3, using the factory method call notation will fall back to a `new` if no `apply` method is found. +We follow the usual staging rules when calling a factory method. +Similarly, when we use a `new C`, the constructor of `C` is implicitly called, which also follows the usual staging rules. +Therefore for an arbitrary known class `C`, we can use both `'{ C(...) }` or `'{ new C(...) }` as constructors. + +## Staged Classes +Quoted code can contain any valid expression including local class definitions. +This allows the creation of new classes with specialized implementations. +For example, we can implement a new version of `Runnable` that will perform some optimized operation. ```scala -val arr: Array[Int] = Array.apply(1, 2, 3) -${ '{ - var sum = 0 - ${ foreach('arr, x => '{sum += $x}) } - sum -} } +def mkRunnable(x: Int)(using Quotes): Expr[Runnable] = '{ + class MyRunnable extends Runnable: + def run(): Unit = ... // generate some custom code that uses `x` + new MyRunnable +} ``` -and cancel the `${'{...}}`: - -```scala -val arr: Array[Int] = Array.apply(1, 2, 3) +The quoted class is a local class and its type cannot escape the enclosing quote. +The class must be used inside the quote or an instance of it can be returned using a known interface (`Runnable` in this case). -var sum = 0 -${ foreach('arr, x => '{sum += $x}) } -sum -``` +## Quote Pattern Matching -then it will extract `x => '{sum += $x}` into `f`, to have a value: +It is sometimes necessary to analyze the structure of the code or decompose the code into its sub-expressions. +A classic example is an embedded DSL, where a macro knows a set of definitions that it can reinterpret while compiling the code (for instance, to perform optimizations). +In the following example, we extend our previous implementation of `powCode` to look into `x` to perform further optimizations. ```scala -val arr: Array[Int] = Array.apply(1, 2, 3) - -var sum = 0 -val f = x => '{sum += $x} -${ _root_.Macros.foreach('arr, 'f)(Type.of[Int]) } -sum +def fusedPowCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = + x match + case '{ power($y, $m) } => // we have (y^m)^n + fusedPowCode(y, '{ $n * $m }) // generate code for y^(n*m) + case _ => + '{ power($x, $n) } ``` -and then call `foreach`: - -```scala -val arr: Array[Int] = Array.apply(1, 2, 3) -var sum = 0 -val f = x => '{sum += $x} -${ '{ - var i: Int = 0 - while i < arr.length do - val element: Int = (arr)(i) - sum += element - i += 1 - sum -} } -``` +#### Sub-patterns -and cancel the `${'{...}}` again: +In quoted patterns, the `$` binds the sub-expression to an expression `Expr` that can be used in that `case` branch. +The contents of `${..}` in a quote pattern are regular Scala patterns. +For example, we can use the `Expr(_)` pattern within the `${..}` to only match if it is a known value and extract it. ```scala -val arr: Array[Int] = Array.apply(1, 2, 3) - -var sum = 0 -val f = x => '{sum += $x} -var i: Int = 0 -while i < arr.length do - val element: Int = (arr)(i) - sum += element - i += 1 -sum +def fusedUnrolledPowCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = + x match + case '{ power($y, ${Expr(m)}) } => // we have (y^m)^n + fusedUnrolledPowCode(y, n * m) // generate code for y * ... * y + case _ => // ( n*m times ) + unrolledPowerCode(x, n) ``` -Finally cleanups and dead code elimination: +These value extraction sub-patterns can be polymorphic using an instance of `FromExpr`. +In the following example, we show the implementation of `OptionFromExpr` which internally uses the `FromExpr[T]` to extract the value using the `Expr(x)` pattern. ```scala -val arr: Array[Int] = Array.apply(1, 2, 3) -var sum = 0 -var i: Int = 0 -while i < arr.length do - val element: Int = arr(i) - sum += element - i += 1 -sum +given OptionFromExpr[T](using Type[T], FromExpr[T]): FromExpr[Option[T]] with + def unapply(x: Expr[Option[T]])(using Quotes): Option[Option[T]] = + x match + case '{ Some( ${Expr(x)} ) } => Some(Some(x)) + case '{ None } => Some(None) + case _ => None ``` -## Find implicits within a macro -Similarly to the `summonFrom` construct, it is possible to make implicit search available -in a quote context. For this we simply provide `scala.quoted.Expr.summon`: -```scala -import scala.collection.immutable.{ TreeSet, HashSet } -inline def setFor[T]: Set[T] = ${ setForExpr[T] } +#### Closed patterns +Patterns may contain two kinds of references: global references such as the call to the `power` method in `'{ power(...) }`, or references to bindings defined in the pattern such as `x` in `case '{ (x: Int) => x }`. +When extracting an expression from a quote, we need to ensure that we do not extrude any variable from the scope where it is defined. -def setForExpr[T: Type](using Quotes): Expr[Set[T]] = - Expr.summon[Ordering[T]] match - case Some(ord) => '{ new TreeSet[T]()($ord) } - case _ => '{ new HashSet[T] } +```scala +'{ (x: Int) => x + 1 } match + case '{ (y: Int) => $z } => + // should not match, otherwise: z = '{ x + 1 } ``` -## Relationship with Transparent Inline +In this example, we see that the pattern should not match. +Otherwise, any use of the expression `z` would contain an unbound reference to `x`. +To avoid any such extrusion, we only match on a `${..}` if its expression is closed under the definitions within the pattern. +Therefore, the pattern will not match if the expression is not closed. -[Inline](./inline.md) documents inlining. The code below introduces a transparent -inline method that can calculate either a value of type `Int` or a value of type -`String`. +#### HOAS patterns +To allow extracting expressions that may contain extruded references we offer a _higher-order abstract syntax_ (HOAS) pattern `$f(y)` (or `$f(y1,...,yn)`). +This pattern will eta-expand the sub-expression with respect to `y` and bind it to `f`. +The lambda arguments will replace the variables that might have been extruded. ```scala -transparent inline def defaultOf(inline str: String) = - ${ defaultOfImpl('str) } - -def defaultOfImpl(strExpr: Expr[String])(using Quotes): Expr[Any] = - strExpr.valueOrError match - case "int" => '{1} - case "string" => '{"a"} - -// in a separate file -val a: Int = defaultOf("int") -val b: String = defaultOf("string") - +'{ ((x: Int) => x + 1).apply(2) } match + case '{ ((y: Int) => $f(y)).apply($z: Int) } => + // f may contain references to `x` (replaced by `$y`) + // f = (y: Expr[Int]) => '{ $y + 1 } + f(z) // generates '{ 2 + 1 } ``` -## Defining a macro and using it in a single project - -It is possible to define macros and use them in the same project as long as the implementation -of the macros does not have run-time dependencies on code in the file where it is used. -It might still have compile-time dependencies on types and quoted code that refers to the use-site file. - -To provide this functionality Scala 3 provides a transparent compilation mode where files that -try to expand a macro but fail because the macro has not been compiled yet are suspended. -If there are any suspended files when the compilation ends, the compiler will automatically restart -compilation of the suspended files using the output of the previous (partial) compilation as macro classpath. -In case all files are suspended due to cyclic dependencies the compilation will fail with an error. - -## Pattern matching on quoted expressions -It is possible to deconstruct or extract values out of `Expr` using pattern matching. +A HOAS pattern `$x(y1,...,yn)` will only match the expression if it does not contain references to variables defined in the pattern that are not in the set `y1,...,yn`. +In other words, the pattern will match if the expression only contains references to variables defined in the pattern that are in `y1,...,yn`. +Note that the HOAS patterns `$x()` are semantically equivalent to closed patterns `$x`. -`scala.quoted` contains objects that can help extracting values from `Expr`. -- `scala.quoted.Expr`/`scala.quoted.Exprs`: matches an expression of a value (resp. list of values) and returns the value (resp. list of values). -- `scala.quoted.Const`/`scala.quoted.Consts`: Same as `Expr`/`Exprs` but only works on primitive values. -- `scala.quoted.Varargs`: matches an explicit sequence of expressions and returns them. These sequences are useful to get individual `Expr[T]` out of a varargs expression of type `Expr[Seq[T]]`. +#### Type variables -These could be used in the following way to optimize any call to `sum` that has statically known values. +Expressions may contain types that are not statically known. +For example, an `Expr[List[Int]]` may contain `list.map(_.toInt)` where `list` is a `List` of some type. +To cover all the possible cases we would need to explicitly match `list` on all possible types (`List[Int]`, `List[Int => Int]`, ...). +This is an infinite set of types and therefore pattern cases. +Even if we would know all possible types that a specific program could use, we may still end up with an unmanageable number of cases. +To overcome this, we introduce type variables in quoted patterns, which will match any type. +In the following example, we show how type variables `t` and `u` match all possible pairs of consecutive calls to `map` on lists. +In the quoted patterns, types named with lower cases are identified as type variables. +This follows the same notation as type variables used in normal patterns. ```scala -inline def sum(inline args: Int*): Int = ${ sumExpr('args) } -private def sumExpr(argsExpr: Expr[Seq[Int]])(using Quotes): Expr[Int] = - argsExpr match - case Varargs(args @ Exprs(argValues)) => - // args is of type Seq[Expr[Int]] - // argValues is of type Seq[Int] - Expr(argValues.sum) // precompute result of sum - case Varargs(argExprs) => // argExprs is of type Seq[Expr[Int]] - val staticSum: Int = argExprs.map(_.value.getOrElse(0)).sum - val dynamicSum: Seq[Expr[Int]] = argExprs.filter(_.value.isEmpty) - dynamicSum.foldLeft(Expr(staticSum))((acc, arg) => '{ $acc + $arg }) - case _ => - '{ $argsExpr.sum } - -sum(1, 2, 3) // gets matched by Varargs +def fuseMapCode(x: Expr[List[Int]]): Expr[List[Int]] = + x match + case '{ ($ls: List[t]).map[u]($f).map[Int]($g) } => + '{ $ls.map($g.compose($f)) } + ... -val xs = List(1, 2, 3) -sum(xs*) // doesn't get matched by Varargs +fuseMapCode('{ List(1.2).map(f).map(g) }) // '{ List(1.2).map(g.compose(f)) } +fuseMapCode('{ List('a').map(h).map(i) }) // '{ List('a').map(i.compose(h)) } ``` +Variables `f` and `g` are inferred to be of type `Expr[t => u]` and `Expr[u => Int]` respectively. +Subsequently, we can infer `$g.compose($f)` to be of type `Expr[t => Int]` which is the type of the argument of `$ls.map(..)`. -### Quoted patterns +Type variables are abstract types that will be erased; this implies that to reference them in the second quote we need a given `Type[t]` and `Type[u]`. +The quoted pattern will implicitly provide those given types. +At run-time, when the pattern matches, the type of `t` and `u` will be known, and the `Type[t]` and `Type[u]` will contain the precise types in the expression. -Quoted pattens allow deconstructing complex code that contains a precise structure, types or methods. -Patterns `'{ ... }` can be placed in any location where Scala expects a pattern. +As `Expr` is covariant, the statically known type of the expression might not be the actual type. +Type variables can also be used to recover the precise type of the expression. +``scala +def let(x: Expr[Any])(using Quotes): Expr[Any] = + x match + case '{ $x: t } => + '{ val y: t = $x; y } -For example +let('{1}) // will return a `Expr[Any]` that contains an `Expr[Int]]` +``` +While we can define the type variable in the middle of the pattern, their normal form is to define them as a `type` with a lower case name at the start of the pattern. +We use the Scala backquote `` `t` `` naming convention which interprets the string within the backquote as a literal name identifier. +This is typically used when we have names that contain special characters that are not allowed for normal Scala identifiers. +But we use it to explicitly state that this is a reference to that name and not the introduction of a new variable. ```scala -optimize { - sum(sum(1, a, 2), 3, b) -} // should be optimized to 6 + a + b + case '{ type t; $x: `t` } => ``` +This is a bit more verbose but has some expressivity advantages such as allowing to define bounds on the variables and be able to refer to them several times in any scope of the pattern. ```scala -def sum(args: Int*): Int = args.sum -inline def optimize(inline arg: Int): Int = ${ optimizeExpr('arg) } -private def optimizeExpr(body: Expr[Int])(using Quotes): Expr[Int] = - body match - // Match a call to sum without any arguments - case '{ sum() } => Expr(0) - // Match a call to sum with an argument $n of type Int. - // n will be the Expr[Int] representing the argument. - case '{ sum($n) } => n - // Match a call to sum and extracts all its args in an `Expr[Seq[Int]]` - case '{ sum(${Varargs(args)}: _*) } => sumExpr(args) - case body => body - -private def sumExpr(args1: Seq[Expr[Int]])(using Quotes): Expr[Int] = - def flatSumArgs(arg: Expr[Int]): Seq[Expr[Int]] = arg match - case '{ sum(${Varargs(subArgs)}: _*) } => subArgs.flatMap(flatSumArgs) - case arg => Seq(arg) - val args2 = args1.flatMap(flatSumArgs) - val staticSum: Int = args2.map(_.value.getOrElse(0)).sum - val dynamicSum: Seq[Expr[Int]] = args2.filter(_.value.isEmpty) - dynamicSum.foldLeft(Expr(staticSum))((acc, arg) => '{ $acc + $arg }) + case '{ type t >: List[Int] <: Seq[Int]; $x: `t` } => + case '{ type t; $x: (`t`, `t`) } => ``` -### Recovering precise types using patterns -Sometimes it is necessary to get a more precise type for an expression. This can be achieved using the following pattern match. +#### Type patterns +It is possible to only have a type and no expression of that type. +To be able to inspect a type, we introduce quoted type pattern `case '[..] =>`. +It works the same way as a quoted pattern but is restricted to contain a type. +Type variables can be used in quoted type patterns to extract a type. ```scala -def f(expr: Expr[Any])(using Quotes) = expr match - case '{ $x: t } => - // If the pattern match succeeds, then there is - // some type `t` such that - // - `x` is bound to a variable of type `Expr[t]` - // - `t` is bound to a new type `t` and a given - // instance `Type[t]` is provided for it - // That is, we have `x: Expr[t]` and `given Type[t]`, - // for some (unknown) type `t`. +def empty[T: Type]: Expr[T] = + Type.of[T] match + case '[String] => '{ "" } + case '[List[t]] => '{ List.empty[t] } + ... ``` -This might be used to then perform an implicit search as in: - -```scala -extension (inline sc: StringContext) - inline def showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } - -private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using Quotes): Expr[String] = - import quotes.reflect.report - argsExpr match - case Varargs(argExprs) => - val argShowedExprs = argExprs.map { - case '{ $arg: tp } => - Expr.summon[Show[tp]] match - case Some(showExpr) => - '{ $showExpr.show($arg) } - case None => - report.error(s"could not find implicit for ${Type.show[Show[tp]]}", arg); '{???} - } - val newArgsExpr = Varargs(argShowedExprs) - '{ $sc.s($newArgsExpr: _*) } - case _ => - // `new StringContext(...).showMeExpr(args: _*)` not an explicit `showMeExpr"..."` - report.error(s"Args must be explicit", argsExpr) - '{???} +`Type.of[T]` is used to summon the given instance of `Type[T]` in scope, it is equivalent to `summon[Type[T]]`. -trait Show[-T]: - def show(x: T): String +#### Type testing and casting +It is important to note that instance checks and casts on `Expr`, such as `isInstanceOf[Expr[T]]` and `asInstanceOf[Expr[T]]`, will only check if the instance is of the class `Expr` but will not be able to check the `T` argument. +These cases will issue a warning at compile-time, but if they are ignored, they can result in unexpected behavior. -// in a different file -given Show[Boolean] with - def show(b: Boolean) = "boolean!" +These operations can be supported correctly in the system. +For a simple type test it is possible to use the `isExprOf[T]` method of `Expr` to check if it is an instance of that type. +Similarly, it is possible to use `asExprOf[T]` to cast an expression to a given type. +These operations use a given `Type[T]` to work around type erasure. -println(showMe"${true}") -``` -### Open code patterns +## Sub-Expression Transformation -Quoted pattern matching also provides higher-order patterns to match open terms. If a quoted term contains a definition, -then the rest of the quote can refer to this definition. +The system provides a mechanism to transform all sub-expressions of an expression. +This is useful when the sub-expressions we want to transform are deep in the expression. +It is also necessary if the expression contains sub-expressions that cannot be matched using quoted patterns (such as local class definitions). ```scala -'{ - val x: Int = 4 - x * x -} +trait ExprMap: + def transform[T](e: Expr[T])(using Type[T])(using Quotes): Expr[T] + def transformChildren[T](e: Expr[T])(using Type[T])(using Quotes): Expr[T] = + ... ``` -To match such a term we need to match the definition and the rest of the code, but we need to explicitly state that the rest of the code may refer to this definition. +Users can extend the `ExprMap` trait and implement the `transform` method. +This interface is flexible and can implement top-down, bottom-up, or other transformations. ```scala -case '{ val y: Int = $x; $body(y): Int } => +object OptimizeIdentity extends ExprMap: + def transform[T](e: Expr[T])(using Type[T])(using Quotes): Expr[T] = + transformChildren(e) match // bottom-up transformation + case '{ identity($x) } => x + case _ => e ``` -Here `$x` will match any closed expression while `$body(y)` will match an expression that is closed under `y`. Then -the subexpression of type `Expr[Int]` is bound to `body` as an `Expr[Int => Int]`. The extra argument represents the references to `y`. Usually this expression is used in combination with `Expr.betaReduce` to replace the extra argument. +The `transformChildren` method is implemented as a primitive that knows how to reach all the direct sub-expressions and calls `transform` on each one. +The type passed to `transform` is the expected type of this sub-expression in its expression. +For example while transforming `Some(1)` in `'{ val x: Option[Int] = Some(1); ...}` the type will be `Option[Int]` and not `Some[Int]`. +This implies that we can safely transform `Some(1)` into `None`. + +## Staged Implicit Summoning +When summoning implicit arguments using `summon`, we will find the given instances in the current scope. +It is possible to use `summon` to get staged implicit arguments by explicitly staging them first. +In the following example, we can pass an implicit `Ordering[T]` in a macro as an `Expr[Ordering[T]]` to its implementation. +Then we can splice it and give it implicitly in the next stage. ```scala -inline def eval(inline e: Int): Int = ${ evalExpr('e) } +inline def treeSetFor[T](using ord: Ordering[T]): Set[T] = + ${ setExpr[T](using 'ord) } -private def evalExpr(e: Expr[Int])(using Quotes): Expr[Int] = e match - case '{ val y: Int = $x; $body(y): Int } => - // body: Expr[Int => Int] where the argument represents - // references to y - evalExpr(Expr.betaReduce('{$body(${evalExpr(x)})})) - case '{ ($x: Int) * ($y: Int) } => - (x.value, y.value) match - case (Some(a), Some(b)) => Expr(a * b) - case _ => e - case _ => e +def setExpr[T:Type](using ord: Expr[Ordering[T]])(using Quotes): Expr[Set[T]] = + '{ given Ordering[T] = $ord; new TreeSet[T]() } ``` +We pass it as an implicit `Expr[Ordering[T]]` because there might be intermediate methods that can pass it along implicitly. + +An alternative is to summon implicit values in the scope where the macro is invoked. +Using the `Expr.summon` method we get an optional expression containing the implicit instance. +This provides the ability to search for implicit instances conditionally. + ```scala -eval { // expands to the code: (16: Int) - val x: Int = 4 - x * x -} +def summon[T: Type](using Quotes): Option[Expr[T]] ``` -We can also close over several bindings using `$b(a1, a2, ..., an)`. -To match an actual application we can use braces on the function part `${b}(a1, a2, ..., an)`. +```scala +inline def setFor[T]: Set[T] = + ${ setForExpr[T] } + +def setForExpr[T: Type]()(using Quotes): Expr[Set[T]] = + Expr.summon[Ordering[T]] match + case Some(ord) => + '{ new TreeSet[T]()($ord) } + case _ => + '{ new HashSet[T] } +``` ## More details -[More details](./macros-spec.md) +* [Specification](./macros-spec.md) +* Scalable Metaprogramming in Scala 3[^1] + + +[^1]: [Scalable Metaprogramming in Scala 3](https://infoscience.epfl.ch/record/299370) +[^2]: [Semantics-preserving inlining for metaprogramming](https://dl.acm.org/doi/10.1145/3426426.3428486) +[^3]: Implemented in the Scala 3 Dotty project https://github.com/lampepfl/dotty. sbt library dependency `"org.scala-lang" %% "scala3-staging" % scalaVersion.value` +[^4]: Using the `-Xcheck-macros` compiler flag diff --git a/docs/_docs/reference/metaprogramming/staging.md b/docs/_docs/reference/metaprogramming/staging.md index e74d491402b5..1c154e09f50e 100644 --- a/docs/_docs/reference/metaprogramming/staging.md +++ b/docs/_docs/reference/metaprogramming/staging.md @@ -1,6 +1,6 @@ --- layout: doc-page -title: "Runtime Multi-Stage Programming" +title: "Run-Time Multi-Stage Programming" nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/staging.html --- @@ -60,7 +60,7 @@ impose the following restrictions on the use of splices. The framework as discussed so far allows code to be staged, i.e. be prepared to be executed at a later stage. To run that code, there is another method in class `Expr` called `run`. Note that `$` and `run` both map from `Expr[T]` -to `T` but only `$` is subject to the [PCP](./macros.md#the-phase-consistency-principle), whereas `run` is just a normal method. +to `T` but only `$` is subject to [Cross-Stage Safety](./macros.md#cross-stage-safety), whereas `run` is just a normal method. `scala.quoted.staging.run` provides a `Quotes` that can be used to show the expression in its scope. On the other hand `scala.quoted.staging.withQuotes` provides a `Quotes` without evaluating the expression. diff --git a/docs/_spec/TODOreference/metaprogramming/macros-spec.md b/docs/_spec/TODOreference/metaprogramming/macros-spec.md index aa8f94a9a1f7..6045354fdbbc 100644 --- a/docs/_spec/TODOreference/metaprogramming/macros-spec.md +++ b/docs/_spec/TODOreference/metaprogramming/macros-spec.md @@ -4,251 +4,711 @@ title: "Macros Spec" nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/macros-spec.html --- +## Formalization + +* Multi-stage programming with generative and analytical macros[^2] +* Multi-Stage Macro Calculus, Chapter 4 of Scalable Metaprogramming in Scala 3[^1]. + Contains and extends the calculus of _Multi-stage programming with generative and analytical macros_ with type polymorphism. + +## Syntax + +The quotation syntax using `'` and `$` was chosen to mimic the string interpolation syntax of Scala. +Like a string double-quotation, a single-quote block can contain splices. +However, unlike strings, splices can contain quotes using the same rules. + +```scala +s" Hello $name" s" Hello ${name}" +'{ hello($name) } '{ hello(${name}) } +${ hello('name) } ${ hello('{name}) } +``` + +### Quotes +Quotes come in four flavors: quoted identifiers, quoted blocks, quoted block patterns and quoted type patterns. +Scala 2 used quoted identifiers to represent `Symbol` literals. They were deprecated in Scala 3, allowing to use them for quotation. +```scala +SimpleExpr ::= ... + | `'` alphaid // quoted identifier + | `'` `{` Block `}` // quoted block +Pattern ::= ... + | `'` `{` Block `}` // quoted block pattern + | `'` `[` Type `]` // quoted type pattern +``` + +Quoted blocks and quoted block patterns contain an expression equivalent to a normal block of code. +When entering either of those we track the fact that we are in a quoted block (`inQuoteBlock`) which is used for spliced identifiers. +When entering a quoted block pattern we additionally track the fact that we are in a quoted pattern (`inQuotePattern`) which is used to distinguish spliced blocks and splice patterns. +Lastly, the quoted type pattern simply contains a type. + +### Splices +Splices come in three flavors: spliced identifiers, spliced blocks and splice patterns. +Scala specifies identifiers containing `$` as valid identifiers but reserves them for compiler and standard library use only. +Unfortunately, many libraries have used such identifiers in Scala~2. Therefore to mitigate the cost of migration, we still support them. +We work around this by only allowing spliced identifiers[^3] within quoted blocks or quoted block patterns (`inQuoteBlock`). +Splice blocks and splice patterns can contain an arbitrary block or pattern respectively. +They are distinguished based on their surrounding quote (`inQuotePattern`), a quote block will contain spliced blocks, and a quote block pattern will contain splice patterns. + +```scala +SimpleExpr ::= ... + | `$` alphaid if inQuoteBlock // spliced identifier + | `$` `{` Block `}` if !inQuotePattern // spliced block + | `$` `{` Pattern `}` if inQuotePattern // splice pattern +``` + +### Quoted Pattern Type Variables +Quoted pattern type variables in quoted patterns and quoted type patterns do not require additional syntax. +Any type definition or reference with a name composed of lower cases is assumed to be a pattern type variable definition while typing. +A backticked type name with lower cases is interpreted as a reference to the type with that name. + + ## Implementation -### Syntax - -Compared to the [Scala 3 reference grammar](../syntax.md) -there are the following syntax changes: -``` -SimpleExpr ::= ... - | ‘'’ ‘{’ Block ‘}’ - | ‘'’ ‘[’ Type ‘]’ - | ‘$’ ‘{’ Block ‘}’ -SimpleType ::= ... - | ‘$’ ‘{’ Block ‘}’ -``` -In addition, an identifier `$x` starting with a `$` that appears inside -a quoted expression or type is treated as a splice `${x}` and a quoted identifier -`'x` that appears inside a splice is treated as a quote `'{x}` - -### Implementation in `scalac` - -Quotes and splices are primitive forms in the generated abstract syntax trees. -Top-level splices are eliminated during macro expansion while typing. On the -other hand, top-level quotes are eliminated in an expansion phase `PickleQuotes` -phase (after typing and pickling). PCP checking occurs while preparing the RHS -of an inline method for top-level splices and in the `Staging` phase (after -typing and before pickling). - -Macro-expansion works outside-in. If the outermost scope is a splice, -the spliced AST will be evaluated in an interpreter. A call to a -previously compiled method can be implemented as a reflective call to -that method. With the restrictions on splices that are currently in -place that’s all that’s needed. We might allow more interpretation in -splices in the future, which would allow us to loosen the -restriction. Quotes in spliced, interpreted code are kept as they -are, after splices nested in the quotes are expanded. - -If the outermost scope is a quote, we need to generate code that -constructs the quoted tree at run-time. We implement this by -serializing the tree as a TASTy structure, which is stored -in a string literal. At runtime, an unpickler method is called to -deserialize the string into a tree. - -Splices inside quoted code insert the spliced tree as is, after -expanding any quotes in the spliced code recursively. +### Run-Time Representation -## Formalization +The standard library defines the `Quotes` interface which contains all the logic and the abstract classes `Expr` and `Type`. +The compiler implements the `Quotes` interface and provides the implementation of `Expr` and `Type`. -The phase consistency principle can be formalized in a calculus that -extends simply-typed lambda calculus with quotes and splices. +##### `class Expr` +Expressions of type `Expr[T]` are represented by the following abstract class: +```scala +abstract class Expr[+T] private[scala] +``` +The only implementation of `Expr` is in the compiler along with the implementation of `Quotes`. +It is a class that wraps a typed AST and a `Scope` object with no methods of its own. +The `Scope` object is used to track the current splice scope and detect scope extrusions. -### Syntax +##### `object Expr` +The companion object of `Expr` contains a few useful static methods; +the `apply`/`unapply` methods to use `ToExpr`/`FromExpr` with ease; +the `betaReduce` and `summon` methods. +It also contains methods to create expressions out of lists or sequences of expressions: `block`, `ofSeq`, `ofList`, `ofTupleFromSeq` and `ofTuple`. -The syntax of terms, values, and types is given as follows: +```scala +object Expr: + def apply[T](x: T)(using ToExpr[T])(using Quotes): Expr[T] = ... + def unapply[T](x: Expr[T])(using FromExpr[T])(using Quotes): Option[T] = ... + def betaReduce[T](e: Expr[T])(using Quotes): Expr[T] = ... + def summon[T: Type](using Quotes): Option[Expr[T]] = ... + def block[T](stats: List[Expr[Any]], e: Expr[T])(using Quotes): Expr[T] = ... + def ofSeq[T: Type](xs: Seq[Expr[T]])(using Quotes): Expr[Seq[T]] = ... + def ofList[T: Type](xs: Seq[Expr[T]])(using Quotes): Expr[List[T]] = ... + def ofTupleFromSeq(xs: Seq[Expr[Any]])(using Quotes): Expr[Tuple] = ... + def ofTuple[T <: Tuple: Tuple.IsMappedBy[Expr]: Type](tup: T)(using Quotes): + Expr[Tuple.InverseMap[T, Expr]] = ... ``` -Terms t ::= x variable - (x: T) => t lambda - t t application - 't quote - $t splice -Values v ::= (x: T) => t lambda - 'u quote +##### `class Type` +Types of type `Type[T]` are represented by the following abstract class: +```scala +abstract class Type[T <: AnyKind] private[scala]: + type Underlying = T +``` + +The only implementation of `Type` is in the compiler along with the implementation of `Quotes`. +It is a class that wraps the AST of a type and a `Scope` object with no methods of its own. +The upper bound of `T` is `AnyKind` which implies that `T` may be a higher-kinded type. +The `Underlying` alias is used to select the type from an instance of `Type`. +Users never need to use this alias as they can always use `T` directly. +`Underlying` is used for internal encoding while compiling the code (see _Type Healing_). -Simple terms u ::= x | (x: T) => u | u u | 't +##### `object Type` +The companion object of `Type` contains a few useful static methods. +The first and most important one is the `Type.of` given definition. +This instance of `Type[T]` is summoned by default when no other instance is available. +The `of` operation is an intrinsic operation that the compiler will transform into code that will generate the `Type[T]` at run-time. +Secondly, the `Type.show[T]` operation will show a string representation of the type, which is often useful when debugging. +Finally, the object defines `valueOfConstant` (and `valueOfTuple`) which can transform singleton types (or tuples of singleton types) into their value. -Types T ::= A base type - T -> T function type - expr T quoted + +```scala +object Type: + given of[T <: AnyKind](using Quotes): Type[T] = ... + def show[T <: AnyKind](using Type[T])(using Quotes): String = ... + def valueOfConstant[T](using Type[T])(using Quotes): Option[T] = ... + def valueOfTuple[T <: Tuple](using Type[T])(using Quotes): Option[T] = ... ``` -Typing rules are formulated using a stack of environments -`Es`. Individual environments `E` consist as usual of variable -bindings `x: T`. Environments can be combined using the two -combinators `'` and `$`. + +##### `Quotes` +The `Quotes` interface is where most of the primitive operations of the quotation system are defined. + +Quotes define all the `Expr[T]` methods as extension methods. +`Type[T]` does not have methods and therefore does not appear here. +These methods are available as long as `Quotes` is implicitly given in the current scope. + +The `Quotes` instance is also the entry point to the [reflection API](./refelction.md) through the `reflect` object. + +Finally, `Quotes` provides the internal logic used in quote un-pickling (`QuoteUnpickler`) in quote pattern matching (`QuoteMatching`). +These interfaces are added to the self-type of the trait to make sure they are implemented on this object but not visible to users of `Quotes`. + +Internally, the implementation of `Quotes` will also track its current splicing scope `Scope`. +This scope will be attached to any expression that is created using this `Quotes` instance. + +```scala +trait Quotes: + this: runtime.QuoteUnpickler & runtime.QuoteMatching => + + extension [T](self: Expr[T]) + def show: String + def matches(that: Expr[Any]): Boolean + def value(using FromExpr[T]): Option[T] + def valueOrAbort(using FromExpr[T]): T + end extension + + extension (self: Expr[Any]) + def isExprOf[X](using Type[X]): Boolean + def asExprOf[X](using Type[X]): Expr[X] + end extension + + // abstract object reflect ... ``` -Environment E ::= () empty - E, x: T -Env. stack Es ::= () empty - E simple - Es * Es combined -Separator * ::= ' - $ +##### `Scope` +The splice context is represented as a stack (immutable list) of `Scope` objects. +Each `Scope` contains the position of the splice (used for error reporting) and a reference to the enclosing splice scope `Scope`. +A scope is a sub-scope of another if the other is contained in its parents. +This check is performed when an expression is spliced into another using the `Scope` provided in the current scope in `Quotes` and the one in the `Expr` or `Type`. + +### Entry Points +The two entry points for multi-stage programming are macros and the `run` operation. + +#### Macros +Inline macro definitions will inline a top-level splice (a splice not nested in a quote). +This splice needs to be evaluated at compile-time. +In _Avoiding a complete interpreter_[^1], we stated the following restrictions: + + * The top-level splice must contain a single call to a compiled static method. + * Arguments to the function are either literal constants, quoted expressions (parameters), `Type.of` for type parameters and a reference to `Quotes`. + +These restrictions make the implementation of the interpreter quite simple. +Java Reflection is used to call the single function call in the top-level splice. +The execution of that function is entirely done on compiled bytecode. +These are Scala static methods and may not always become Java static methods, they might be inside module objects. +As modules are encoded as class instances, we need to interpret the prefix of the method to instantiate it before we can invoke the method. + +The code of the arguments has not been compiled and therefore needs to be interpreted by the compiler. +Interpreting literal constants is as simple as extracting the constant from the AST that represents literals. +When interpreting a quoted expression, the contents of the quote is kept as an AST which is wrapped inside the implementation of `Expr`. +Calls to `Type.of[T]` also wrap the AST of the type inside the implementation of `Type`. +Finally, the reference to `Quotes` is supposed to be the reference to the quotes provided by the splice. +This reference is interpreted as a new instance of `Quotes` that contains a fresh initial `Scope` with no parents. + +The result of calling the method via Java Reflection will return an `Expr` containing a new AST that was generated by the implementation of that macro. +The scope of this `Expr` is checked to make sure it did not extrude from some splice or `run` operation. +Then the AST is extracted from the `Expr` and it is inserted as replacement for the AST that contained the top-level splice. + + +#### Run-time Multi-Stage Programming + +To be able to compile the code, the `scala.quoted.staging` library defines the `Compiler` trait. +An instance of `staging.Compiler` is a wrapper over the normal Scala~3 compiler. +To be instantiated it requires an instance of the JVM _classloader_ of the application. + +```scala +import scala.quoted.staging.* +given Compiler = Compiler.make(getClass.getClassLoader) ``` -The two environment combinators are both associative with left and -right identity `()`. -### Operational semantics +The classloader is needed for the compiler to know which dependencies have been loaded and to load the generated code using the same classloader. + +```scala +def mkPower2()(using Quotes): Expr[Double => Double] = ... -We define a small step reduction relation `-->` with the following rules: +run(mkPower2()) ``` - ((x: T) => t) v --> [x := v]t +To run the previous example, the compiler will create code equivalent to the following class and compile it using a new `Scope` without parents. + +```scala +class RunInstance: + def exec(): Double => Double = ${ mkPower2() } +``` +Finally, `run` will interpret `(new RunInstance).exec()` to evaluate the contents of the quote. +To do this, the resulting `RunInstance` class is loaded in the JVM using Java Reflection, instantiated and then the `exec` method is invoked. + + +### Compilation + +Quotes and splices are primitive forms in the generated typed abstract syntax trees. +These need to be type-checked with some extra rules, e.g., staging levels need to be checked and the references to generic types need to be adapted. +Finally, quoted expressions that will be generated at run-time need to be encoded (serialized) and decoded (deserialized). + +#### Typing Quoted Expressions - ${'u} --> u +The typing process for quoted expressions and splices with `Expr` is relatively straightforward. +At its core, quotes are desugared into calls to `quote`, splices are desugared into calls to `splice`. +We track the quotation level when desugaring into these methods. - t1 --> t2 - ----------------- - e[t1] --> e[t2] + +```scala +def quote[T](x: T): Quotes ?=> Expr[T] + +def splice[T](x: Quotes ?=> Expr[T]): T ``` -The first rule is standard call-by-value beta-reduction. The second -rule says that splice and quotes cancel each other out. The third rule -is a context rule; it says that reduction is allowed in the hole `[ ]` -position of an evaluation context. Evaluation contexts `e` and -splice evaluation context `e_s` are defined syntactically as follows: + +It would be impossible to track the quotation levels if users wrote calls to these methods directly. +To know if it is a call to one of those methods we would need to type it first, but to type it we would need to know if it is one of these methods to update the quotation level. +Therefore these methods can only be used by the compiler. + +At run-time, the splice needs to have a reference to the `Quotes` that created its surrounding quote. +To simplify this for later phases, we track the current `Quotes` and encode a reference directly in the splice using `nestedSplice` instead of `splice`. + +```scala +def nestedSplice[T](q: Quotes)(x: q.Nested ?=> Expr[T]): T ``` -Eval context e ::= [ ] | e t | v e | 'e_s[${e}] -Splice context e_s ::= [ ] | (x: T) => e_s | e_s t | u e_s +With this addition, the original `splice` is only used for top-level splices. + +The levels are mostly used to identify top-level splices that need to be evaluated while typing. +We do not use the quotation level to influence the typing process. +Level checking is performed at a later phase. +This ensures that a source expression in a quote will have the same elaboration as a source expression outside the quote. + + + +#### Quote Pattern Matching + +Pattern matching is defined in the trait `QuoteMatching`, which is part of the self type of `Quotes`. +It is implemented by `Quotes` but not available to users of `Quotes`. +To access it, the compiler generates a cast from `Quotes` to `QuoteMatching` and then selects one of its two members: `ExprMatch` or `TypeMatch`. +`ExprMatch` defines an `unapply` extractor method that is used to encode quote patterns and `TypeMatch` defines an `unapply` method for quoted type patterns. + +```scala +trait Quotes: + self: runtime.QuoteMatching & ... => + ... + +trait QuoteMatching: + object ExprMatch: + def unapply[TypeBindings <: Tuple, Tup <: Tuple] + (scrutinee: Expr[Any]) + (using pattern: Expr[Any]): Option[Tup] = ... + object TypeMatch: + ... ``` -### Typing rules +These extractor methods are only meant to be used in code generated by the compiler. +The call to the extractor that is generated has an already elaborated form that cannot be written in source, namely explicit type parameters and explicit contextual parameters. + +This extractor returns a tuple type `Tup` which cannot be inferred from the types in the method signature. +This type will be computed when typing the quote pattern and will be explicitly added to the extractor call. +To refer to type variables in arbitrary places of `Tup`, we need to define them all before their use, hence we have `TypeBindings`, which will contain all pattern type variable definitions. +The extractor also receives a given parameter of type `Expr[Any]` that will contain an expression that represents the pattern. +The compiler will explicitly add this pattern expression. +We use a given parameter because these are the only parameters we are allowed to add to the extractor call in a pattern position. + +This extractor is a bit convoluted, but it encodes away all the quotation-specific features. +It compiles the pattern down into a representation that the pattern matcher compiler phase understands. -Typing judgments are of the form `Es |- t: T`. There are two -substructural rules which express the fact that quotes and splices -cancel each other out: +The quote patterns are encoded into two parts: a tuple pattern that is tasked with extracting the result of the match and a quoted expression representing the pattern. +For example, if the pattern has no `$` we will have an `EmptyTuple` as the pattern and `'{1}` to represent the pattern. + +```scala + case '{ 1 } => +// is elaborated to + case ExprMatch(EmptyTuple)(using '{1}) => +// ^^^^^^^^^^ ^^^^^^^^^^ +// pattern expression +``` +When extracting expressions, each pattern that is contained in a splice `${..}` will be placed in order in the tuple pattern. +In the following case, the `f` and `x` are placed in a tuple pattern `(f, x)`. +The type of the tuple is encoded in the `Tup` and not only in the tuple itself. +Otherwise, the extractor would return a tuple `Tuple` for which the types need to be tested which is in turn not possible due to type erasure. + +```scala + case '{ ((y: Int) => $f(y)).apply($x) } => +// is elaborated to + case ExprMatch[.., (Expr[Int => Int], Expr[Int])]((f, x))(using pattern) => +// pattern = '{ ((y: Int) => pat[Int](y)).apply(pat[Int]()) } ``` - Es1 * Es2 |- t: T - --------------------------- - Es1 $ E1 ' E2 * Es2 |- t: T +The contents of the quote are transformed into a valid quote expression by replacing the splice with a marker expression `pat[T](..)`. +The type `T` is taken from the type of the splice and the arguments are the HOAS arguments. +This implies that a `pat[T]()` is a closed pattern and `pat[T](y)` is an HOAS pattern that can refer to `y`. - Es1 * Es2 |- t: T - --------------------------- - Es1 ' E1 $ E2 * Es2 |- t: T +Type variables in quoted patterns are first normalized to have all definitions at the start of the pattern. +For each definition of a type variable `t` in the pattern we will add a type variable definition in `TypeBindings`. +Each one will have a corresponding `Type[t]` that will get extracted if the pattern matches. +These `Type[t]` are also listed in the `Tup` and added in the tuple pattern. +It is additionally marked as `using` in the pattern to make it implicitly available in this case branch. + + +```scala + case '{ type t; ($xs: List[t]).map[t](identity[t]) } => +// is elaborated to + case ExprMatch[(t), (Type[t], Expr[List[t]])]((using t, xs))(using p) => +// ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^ +// type bindings result type pattern expression +// p = '{ @patternType type u; pat[List[u]]().map[u](identity[u]) } ``` -The lambda calculus fragment of the rules is standard, except that we -use a stack of environments. The rules only interact with the topmost -environment of the stack. + +The contents of the quote are transformed into a valid quote expression by replacing type variables with fresh ones that do not escape the quote scope. +These are also annotated to be easily identifiable as pattern variables. + +#### Level Consistency Checking +Level consistency checking is performed after typing the program as a static check. +To check level consistency we traverse the tree top-down remembering the context staging level. +Each local definition in scope is recorded with its level and each term reference to a definition is checked against the current staging level. +```scala +// level 0 +'{ // level 1 + val x = ... // level 1 with (x -> 1) + ${ // level 0 (x -> 1) + val y = ... // level 0 with (x -> 1, y -> 0) + x // error: defined at level 1 but used in level 0 + } + // level 1 (x -> 1) + x // x is ok +} ``` - x: T in E - -------------- - Es * E |- x: T +#### Type Healing - Es * E, x: T1 |- t: T2 - ------------------------------- - Es * E |- (x: T1) => t: T -> T2 +When using a generic type `T` in a future stage, it is necessary to have a given `Type[T]` in scope. +The compiler needs to identify those references and link them with the instance of `Type[T]`. +For instance consider the following example: +```scala +def emptyList[T](using t: Type[T])(using Quotes): Expr[List[T]] = + '{ List.empty[T] } +``` - Es |- t1: T2 -> T Es |- t2: T2 - --------------------------------- - Es |- t1 t2: T +For each reference to a generic type `T` that is defined at level 0 and used at level 1 or greater, the compiler will summon a `Type[T]`. +This is usually the given type that is provided as parameter, `t` in this case. +We can use the type `t.Underlying` to replace `T` as it is an alias of that type. +But `t.Underlying` contains the extra information that it is `t` that will be used in the evaluation of the quote. +In a sense, `Underlying` acts like a splice for types. + +```scala +def emptyList[T](using t: Type[T])(using Quotes): Expr[List[T]] = + '{ List.empty[t.Underlying] } ``` -The rules for quotes and splices map between `expr T` and `T` by trading `'` and `$` between -environments and terms. + +Due to some technical limitations, it is not always possible to replace the type reference with the AST containing `t.Underlying`. +To overcome this limitation, we can simply define a list of type aliases at the start of the quote and insert the `t.Underlying` there. +This has the added advantage that we do not have to repeatedly insert the `t.Underlying` in the quote. + +```scala +def emptyList[T](using t: Type[T])(using Quotes): Expr[List[T]] = + '{ type U = t.Underlying; List.empty[U] } +``` +These aliases can be used at any level within the quote and this transformation is only performed on quotes that are at level 0. + +```scala + '{ List.empty[T] ... '{ List.empty[T] } ... } +// becomes + '{ type U = t.Underlying; List.empty[U] ... '{ List.empty[U] } ... } +``` +If we define a generic type at level 1 or greater, it will not be subject to this transformation. +In some future compilation stage, when the definition of the generic type is at level 0, it will be subject to this transformation. +This simplifies the transformation logic and avoids leaking the encoding into code that a macro could inspect. + +```scala +'{ + def emptyList[T: Type](using Quotes): Expr[List[T]] = '{ List.empty[T] } + ... +} +``` +A similar transformation is performed on `Type.of[T]`. +Any generic type in `T` needs to have an implicitly given `Type[T]` in scope, which will also be used as a path. +The example: + +```scala +def empty[T](using t: Type[T])(using Quotes): Expr[T] = + Type.of[T] match ... +// becomes +def empty[T](using t: Type[T])(using Quotes): Expr[T] = + Type.of[t.Underlying] match ... +// then becomes +def empty[T](using t: Type[T])(using Quotes): Expr[T] = + t match ... +``` + +The operation `Type.of[t.Underlying]` can be optimized to just `t`. +But this is not always the case. +If the generic reference is nested in the type, we will need to keep the `Type.of`. + +```scala +def matchOnList[T](using t: Type[T])(using Quotes): Expr[List[T]] = + Type.of[List[T]] match ... +// becomes +def matchOnList[T](using t: Type[T])(using Quotes): Expr[List[T]] = + Type.of[List[t.Underlying]] match ... +``` + +By doing this transformation, we ensure that each abstract type `U` used in `Type.of` has an implicit `Type[U]` in scope. +This representation makes it simpler to identify parts of the type that are statically known from those that are known dynamically. +Type aliases are also added within the type of the `Type.of` though these are not valid source code. +These would look like `Type.of[{type U = t.Underlying; Map[U, U]}]` if written in source code. + + +#### Splice Normalization + +The contents of a splice may refer to variables defined in the enclosing quote. +This complicates the process of serialization of the contents of the quotes. +To make serialization simple, we first transform the contents of each level 1 splice. +Consider the following example: + +```scala +def power5to(n: Expr[Int]): Expr[Double] = '{ + val x: Int = 5 + ${ powerCode('{x}, n) } +} +``` + +The variable `x` is defined in the quote and used in the splice. +The normal form will extract all references to `x` and replace them with a staged version of `x`. +We will replace the reference to `x` of type `T` with a `$y` where `y` is of type `Expr[T]`. +Then we wrap the new contents of the splice in a lambda that defines `y` and apply it to the quoted version of `x`. +After this transformation we have 2 parts, a lambda without references to the quote, which knows how to compute the contents of the splice, and a sequence of quoted arguments that refer to variables defined in the lambda. + +```scala +def power5to(n: Expr[Int]): Expr[Double] = '{ + val x: Int = 5 + ${ ((y: Expr[Int]) => powerCode('{$y}, n)).apply('x) } +} +``` + +In general, the splice normal form has the shape `${ .apply(*) }` and the following constraints: + * `` a lambda expression that does not refer to variables defined in the outer quote + * `` sequence of quoted expressions or `Type.of` containing references to variables defined in the enclosing quote and no references to local variables defined outside the enclosing quote + + +##### Function references normalization +A reference to a function `f` that receives parameters is not a valid value in Scala. +Such a function reference `f` can be eta-expaned as `x => f(x)` to be used as a lambda value. +Therefore function references cannot be transformed by the normalization as directly as other expressions as we cannot represent `'{f}` with a method reference type. +We can use the eta-expanded form of `f` in the normalized form. +For example, consider the reference to `f` below. + +```scala +'{ + def f(a: Int)(b: Int, c: Int): Int = 2 + a + b + c + ${ '{ f(3)(4, 5) } } +} +``` + +To normalize this code, we can eta-expand the reference to `f` and place it in a quote containing a proper expression. +Therefore the normalized form of the argument `'{f}` becomes the quoted lambda `'{ (a: Int) => (b: Int, c: Int) => f(a)(b, c) }` and is an expression of type `Expr[Int => (Int, Int) => Int]`. +The eta-expansion produces one curried lambda per parameter list. +The application `f(3)(4, 5)` does not become `$g(3)(4, 5)` but `$g.apply(3).apply(4, 5)`. +We add the `apply` because `g` is not a quoted reference to a function but a curried lambda. + +```scala +'{ + def f(a: Int)(b: Int, c: Int): Int = 2 + a + b + c + ${ + ( + (g: Expr[Int => (Int, Int) => Int]) => '{$g.apply(3).apply(4, 5)} + ).apply('{ (a: Int) => (b: Int, c: Int) => f(a)(b, c) }) + } +} +``` + +Then we can apply it and beta-reduce the application when generating the code. + +```scala + (g: Expr[Int => Int => Int]) => betaReduce('{$g.apply(3).apply(4)}) +``` + + +##### Variable assignment normalization +A reference to a mutable variable in the left-hand side of an assignment cannot be transformed directly as it is not in an expression position. +```scala +'{ + var x: Int = 5 + ${ g('{x = 2}) } +} +``` + +We can use the same strategy used for function references by eta-expanding the assignment operation `x = _` into `y => x = y`. + +```scala +'{ + var x: Int = 5 + ${ + g( + ( + (f: Expr[Int => Unit]) => betaReduce('{$f(2)}) + ).apply('{ (y: Int) => x = $y }) + ) + } +} +``` + + +##### Type normalization +Types defined in the quote are subject to a similar transformation. +In this example, `T` is defined within the quote at level 1 and used in the splice again at level 1. + +```scala +'{ def f[T] = ${ '{g[T]} } } +``` + +The normalization will add a `Type[T]` to the lambda, and we will insert this reference. +The difference is that it will add an alias similar to the one used in type healing. +In this example, we create a `type U` that aliases the staged type. + +```scala +'{ + def f[T] = ${ + ( + (t: Type[T]) => '{type U = t.Underling; g[U]} + ).apply(Type.of[T]) + } +} +``` + +#### Serialization + +Quoted code needs to be pickled to make it available at run-time in the next compilation phase. +We implement this by pickling the AST as a TASTy binary. + +##### TASTy +The TASTy format is the typed abstract syntax tree serialization format of Scala 3. +It usually pickles the fully elaborated code after type-checking and is kept along the generated Java classfiles. + + +##### Pickling +We use TASTy as a serialization format for the contents of the quotes. +To show how serialization is performed, we will use the following example. +```scala +'{ + val (x, n): (Double, Int) = (5, 2) + ${ powerCode('{x}, '{n}) } * ${ powerCode('{2}, '{n}) } +} ``` - Es $ () |- t: expr T - -------------------- - Es |- $t: T +This quote is transformed into the following code when normalizing the splices. - Es ' () |- t: T - ---------------- - Es |- 't: expr T +```scala +'{ + val (x, n): (Double, Int) = (5, 2) + ${ + ((y: Expr[Double], m: Expr[Int]) => powerCode(y, m)).apply('x, 'n) + } * ${ + ((m: Expr[Int]) => powerCode('{2}, m)).apply('n) + } +} ``` -The meta theory of a slightly simplified 2-stage variant of this calculus -is studied [separately](./simple-smp.md). -## Going Further +Splice normalization is a key part of the serialization process as it only allows references to variables defined in the quote in the arguments of the lambda in the splice. +This makes it possible to create a closed representation of the quote without much effort. +The first step is to remove all the splices and replace them with holes. +A hole is like a splice but it lacks the knowledge of how to compute the contents of the splice. +Instead, it knows the index of the hole and the contents of the arguments of the splice. +We can see this transformation in the following example where a hole is represented by `<< idx; holeType; args* >>`. + +```scala + ${ ((y: Expr[Double], m: Expr[Int]) => powerCode(y, m)).apply('x, 'n) } +// becomes + << 0; Double; x, n >> +``` -The metaprogramming framework as presented and currently implemented is quite restrictive -in that it does not allow for the inspection of quoted expressions and -types. It’s possible to work around this by providing all necessary -information as normal, unquoted inline parameters. But we would gain -more flexibility by allowing for the inspection of quoted code with -pattern matching. This opens new possibilities. +As this was the first hole it has index 0. +The hole type is `Double`, which needs to be remembered now that we cannot infer it from the contents of the splice. +The arguments of the splice are `x` and `n`; note that they do not require quoting because they were moved out of the splice. -For instance, here is a version of `power` that generates the multiplications -directly if the exponent is statically known and falls back to the dynamic -implementation of `power` otherwise. +References to healed types are handled in a similar way. +Consider the `emptyList` example, which shows the type aliases that are inserted into the quote. ```scala -import scala.quoted.* +'{ List.empty[T] } +// type healed to +'{ type U = t.Underlying; List.empty[U] } +``` +Instead of replacing a splice, we replace the `t.Underlying` type with a type hole. +The type hole is represented by `<< idx; bounds >>`. +```scala +'{ type U = << 0; Nothing..Any >>; List.empty[U] } +``` +Here, the bounds of `Nothing..Any` are the bounds of the original `T` type. +The types of a `Type.of` are transformed in the same way. -inline def power(x: Double, n: Int): Double = - ${ powerExpr('x, 'n) } -private def powerExpr(x: Expr[Double], n: Expr[Int]) - (using Quotes): Expr[Double] = - n.value match - case Some(m) => powerExpr(x, m) - case _ => '{ dynamicPower($x, $n) } +With these transformations, the contents of the quote or `Type.of` are guaranteed to be closed and therefore can be pickled. +The AST is pickled into TASTy, which is a sequence of bytes. +This sequence of bytes needs to be instantiated in the bytecode, but unfortunately it cannot be dumped into the classfile as bytes. +To reify it we encode the bytes into a Java `String`. +In the following examples we display this encoding in human readable form with the fictitious |tasty"..."| string literal. -private def powerExpr(x: Expr[Double], n: Int) - (using Quotes): Expr[Double] = - if n == 0 then '{ 1.0 } - else if n == 1 then x - else if n % 2 == 0 then '{ val y = $x * $x; ${ powerExpr('y, n / 2) } } - else '{ $x * ${ powerExpr(x, n - 1) } } +```scala +// pickled AST bytes encoded in a base64 string +tasty""" + val (x, n): (Double, Int) = (5, 2) + << 0; Double; x, n >> * << 1; Double; n >> +""" +// or +tasty""" + type U = << 0; Nothing..Any; >> + List.empty[U] +""" +``` +The contents of a quote or `Type.of` are not always pickled. +In some cases it is better to generate equivalent (smaller and/or faster) code that will compute the expression. +Literal values are compiled into a call to `Expr()` using the implementation of `ToExpr` to create the quoted expression. +This is currently performed only on literal values, but can be extended to any value for which we have a `ToExpr` defined in the standard library. +Similarly, for non-generic types we can use their respective `java.lang.Class` and convert them into a `Type` using a primitive operation `typeConstructorOf` defined in the reflection API. -private def dynamicPower(x: Double, n: Int): Double = - if n == 0 then 1.0 - else if n % 2 == 0 then dynamicPower(x * x, n / 2) - else x * dynamicPower(x, n - 1) +##### Unpickling + +Now that we have seen how a quote is pickled, we can look at how to unpickle it. +We will continue with the previous example. + +Holes were used to replace the splices in the quote. +When we perform this transformation we also need to remember the lambdas from the splices and their hole index. +When unpickling a hole, the corresponding splice lambda will be used to compute the contents of the hole. +The lambda will receive as parameters quoted versions of the arguments of the hole. +For example to compute the contents of `<< 0; Double; x, n >>` we will evaluate the following code + +```scala + ((y: Expr[Double], m: Expr[Int]) => powerCode(y, m)).apply('x, 'n) ``` -In the above, the method `.value` maps a constant expression of the type -`Expr[T]` to its value of the type `T`. +The evaluation is not as trivial as it looks, because the lambda comes from compiled code and the rest is code that must be interpreted. +We put the AST of `x` and `n` into `Expr` objects to simulate the quotes and then we use Java Reflection to call the `apply` method. + +We may have many holes in a quote and therefore as many lambdas. +To avoid the instantiation of many lambdas, we can join them together into a single lambda. +Apart from the list of arguments, this lambda will also take the index of the hole that is being evaluated. +It will perform a switch match on the index and call the corresponding lambda in each branch. +Each branch will also extract the arguments depending on the definition of the lambda. +The application of the original lambdas are beta-reduced to avoid extra overhead. -With the right extractors, the "AsFunction" conversion -that maps expressions over functions to functions over expressions can -be implemented in user code: ```scala -given AsFunction1[T, U]: Conversion[Expr[T => U], Expr[T] => Expr[U]] with - def apply(f: Expr[T => U]): Expr[T] => Expr[U] = - (x: Expr[T]) => f match - case Lambda(g) => g(x) - case _ => '{ ($f)($x) } +(idx: Int, args: Seq[Any]) => + idx match + case 0 => // for << 0; Double; x, n >> + val x = args(0).asInstanceOf[Expr[Double]] + val n = args(1).asInstanceOf[Expr[Int]] + powerCode(x, n) + case 1 => // for << 1; Double; n >> + val n = args(0).asInstanceOf[Expr[Int]] + powerCode('{2}, n) ``` -This assumes an extractor + +This is similar to what we do for splices when we replace the type aliased with holes we keep track of the index of the hole. +Instead of lambdas, we will have a list of references to instances of `Type`. +From the following example we would extract `t`, `u`, ... . + ```scala -object Lambda: - def unapply[T, U](x: Expr[T => U]): Option[Expr[T] => Expr[U]] + '{ type T1 = t1.Underlying; type Tn = tn.Underlying; ... } +// with holes + '{ type T1 = << 0; ... >>; type Tn = << n-1; ... >>; ... } ``` -Once we allow inspection of code via extractors, it’s tempting to also -add constructors that create typed trees directly without going -through quotes. Most likely, those constructors would work over `Expr` -types which lack a known type argument. For instance, an `Apply` -constructor could be typed as follows: + +As the type holes are at the start of the quote, they will have the first `N` indices. +This implies that we can place the references in a sequence `Seq(t, u, ...)` where the index in the sequence is the same as the hole index. + +Lastly, the quote itself is replaced by a call to `QuoteUnpickler.unpickleExpr` which will unpickle the AST, evaluate the holes, i.e., splices, and wrap the resulting AST in an `Expr[Int]`. +This method takes takes the pickled |tasty"..."|, the types and the hole lambda. +Similarly, `Type.of` is replaced with a call to `QuoteUnpickler.unpickleType` but only receives the pickled |tasty"..."| and the types. +Because `QuoteUnpickler` is part of the self-type of the `Quotes` class, we have to cast the instance but know that this cast will always succeed. + ```scala -def Apply(fn: Expr[Any], args: List[Expr[Any]]): Expr[Any] +quotes.asInstanceOf[runtime.QuoteUnpickler].unpickleExpr[T]( + pickled = tasty"...", + types = Seq(...), + holes = (idx: Int, args: Seq[Any]) => idx match ... +) ``` -This would allow constructing applications from lists of arguments -without having to match the arguments one-by-one with the -corresponding formal parameter types of the function. We then need "at -the end" a method to convert an `Expr[Any]` to an `Expr[T]` where `T` is -given from the outside. For instance, if `code` yields a `Expr[Any]`, then -`code.atType[T]` yields an `Expr[T]`. The `atType` method has to be -implemented as a primitive; it would check that the computed type -structure of `Expr` is a subtype of the type structure representing -`T`. -Before going down that route, we should evaluate in detail the tradeoffs it -presents. Constructing trees that are only verified _a posteriori_ -to be type correct loses a lot of guidance for constructing the right -trees. So we should wait with this addition until we have more -use-cases that help us decide whether the loss in type-safety is worth -the gain in flexibility. In this context, it seems that deconstructing types is -less error-prone than deconstructing terms, so one might also -envisage a solution that allows the former but not the latter. - -## Conclusion - -Metaprogramming has a reputation of being difficult and confusing. -But with explicit `Expr/Type` types and quotes and splices it can become -downright pleasant. A simple strategy first defines the underlying quoted or unquoted -values using `Expr` and `Type` and then inserts quotes and splices to make the types -line up. Phase consistency is at the same time a great guideline -where to insert a splice or a quote and a vital sanity check that -the result makes sense. +[^1]: [Scalable Metaprogramming in Scala 3](https://infoscience.epfl.ch/record/299370) +[^2]: [Multi-stage programming with generative and analytical macros](https://dl.acm.org/doi/10.1145/3486609.3487203). +[^3]: In quotes, identifiers starting with `$` must be surrounded by backticks (`` `$` ``). For example `$conforms` from `scala.Predef`. diff --git a/docs/_spec/TODOreference/metaprogramming/macros.md b/docs/_spec/TODOreference/metaprogramming/macros.md index 8045794d1143..e39f6f1022b8 100644 --- a/docs/_spec/TODOreference/metaprogramming/macros.md +++ b/docs/_spec/TODOreference/metaprogramming/macros.md @@ -6,818 +6,616 @@ nightlyOf: https://docs.scala-lang.org/scala3/reference/metaprogramming/macros.h > When developing macros enable `-Xcheck-macros` scalac option flag to have extra runtime checks. -## Macros: Quotes and Splices +## Multi-Staging -Macros are built on two well-known fundamental operations: quotation and splicing. -Quotation is expressed as `'{...}` for expressions and splicing is expressed as `${ ... }`. -Additionally, within a quote or a splice we can quote or splice identifiers directly (i.e. `'e` and `$e`). -Readers may notice the resemblance of the two aforementioned syntactic -schemes with the familiar string interpolation syntax. +#### Quoted expressions +Multi-stage programming in Scala 3 uses quotes `'{..}` to delay, i.e., stage, execution of code and splices `${..}` to evaluate and insert code into quotes. +Quoted expressions are typed as `Expr[T]` with a covariant type parameter `T`. +It is easy to write statically safe code generators with these two concepts. +The following example shows a naive implementation of the $x^n$ mathematical operation. ```scala -println(s"Hello, $name, here is the result of 1 + 1 = ${1 + 1}") +import scala.quoted.* +def unrolledPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = + if n == 0 then '{ 1.0 } + else if n == 1 then x + else '{ $x * ${ unrolledPowerCode(x, n-1) } } ``` -In string interpolation we _quoted_ a string and then we _spliced_ into it, two others. The first, `name`, is a reference to a value of type [`String`](https://scala-lang.org/api/3.x/scala/Predef$.html#String-0), and the second is an arithmetic expression that will be _evaluated_ followed by the splicing of its string representation. - -Quotes and splices in this section allow us to treat code in a similar way, -effectively supporting macros. The entry point for macros is an inline method -with a top-level splice. We call it a top-level because it is the only occasion -where we encounter a splice outside a quote (consider as a quote the -compilation-unit at the call-site). For example, the code below presents an -`inline` method `assert` which calls at compile-time a method `assertImpl` with -a boolean expression tree as argument. `assertImpl` evaluates the expression and -prints it again in an error message if it evaluates to `false`. - ```scala -import scala.quoted.* - -inline def assert(inline expr: Boolean): Unit = - ${ assertImpl('expr) } - -def assertImpl(expr: Expr[Boolean])(using Quotes) = '{ - if !$expr then - throw AssertionError(s"failed assertion: ${${ showExpr(expr) }}") +'{ + val x = ... + ${ unrolledPowerCode('{x}, 3) } // evaluates to: x * x * x } - -def showExpr(expr: Expr[Boolean])(using Quotes): Expr[String] = - '{ [actual implementation later in this document] } ``` -If `e` is an expression, then `'{e}` represents the typed -abstract syntax tree representing `e`. If `T` is a type, then `Type.of[T]` -represents the type structure representing `T`. The precise -definitions of "typed abstract syntax tree" or "type-structure" do not -matter for now, the terms are used only to give some -intuition. Conversely, `${e}` evaluates the expression `e`, which must -yield a typed abstract syntax tree or type structure, and embeds the -result as an expression (respectively, type) in the enclosing program. +Quotes and splices are duals of each other. +For an arbitrary expression `x` of type `T` we have `${'{x}} = x` and for an arbitrary expression `e` of type `Expr[T]` we have `'{${e}} = e`. -Quotations can have spliced parts in them; in this case the embedded -splices are evaluated and embedded as part of the formation of the -quotation. +#### Abstract types +Quotes can handle generic and abstract types using the type class `Type[T]`. +A quote that refers to a generic or abstract type `T` requires a given `Type[T]` to be provided in the implicit scope. +The following examples show how `T` is annotated with a context bound (`: Type`) to provide an implicit `Type[T]`, or the equivalent `using Type[T]` parameter. -Quotes and splices can also be applied directly to identifiers. An identifier -`$x` starting with a `$` that appears inside a quoted expression or type is treated as a -splice `${x}`. Analogously, an quoted identifier `'x` that appears inside a splice -is treated as a quote `'{x}`. See the Syntax section below for details. +```scala +import scala.quoted.* +def singletonListExpr[T: Type](x: Expr[T])(using Quotes): Expr[List[T]] = + '{ List[T]($x) } // generic T used within a quote -Quotes and splices are duals of each other. -For arbitrary expressions `e` we have: +def emptyListExpr[T](using Type[T], Quotes): Expr[List[T]] = + '{ List.empty[T] } // generic T used within a quote +``` +If no other instance is found, the default `Type.of[T]` is used. +The following example implicitly uses `Type.of[String]` and `Type.of[Option[U]]`. ```scala -${'{e}} = e -'{${e}} = e +val list1: Expr[List[String]] = + singletonListExpr('{"hello"}) // requires a given `Type[Sting]` +val list0: Expr[List[Option[T]]] = + emptyListExpr[Option[U]] // requires a given `Type[Option[U]]` ``` -## Types for Quotations - -The type signatures of quotes and splices can be described using -two fundamental types: -- `Expr[T]`: abstract syntax trees representing expressions of type `T` -- `Type[T]`: non erased representation of type `T`. - -Quoting takes expressions of type `T` to expressions of type `Expr[T]` -and it takes types `T` to expressions of type `Type[T]`. Splicing -takes expressions of type `Expr[T]` to expressions of type `T` and it -takes expressions of type `Type[T]` to types `T`. - -The two types can be defined in package [`scala.quoted`](https://scala-lang.org/api/3.x/scala/quoted.html) as follows: +The `Type.of[T]` method is a primitive operation that the compiler will handle specially. +It will provide the implicit if the type `T` is statically known, or if `T` contains some other types `Ui` for which we have an implicit `Type[Ui]`. +In the example, `Type.of[String]` has a statically known type and `Type.of[Option[U]]` requires an implicit `Type[U]` in scope. +#### Quote context +We also track the current quotation context using a given `Quotes` instance. +To create a quote `'{..}` we require a given `Quotes` context, which should be passed as a contextual parameter `(using Quotes)` to the function. +Each splice will provide a new `Quotes` context within the scope of the splice. +Therefore quotes and splices can be seen as methods with the following signatures, but with special semantics. ```scala -package scala.quoted +def '[T](x: T): Quotes ?=> Expr[T] // def '[T](x: T)(using Quotes): Expr[T] -sealed trait Expr[+T] -sealed trait Type[T] +def $[T](x: Quotes ?=> Expr[T]): T ``` -Both `Expr` and `Type` are abstract and sealed, so all constructors for -these types are provided by the system. One way to construct values of -these types is by quoting, the other is by type-specific lifting -operations that will be discussed later on. +The lambda with a question mark `?=>` is a contextual function; it is a lambda that takes its argument implicitly and provides it implicitly in the implementation the lambda. +`Quotes` are used for a variety of purposes that will be mentioned when covering those topics. -## The Phase Consistency Principle +## Quoted Values -A fundamental *phase consistency principle* (PCP) regulates accesses -to free variables in quoted and spliced code: +#### Lifting +While it is not possible to use cross-stage persistence of local variables, it is possible to lift them to the next stage. +To this end, we provide the `Expr.apply` method, which can take a value and lift it into a quoted representation of the value. -- _For any free variable reference `x`, the number of quoted scopes and the number of spliced scopes between the reference to `x` and the definition of `x` must be equal_. - -Here, `this`-references count as free variables. On the other -hand, we assume that all imports are fully expanded and that `_root_` is -not a free variable. So references to global definitions are -allowed everywhere. +```scala +val expr1plus1: Expr[Int] = '{ 1 + 1 } -The phase consistency principle can be motivated as follows: First, -suppose the result of a program `P` is some quoted text `'{ ... x -... }` that refers to a free variable `x` in `P`. This can be -represented only by referring to the original variable `x`. Hence, the -result of the program will need to persist the program state itself as -one of its parts. We don’t want to do this, hence this situation -should be made illegal. Dually, suppose a top-level part of a program -is a spliced text `${ ... x ... }` that refers to a free variable `x` -in `P`. This would mean that we refer during _construction_ of `P` to -a value that is available only during _execution_ of `P`. This is of -course impossible and therefore needs to be ruled out. Now, the -small-step evaluation of a program will reduce quotes and splices in -equal measure using the cancellation rules above. But it will neither -create nor remove quotes or splices individually. So the PCP ensures -that program elaboration will lead to neither of the two unwanted -situations described above. +val expr2: Expr[Int] = Expr(1 + 1) // lift 2 into '{ 2 } +``` -In what concerns the range of features it covers, this form of macros introduces -a principled metaprogramming framework that is quite close to the MetaML family of -languages. One difference is that MetaML does not have an equivalent of the PCP - -quoted code in MetaML _can_ access variables in its immediately enclosing -environment, with some restrictions and caveats since such accesses involve -serialization. However, this does not constitute a fundamental gain in -expressiveness. +While it looks type wise similar to `'{ 1 + 1 }`, the semantics of `Expr(1 + 1)` are quite different. +`Expr(1 + 1)` will not stage or delay any computation; the argument is evaluated to a value and then lifted into a quote. +The quote will contain code that will create a copy of this value in the next stage. +`Expr` is polymorphic and user-extensible via the `ToExpr` type class. -## From `Expr`s to Functions and Back +```scala +trait ToExpr[T]: + def apply(x: T)(using Quotes): Expr[T] +``` -It is possible to convert any `Expr[T => R]` into `Expr[T] => Expr[R]` and back. -These conversions can be implemented as follows: +We can implement a `ToExpr` using a `given` definition that will add the definition to the implicits in scope. +In the following example we show how to implement a `ToExpr[Option[T]]` for any liftable type `T. ```scala -def to[T: Type, R: Type](f: Expr[T] => Expr[R])(using Quotes): Expr[T => R] = - '{ (x: T) => ${ f('x) } } - -def from[T: Type, R: Type](f: Expr[T => R])(using Quotes): Expr[T] => Expr[R] = - (x: Expr[T]) => '{ $f($x) } +given OptionToExpr[T: Type: ToExpr]: ToExpr[Option[T]] with + def apply(opt: Option[T])(using Quotes): Expr[Option[T]] = + opt match + case Some(x) => '{ Some[T]( ${Expr(x)} ) } + case None => '{ None } ``` -Note how the fundamental phase consistency principle works in two -different directions here for `f` and `x`. In the method `to`, the reference to `f` is -legal because it is quoted, then spliced, whereas the reference to `x` -is legal because it is spliced, then quoted. +The `ToExpr` for primitive types must be implemented as primitive operations in the system. +In our case, we use the reflection API to implement them. -They can be used as follows: +#### Extracting values from quotes +To be able to generate optimized code using the method `unrolledPowerCode`, the macro implementation `powerCode` needs to first +determine whether the argument passed as parameter `n` is a known constant value. +This can be achieved via _unlifting_ using the `Expr.unapply` extractor from our library implementation, which will only match if `n` is a quoted constant and extracts its value. ```scala -val f1: Expr[Int => String] = - to((x: Expr[Int]) => '{ $x.toString }) // '{ (x: Int) => x.toString } - -val f2: Expr[Int] => Expr[String] = - from('{ (x: Int) => x.toString }) // (x: Expr[Int]) => '{ ((x: Int) => x.toString)($x) } -f2('{2}) // '{ ((x: Int) => x.toString)(2) } +def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = + n match + case Expr(m) => // it is a constant: unlift code n='{m} into number m + unrolledPowerCode(x, m) + case _ => // not known: call power at run-time + '{ power($x, $n) } ``` -One limitation of `from` is that it does not β-reduce when a lambda is called immediately, as evidenced in the code `{ ((x: Int) => x.toString)(2) }`. -In some cases we want to remove the lambda from the code, for this we provide the method `Expr.betaReduce` that turns a tree -describing a function into a function mapping trees to trees. - +Alternatively, the `n.value` method can be used to get an `Option[Int]` with the value or `n.valueOrAbort` to get the value directly. ```scala -object Expr: - ... - def betaReduce[...](...)(...): ... = ... +def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = + // emits an error message if `n` is not a constant + unrolledPowerCode(x, n.valueOrAbort) ``` -The definition of `Expr.betaReduce(f)(x)` is assumed to be functionally the same as -`'{($f)($x)}`, however it should optimize this call by returning the -result of beta-reducing `f(x)` if `f` is a known lambda expression. -`Expr.betaReduce` distributes applications of `Expr` over function arrows: +`Expr.unapply` and all variants of `value` are polymorphic and user-extensible via a given `FromExpr` type class. ```scala -Expr.betaReduce(_): Expr[(T1, ..., Tn) => R] => ((Expr[T1], ..., Expr[Tn]) => Expr[R]) +trait FromExpr[T]: + def unapply(x: Expr[T])(using Quotes): Option[T] ``` -## Lifting Types +We can use `given` definitions to implement the `FromExpr` as we did for `ToExpr`. +The `FromExpr` for primitive types must be implemented as primitive operations in the system. +In our case, we use the reflection API to implement them. +To implement `FromExpr` for non-primitive types we use quote pattern matching (for example `OptionFromExpr`). -Types are not directly affected by the phase consistency principle. -It is possible to use types defined at any level in any other level. -But, if a type is used in a subsequent stage it will need to be lifted to a `Type`. -Indeed, the definition of `to` above uses `T` in the next stage, there is a -quote but no splice between the parameter binding of `T` and its -usage. But the code can be rewritten by adding an explicit binding of a `Type[T]`: - -```scala -def to[T, R](f: Expr[T] => Expr[R])(using t: Type[T])(using Type[R], Quotes): Expr[T => R] = - '{ (x: t.Underlying) => ${ f('x) } } -``` -In this version of `to`, the type of `x` is now the result of -inserting the type `Type[T]` and selecting its `Underlying`. +## Macros and Multi-Stage Programming -To avoid clutter, the compiler converts any type reference to -a type `T` in subsequent phases to `summon[Type[T]].Underlying`. +The system supports multi-stage macros and run-time multi-stage programming using the same quotation abstractions. -And to avoid duplication it does it once per type, and creates -an alias for that type at the start of the quote. +### Multi-Stage Macros -For instance, the user-level definition of `to`: +#### Macros +We can generalize the splicing abstraction to express macros. +A macro consists of a top-level splice that is not nested in any quote. +Conceptually, the contents of the splice are evaluated one stage earlier than the program. +In other words, the contents are evaluated while compiling the program. The generated code resulting from the macro replaces the splice in the program. ```scala -def to[T, R](f: Expr[T] => Expr[R])(using t: Type[T], r: Type[R])(using Quotes): Expr[T => R] = - '{ (x: T) => ${ f('x) } } +def power2(x: Double): Double = + ${ unrolledPowerCode('x, 2) } // x * x ``` -would be rewritten to +#### Inline macros +Since using the splices in the middle of a program is not as ergonomic as calling a function; we hide the staging mechanism from end-users of macros. We have a uniform way of calling macros and normal functions. +For this, _we restrict the use of top-level splices to only appear in inline methods_[^1][^2]. ```scala -def to[T, R](f: Expr[T] => Expr[R])(using t: Type[T], r: Type[R])(using Quotes): Expr[T => R] = - '{ - type T = t.Underlying - (x: T) => ${ f('x) } - } +// inline macro definition +inline def powerMacro(x: Double, inline n: Int): Double = + ${ powerCode('x, 'n) } + +// user code +def power2(x: Double): Double = + powerMacro(x, 2) // x * x ``` -The `summon` query succeeds because there is a given instance of -type `Type[T]` available (namely the given parameter corresponding -to the context bound `: Type`), and the reference to that value is -phase-correct. If that was not the case, the phase inconsistency for -`T` would be reported as an error. +The evaluation of the macro will only happen when the code is inlined into `power2`. +When inlined, the code is equivalent to the previous definition of `power2`. +A consequence of using inline methods is that none of the arguments nor the return type of the macro will have to mention the `Expr` types; this hides all aspects of metaprogramming from the end-users. -## Lifting Expressions +#### Avoiding a complete interpreter +When evaluating a top-level splice, the compiler needs to interpret the code that is within the splice. +Providing an interpreter for the entire language is quite tricky, and it is even more challenging to make that interpreter run efficiently. +To avoid needing a complete interpreter, we can impose the following restrictions on splices to simplify the evaluation of the code in top-level splices. + * The top-level splice must contain a single call to a compiled static method. + * Arguments to the function are literal constants, quoted expressions (parameters), calls to `Type.of` for type parameters and a reference to `Quotes`. -Consider the following implementation of a staged interpreter that implements -a compiler through staging. +In particular, these restrictions disallow the use of splices in top-level splices. +Such a splice would require several stages of interpretation which would be unnecessarily inefficient. +#### Compilation stages +The macro implementation (i.e., the method called in the top-level splice) can come from any pre-compiled library. +This provides a clear difference between the stages of the compilation process. +Consider the following 3 source files defined in distinct libraries. ```scala -import scala.quoted.* - -enum Exp: - case Num(n: Int) - case Plus(e1: Exp, e2: Exp) - case Var(x: String) - case Let(x: String, e: Exp, in: Exp) - -import Exp.* +// Macro.scala +def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = ... +inline def powerMacro(x: Double, inline n: Int): Double = + ${ powerCode('x, 'n) } ``` -The interpreted language consists of numbers `Num`, addition `Plus`, and variables -`Var` which are bound by `Let`. Here are two sample expressions in the language: - ```scala -val exp = Plus(Plus(Num(2), Var("x")), Num(4)) -val letExp = Let("x", Num(3), exp) +// Lib.scala (depends on Macro.scala) +def power2(x: Double) = + ${ powerCode('x, '{2}) } // inlined from a call to: powerMacro(x, 2) ``` -Here’s a compiler that maps an expression given in the interpreted -language to quoted Scala code of type `Expr[Int]`. -The compiler takes an environment that maps variable names to Scala `Expr`s. - ```scala -import scala.quoted.* - -def compile(e: Exp, env: Map[String, Expr[Int]])(using Quotes): Expr[Int] = - e match - case Num(n) => - Expr(n) - case Plus(e1, e2) => - '{ ${ compile(e1, env) } + ${ compile(e2, env) } } - case Var(x) => - env(x) - case Let(x, e, body) => - '{ val y = ${ compile(e, env) }; ${ compile(body, env + (x -> 'y)) } } +// App.scala (depends on Lib.scala) +@main def app() = power2(3.14) ``` - -Running `compile(letExp, Map())` would yield the following Scala code: +One way to syntactically visualize this is to put the application in a quote that delays the compilation of the application. +Then the application dependencies can be placed in an outer quote that contains the quoted application, and we repeat this recursively for dependencies of dependencies. ```scala -'{ val y = 3; (2 + y) + 4 } +'{ // macro library (compilation stage 1) + def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = + ... + inline def powerMacro(x: Double, inline n: Int): Double = + ${ powerCode('x, 'n) } + '{ // library using macros (compilation stage 2) + def power2(x: Double) = + ${ powerCode('x, '{2}) } // inlined from a call to: powerMacro(x, 2) + '{ power2(3.14) /* app (compilation stage 3) */ } + } +} ``` -The body of the first clause, `case Num(n) => Expr(n)`, looks suspicious. `n` -is declared as an `Int`, yet it is converted to an `Expr[Int]` with `Expr()`. -Shouldn’t `n` be quoted? In fact this would not -work since replacing `n` by `'n` in the clause would not be phase -correct. +To make the system more versatile, we allow calling macros in the project where it is defined, with some restrictions. +For example, to compile `Macro.scala` and `Lib.scala` together in the same library. +To this end, we do not follow the simpler syntactic model and rely on semantic information from the source files. +When compiling a source, if we detect a call to a macro that is not compiled yet, we delay the compilation of this source to the following compilation stage. +In the example, we would delay the compilation of `Lib.scala` because it contains a compile-time call to `powerCode`. +Compilation stages are repeated until all sources are compiled, or no progress can be made. +If no progress is made, there was a cyclic dependency between the definition and the use of the macro. +We also need to detect if at runtime the macro depends on sources that have not been compiled yet. +These are detected by executing the macro and checking for JVM linking errors to classes that have not been compiled yet. -The `Expr.apply` method is defined in package `quoted`: +### Run-Time Multi-Stage Programming -```scala -package quoted +See [Run-Time Multi-Stage Programming](./staging.md) -object Expr: - ... - def apply[T: ToExpr](x: T)(using Quotes): Expr[T] = - summon[ToExpr[T]].toExpr(x) -``` +## Safety -This method says that values of types implementing the `ToExpr` type class can be -converted to `Expr` values using `Expr.apply`. +Multi-stage programming is by design statically safe and cross-stage safe. -Scala 3 comes with given instances of `ToExpr` for -several types including `Boolean`, `String`, and all primitive number -types. For example, `Int` values can be converted to `Expr[Int]` -values by wrapping the value in a `Literal` tree node. This makes use -of the underlying tree representation in the compiler for -efficiency. But the `ToExpr` instances are nevertheless not _magic_ -in the sense that they could all be defined in a user program without -knowing anything about the representation of `Expr` trees. For -instance, here is a possible instance of `ToExpr[Boolean]`: +### Static Safety -```scala -given ToExpr[Boolean] with - def toExpr(b: Boolean) = - if b then '{ true } else '{ false } -``` +#### Hygiene +All identifier names are interpreted as symbolic references to the corresponding variable in the context of the quote. +Therefore, while evaluating the quote, it is not possible to accidentally rebind a reference to a new variable with the same textual name. -Once we can lift bits, we can work our way up. For instance, here is a -possible implementation of `ToExpr[Int]` that does not use the underlying -tree machinery: +#### Well-typed +If a quote is well typed, then the generated code is well typed. +This is a simple consequence of tracking the type of each expression. +An `Expr[T]` can only be created from a quote that contains an expression of type `T. +Conversely, an `Expr[T]` can only be spliced in a location that expects a type `T. +As mentioned before, `Expr` is covariant in its type parameter. +This means that an `Expr[T]` can contain an expression of a subtype of `T. +When spliced in a location that expects a type `T, these expressions also have a valid type. -```scala -given ToExpr[Int] with - def toExpr(n: Int) = n match - case Int.MinValue => '{ Int.MinValue } - case _ if n < 0 => '{ - ${ toExpr(-n) } } - case 0 => '{ 0 } - case _ if n % 2 == 0 => '{ ${ toExpr(n / 2) } * 2 } - case _ => '{ ${ toExpr(n / 2) } * 2 + 1 } -``` +### Cross-Stage Safety -Since `ToExpr` is a type class, its instances can be conditional. For example, -a `List` is liftable if its element type is: +#### Level consistency +We define the _staging level_ of some code as the number of quotes minus the number of splices surrounding said code. +Local variables must be defined and used in the same staging level. + +It is never possible to access a local variable from a lower staging level as it does not yet exist. ```scala -given [T: ToExpr : Type]: ToExpr[List[T]] with - def toExpr(xs: List[T]) = xs match - case head :: tail => '{ ${ Expr(head) } :: ${ toExpr(tail) } } - case Nil => '{ Nil: List[T] } +def badPower(x: Double, n: Int): Double = + ${ unrolledPowerCode('x, n) } // error: value of `n` not known yet ``` -In the end, `ToExpr` resembles very much a serialization -framework. Like the latter it can be derived systematically for all -collections, case classes and enums. Note also that the synthesis -of _type-tag_ values of type `Type[T]` is essentially the type-level -analogue of lifting. -Using lifting, we can now give the missing definition of `showExpr` in the introductory example: +In the context of macros and _cross-platform portability_, that is, +macros compiled on one machine but potentially executed on another, +we cannot support cross-stage persistence of local variables. +Therefore, local variables can only be accessed at precisely the same staging level in our system. ```scala -def showExpr[T](expr: Expr[T])(using Quotes): Expr[String] = - val code: String = expr.show - Expr(code) +def badPowerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = + // error: `n` potentially not available in the next execution environment + '{ power($x, n) } ``` -That is, the `showExpr` method converts its `Expr` argument to a string (`code`), and lifts -the result back to an `Expr[String]` using `Expr.apply`. -## Lifting Types +The rules are slightly different for global definitions, such as `unrolledPowerCode`. +It is possible to generate code that contains a reference to a _global_ definition such as in `'{ power(2, 4) }`. +This is a limited form of cross-stage persistence that does not impede cross-platform portability, where we refer to the already compiled code for `power`. +Each compilation step will lower the staging level by one while keeping global definitions. +In consequence, we can refer to compiled definitions in macros such as `unrolledPowerCode` in `${ unrolledPowerCode('x, 2) }`. + +We can sumarize level consistency in two rules: + * Local variables can be used only at the same staging level as their definition + * Global variables can be used at any staging level -The previous section has shown that the metaprogramming framework has -to be able to take a type `T` and convert it to a type tree of type -`Type[T]` that can be reified. This means that all free variables of -the type tree refer to types and values defined in the current stage. -For a reference to a global class, this is easy: Just issue the fully -qualified name of the class. Members of reifiable types are handled by -just reifying the containing type together with the member name. But -what to do for references to type parameters or local type definitions -that are not defined in the current stage? Here, we cannot construct -the `Type[T]` tree directly, so we need to get it from a recursive -implicit search. For instance, to implement +#### Type consistency +As Scala uses type erasure, generic types will be erased at run-time and hence in any following stage. +To ensure any quoted expression that refers to a generic type `T` does not lose the information it needs, we require a given `Type[T]` in scope. +The `Type[T]` will carry over the non-erased representation of the type into the next phase. +Therefore any generic type used at a higher staging level than its definition will require its `Type`. +#### Scope extrusion +Within the contents of a splice, it is possible to have a quote that refers to a local variable defined in the outer quote. +If this quote is used within the splice, the variable will be in scope. +However, if the quote is somehow _extruded_ outside the splice, then variables might not be in scope anymore. +Quoted expressions can be extruded using side effects such as mutable state and exceptions. +The following example shows how a quote can be extruded using mutable state. ```scala -summon[Type[List[T]]] +var x: Expr[T] = null +'{ (y: T) => ${ x = 'y; 1 } } +x // has value '{y} but y is not in scope ``` -where `T` is not defined in the current stage, we construct the type constructor -of `List` applied to the splice of the result of searching for a given instance for `Type[T]`: +A second way a variable can be extruded is through the `run` method. +If `run` consumes a quoted variable reference, it will not be in scope anymore. +The result will reference a variable that is defined in the next stage. ```scala -Type.of[ List[ summon[Type[T]].Underlying ] ] +'{ (x: Int) => ${ run('x); ... } } +// evaluates to: '{ (x: Int) => ${ x; ... } 1 ``` -This is exactly the algorithm that Scala 2 uses to search for type tags. -In fact Scala 2's type tag feature can be understood as a more ad-hoc version of -`quoted.Type`. As was the case for type tags, the implicit search for a `quoted.Type` -is handled by the compiler, using the algorithm sketched above. +To catch both scope extrusion scenarios, our system restricts the use of quotes by only allowing a quote to be spliced if it was not extruded from a splice scope. +Unlike level consistency, this is checked at run-time[^4] rather than compile-time to avoid making the static type system too complicated. -## Relationship with `inline` +Each `Quotes` instance contains a unique scope identifier and refers to its parent scope, forming a stack of identifiers. +The parent of the scope of a `Quotes` is the scope of the `Quotes` used to create the enclosing quote. +Top-level splices and `run` create new scope stacks. +Every `Expr` knows in which scope it was created. +When it is spliced, we check that the quote scope is either the same as the splice scope, or a parent scope thereof. -Seen by itself, principled metaprogramming looks more like a framework for -runtime metaprogramming than one for compile-time metaprogramming with macros. -But combined with Scala 3’s `inline` feature it can be turned into a compile-time -system. The idea is that macro elaboration can be understood as a combination of -a macro library and a quoted program. For instance, here’s the `assert` macro -again together with a program that calls `assert`. -```scala -object Macros: +## Staged Lambdas - inline def assert(inline expr: Boolean): Unit = - ${ assertImpl('expr) } +When staging programs in a functional language there are two fundamental abstractions: a staged lambda `Expr[T => U]` and a staging lambda `Expr[T] => Expr[U]`. +The first is a function that will exist in the next stage, whereas the second is a function that exists in the current stage. +It is often convenient to have a mechanism to go from `Expr[T => U]` to `Expr[T] => Expr[U]` and vice versa. - def assertImpl(expr: Expr[Boolean])(using Quotes) = - val failMsg: Expr[String] = Expr("failed assertion: " + expr.show) - '{ if !($expr) then throw new AssertionError($failMsg) } +```scala +def later[T: Type, U: Type](f: Expr[T] => Expr[U]): Expr[T => U] = + '{ (x: T) => ${ f('x) } } -@main def program = - val x = 1 - Macros.assert(x != 0) +def now[T: Type, U: Type](f: Expr[T => U]): Expr[T] => Expr[U] = + (x: Expr[T]) => '{ $f($x) } ``` -Inlining the `assert` function would give the following program: +Both conversions can be performed out of the box with quotes and splices. +But if `f` is a known lambda function, `'{ $f($x) }` will not beta-reduce the lambda in place. +This optimization is performed in a later phase of the compiler. +Not reducing the application immediately can simplify analysis of generated code. +Nevertheless, it is possible to beta-reduce the lambda in place using the `Expr.betaReduce` method. ```scala -@main def program = - val x = 1 - ${ Macros.assertImpl('{ x != 0}) } +def now[T: Type, U: Type](f: Expr[T => U]): Expr[T] => Expr[U] = + (x: Expr[T]) => Expr.betaReduce('{ $f($x) }) ``` -The example is only phase correct because `Macros` is a global value and -as such not subject to phase consistency checking. Conceptually that’s -a bit unsatisfactory. If the PCP is so fundamental, it should be -applicable without the global value exception. But in the example as -given this does not hold since both `assert` and `program` call -`assertImpl` with a splice but no quote. +The `betaReduce` method will beta-reduce the outermost application of the expression if possible (regardless of arity). +If it is not possible to beta-reduce the expression, then it will return the original expression. -However, one could argue that the example is really missing -an important aspect: The macro library has to be compiled in a phase -prior to the program using it, but in the code above, macro -and program are defined together. A more accurate view of -macros would be to have the user program be in a phase after the macro -definitions, reflecting the fact that macros have to be defined and -compiled before they are used. Hence, conceptually the program part -should be treated by the compiler as if it was quoted: +## Staged Constructors +To create new class instances in a later stage, we can create them using factory methods (usually `apply` methods of an `object`), or we can instantiate them with a `new`. +For example, we can write `Some(1)` or `new Some(1)`, creating the same value. +In Scala 3, using the factory method call notation will fall back to a `new` if no `apply` method is found. +We follow the usual staging rules when calling a factory method. +Similarly, when we use a `new C`, the constructor of `C` is implicitly called, which also follows the usual staging rules. +Therefore for an arbitrary known class `C`, we can use both `'{ C(...) }` or `'{ new C(...) }` as constructors. +## Staged Classes +Quoted code can contain any valid expression including local class definitions. +This allows the creation of new classes with specialized implementations. +For example, we can implement a new version of `Runnable` that will perform some optimized operation. ```scala -@main def program = '{ - val x = 1 - ${ Macros.assertImpl('{ x != 0 }) } +def mkRunnable(x: Int)(using Quotes): Expr[Runnable] = '{ + class MyRunnable extends Runnable: + def run(): Unit = ... // generate some custom code that uses `x` + new MyRunnable } ``` -If `program` is treated as a quoted expression, the call to -`Macro.assertImpl` becomes phase correct even if macro library and -program are conceptualized as local definitions. - -But what about the call from `assert` to `assertImpl`? Here, we need a -tweak of the typing rules. An inline function such as `assert` that -contains a splice operation outside an enclosing quote is called a -_macro_. Macros are supposed to be expanded in a subsequent phase, -i.e. in a quoted context. Therefore, they are also type checked as if -they were in a quoted context. For instance, the definition of -`assert` is typechecked as if it appeared inside quotes. This makes -the call from `assert` to `assertImpl` phase-correct, even if we -assume that both definitions are local. +The quoted class is a local class and its type cannot escape the enclosing quote. +The class must be used inside the quote or an instance of it can be returned using a known interface (`Runnable` in this case). -The `inline` modifier is used to declare a `val` that is -either a constant or is a parameter that will be a constant when instantiated. This -aspect is also important for macro expansion. +## Quote Pattern Matching -To get values out of expressions containing constants `Expr` provides the method -`value` (or `valueOrError`). This will convert the `Expr[T]` into a `Some[T]` (or `T`) when the -expression contains value. Otherwise it will return `None` (or emit an error). -To avoid having incidental val bindings generated by the inlining of the `def` -it is recommended to use an inline parameter. To illustrate this, consider an -implementation of the `power` function that makes use of a statically known exponent: +It is sometimes necessary to analyze the structure of the code or decompose the code into its sub-expressions. +A classic example is an embedded DSL, where a macro knows a set of definitions that it can reinterpret while compiling the code (for instance, to perform optimizations). +In the following example, we extend our previous implementation of `powCode` to look into `x` to perform further optimizations. ```scala -inline def power(x: Double, inline n: Int) = ${ powerCode('x, 'n) } - -private def powerCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = - n.value match - case Some(m) => powerCode(x, m) - case None => '{ Math.pow($x, $n.toDouble) } - -private def powerCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = - if n == 0 then '{ 1.0 } - else if n == 1 then x - else if n % 2 == 0 then '{ val y = $x * $x; ${ powerCode('y, n / 2) } } - else '{ $x * ${ powerCode(x, n - 1) } } +def fusedPowCode(x: Expr[Double], n: Expr[Int])(using Quotes): Expr[Double] = + x match + case '{ power($y, $m) } => // we have (y^m)^n + fusedPowCode(y, '{ $n * $m }) // generate code for y^(n*m) + case _ => + '{ power($x, $n) } ``` -## Scope Extrusion - -Quotes and splices are duals as far as the PCP is concerned. But there is an -additional restriction that needs to be imposed on splices to guarantee -soundness: code in splices must be free of side effects. The restriction -prevents code like this: - -```scala -var x: Expr[T] = ... -'{ (y: T) => ${ x = 'y; 1 } } -``` -This code, if it was accepted, would _extrude_ a reference to a quoted variable -`y` from its scope. This would subsequently allow access to a variable outside the -scope where it is defined, which is likely problematic. The code is clearly -phase consistent, so we cannot use PCP to rule it out. Instead, we postulate a -future effect system that can guarantee that splices are pure. In the absence of -such a system we simply demand that spliced expressions are pure by convention, -and allow for undefined compiler behavior if they are not. This is analogous to -the status of pattern guards in Scala, which are also required, but not -verified, to be pure. +#### Sub-patterns -[Multi-Stage Programming](./staging.md) introduces one additional method where -you can expand code at runtime with a method `run`. There is also a problem with -that invocation of `run` in splices. Consider the following expression: +In quoted patterns, the `$` binds the sub-expression to an expression `Expr` that can be used in that `case` branch. +The contents of `${..}` in a quote pattern are regular Scala patterns. +For example, we can use the `Expr(_)` pattern within the `${..}` to only match if it is a known value and extract it. ```scala -'{ (x: Int) => ${ run('x); 1 } } +def fusedUnrolledPowCode(x: Expr[Double], n: Int)(using Quotes): Expr[Double] = + x match + case '{ power($y, ${Expr(m)}) } => // we have (y^m)^n + fusedUnrolledPowCode(y, n * m) // generate code for y * ... * y + case _ => // ( n*m times ) + unrolledPowerCode(x, n) ``` -This is again phase correct, but will lead us into trouble. Indeed, evaluating -the splice will reduce the expression `run('x)` to `x`. But then the result +These value extraction sub-patterns can be polymorphic using an instance of `FromExpr`. +In the following example, we show the implementation of `OptionFromExpr` which internally uses the `FromExpr[T]` to extract the value using the `Expr(x)` pattern. ```scala -'{ (x: Int) => ${ x; 1 } } +given OptionFromExpr[T](using Type[T], FromExpr[T]): FromExpr[Option[T]] with + def unapply(x: Expr[Option[T]])(using Quotes): Option[Option[T]] = + x match + case '{ Some( ${Expr(x)} ) } => Some(Some(x)) + case '{ None } => Some(None) + case _ => None ``` -is no longer phase correct. To prevent this soundness hole it seems easiest to -classify `run` as a side-effecting operation. It would thus be prevented from -appearing in splices. In a base language with side effects we would have to do this -anyway: Since `run` runs arbitrary code it can always produce a side effect if -the code it runs produces one. -## Example Expansion -Assume we have two methods, one `map` that takes an `Expr[Array[T]]` and a -function `f` and one `sum` that performs a sum by delegating to `map`. +#### Closed patterns +Patterns may contain two kinds of references: global references such as the call to the `power` method in `'{ power(...) }`, or references to bindings defined in the pattern such as `x` in `case '{ (x: Int) => x }`. +When extracting an expression from a quote, we need to ensure that we do not extrude any variable from the scope where it is defined. ```scala -object Macros: - - def map[T](arr: Expr[Array[T]], f: Expr[T] => Expr[Unit]) - (using Type[T], Quotes): Expr[Unit] = '{ - var i: Int = 0 - while i < ($arr).length do - val element: T = ($arr)(i) - ${f('element)} - i += 1 - } - - def sum(arr: Expr[Array[Int]])(using Quotes): Expr[Int] = '{ - var sum = 0 - ${ map(arr, x => '{sum += $x}) } - sum - } - - inline def sum_m(arr: Array[Int]): Int = ${sum('arr)} - -end Macros +'{ (x: Int) => x + 1 } match + case '{ (y: Int) => $z } => + // should not match, otherwise: z = '{ x + 1 } ``` -A call to `sum_m(Array(1,2,3))` will first inline `sum_m`: +In this example, we see that the pattern should not match. +Otherwise, any use of the expression `z` would contain an unbound reference to `x`. +To avoid any such extrusion, we only match on a `${..}` if its expression is closed under the definitions within the pattern. +Therefore, the pattern will not match if the expression is not closed. + +#### HOAS patterns +To allow extracting expressions that may contain extruded references we offer a _higher-order abstract syntax_ (HOAS) pattern `$f(y)` (or `$f(y1,...,yn)`). +This pattern will eta-expand the sub-expression with respect to `y` and bind it to `f`. +The lambda arguments will replace the variables that might have been extruded. ```scala -val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*) -${_root_.Macros.sum('arr)} +'{ ((x: Int) => x + 1).apply(2) } match + case '{ ((y: Int) => $f(y)).apply($z: Int) } => + // f may contain references to `x` (replaced by `$y`) + // f = (y: Expr[Int]) => '{ $y + 1 } + f(z) // generates '{ 2 + 1 } ``` -then it will splice `sum`: -```scala -val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*) +A HOAS pattern `$x(y1,...,yn)` will only match the expression if it does not contain references to variables defined in the pattern that are not in the set `y1,...,yn`. +In other words, the pattern will match if the expression only contains references to variables defined in the pattern that are in `y1,...,yn`. +Note that the HOAS patterns `$x()` are semantically equivalent to closed patterns `$x`. -var sum = 0 -${ map('arr, x => '{sum += $x}) } -sum -``` -then it will inline `map`: +#### Type variables +Expressions may contain types that are not statically known. +For example, an `Expr[List[Int]]` may contain `list.map(_.toInt)` where `list` is a `List` of some type. +To cover all the possible cases we would need to explicitly match `list` on all possible types (`List[Int]`, `List[Int => Int]`, ...). +This is an infinite set of types and therefore pattern cases. +Even if we would know all possible types that a specific program could use, we may still end up with an unmanageable number of cases. +To overcome this, we introduce type variables in quoted patterns, which will match any type. + +In the following example, we show how type variables `t` and `u` match all possible pairs of consecutive calls to `map` on lists. +In the quoted patterns, types named with lower cases are identified as type variables. +This follows the same notation as type variables used in normal patterns. ```scala -val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*) +def fuseMapCode(x: Expr[List[Int]]): Expr[List[Int]] = + x match + case '{ ($ls: List[t]).map[u]($f).map[Int]($g) } => + '{ $ls.map($g.compose($f)) } + ... -var sum = 0 -val f = x => '{sum += $x} -${ _root_.Macros.map('arr, 'f)(Type.of[Int])} -sum +fuseMapCode('{ List(1.2).map(f).map(g) }) // '{ List(1.2).map(g.compose(f)) } +fuseMapCode('{ List('a').map(h).map(i) }) // '{ List('a').map(i.compose(h)) } ``` +Variables `f` and `g` are inferred to be of type `Expr[t => u]` and `Expr[u => Int]` respectively. +Subsequently, we can infer `$g.compose($f)` to be of type `Expr[t => Int]` which is the type of the argument of `$ls.map(..)`. -then it will expand and splice inside quotes `map`: +Type variables are abstract types that will be erased; this implies that to reference them in the second quote we need a given `Type[t]` and `Type[u]`. +The quoted pattern will implicitly provide those given types. +At run-time, when the pattern matches, the type of `t` and `u` will be known, and the `Type[t]` and `Type[u]` will contain the precise types in the expression. -```scala -val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*) +As `Expr` is covariant, the statically known type of the expression might not be the actual type. +Type variables can also be used to recover the precise type of the expression. +``scala +def let(x: Expr[Any])(using Quotes): Expr[Any] = + x match + case '{ $x: t } => + '{ val y: t = $x; y } -var sum = 0 -val f = x => '{sum += $x} -var i: Int = 0 -while i < arr.length do - val element: Int = (arr)(i) - sum += element - i += 1 -sum +let('{1}) // will return a `Expr[Any]` that contains an `Expr[Int]]` ``` -Finally cleanups and dead code elimination: - +While we can define the type variable in the middle of the pattern, their normal form is to define them as a `type` with a lower case name at the start of the pattern. +We use the Scala backquote `` `t` `` naming convention which interprets the string within the backquote as a literal name identifier. +This is typically used when we have names that contain special characters that are not allowed for normal Scala identifiers. +But we use it to explicitly state that this is a reference to that name and not the introduction of a new variable. ```scala -val arr: Array[Int] = Array.apply(1, [2,3 : Int]:Int*) -var sum = 0 -var i: Int = 0 -while i < arr.length do - val element: Int = arr(i) - sum += element - i += 1 -sum + case '{ type t; $x: `t` } => ``` - -## Find implicits within a macro - -Similarly to the `summonFrom` construct, it is possible to make implicit search available -in a quote context. For this we simply provide `scala.quoted.Expr.summon`: +This is a bit more verbose but has some expressivity advantages such as allowing to define bounds on the variables and be able to refer to them several times in any scope of the pattern. ```scala -import scala.collection.immutable.{ TreeSet, HashSet } -inline def setFor[T]: Set[T] = ${ setForExpr[T] } - -def setForExpr[T: Type](using Quotes): Expr[Set[T]] = - Expr.summon[Ordering[T]] match - case Some(ord) => '{ new TreeSet[T]()($ord) } - case _ => '{ new HashSet[T] } + case '{ type t >: List[Int] <: Seq[Int]; $x: `t` } => + case '{ type t; $x: (`t`, `t`) } => ``` -## Relationship with Transparent Inline -[Inline](./inline.md) documents inlining. The code below introduces a transparent -inline method that can calculate either a value of type `Int` or a value of type -`String`. +#### Type patterns +It is possible to only have a type and no expression of that type. +To be able to inspect a type, we introduce quoted type pattern `case '[..] =>`. +It works the same way as a quoted pattern but is restricted to contain a type. +Type variables can be used in quoted type patterns to extract a type. ```scala -transparent inline def defaultOf(inline str: String) = - ${ defaultOfImpl('str) } - -def defaultOfImpl(strExpr: Expr[String])(using Quotes): Expr[Any] = - strExpr.valueOrError match - case "int" => '{1} - case "string" => '{"a"} - -// in a separate file -val a: Int = defaultOf("int") -val b: String = defaultOf("string") - +def empty[T: Type]: Expr[T] = + Type.of[T] match + case '[String] => '{ "" } + case '[List[t]] => '{ List.empty[t] } + ... ``` -## Defining a macro and using it in a single project - -It is possible to define macros and use them in the same project as long as the implementation -of the macros does not have run-time dependencies on code in the file where it is used. -It might still have compile-time dependencies on types and quoted code that refers to the use-site file. - -To provide this functionality Scala 3 provides a transparent compilation mode where files that -try to expand a macro but fail because the macro has not been compiled yet are suspended. -If there are any suspended files when the compilation ends, the compiler will automatically restart -compilation of the suspended files using the output of the previous (partial) compilation as macro classpath. -In case all files are suspended due to cyclic dependencies the compilation will fail with an error. +`Type.of[T]` is used to summon the given instance of `Type[T]` in scope, it is equivalent to `summon[Type[T]]`. -## Pattern matching on quoted expressions +#### Type testing and casting +It is important to note that instance checks and casts on `Expr`, such as `isInstanceOf[Expr[T]]` and `asInstanceOf[Expr[T]]`, will only check if the instance is of the class `Expr` but will not be able to check the `T` argument. +These cases will issue a warning at compile-time, but if they are ignored, they can result in unexpected behavior. -It is possible to deconstruct or extract values out of `Expr` using pattern matching. +These operations can be supported correctly in the system. +For a simple type test it is possible to use the `isExprOf[T]` method of `Expr` to check if it is an instance of that type. +Similarly, it is possible to use `asExprOf[T]` to cast an expression to a given type. +These operations use a given `Type[T]` to work around type erasure. -`scala.quoted` contains objects that can help extracting values from `Expr`. -- `scala.quoted.Expr`/`scala.quoted.Exprs`: matches an expression of a value (or list of values) and returns the value (or list of values). -- `scala.quoted.Const`/`scala.quoted.Consts`: Same as `Expr`/`Exprs` but only works on primitive values. -- `scala.quoted.Varargs`: matches an explicit sequence of expressions and returns them. These sequences are useful to get individual `Expr[T]` out of a varargs expression of type `Expr[Seq[T]]`. +## Sub-Expression Transformation -These could be used in the following way to optimize any call to `sum` that has statically known values. +The system provides a mechanism to transform all sub-expressions of an expression. +This is useful when the sub-expressions we want to transform are deep in the expression. +It is also necessary if the expression contains sub-expressions that cannot be matched using quoted patterns (such as local class definitions). ```scala -inline def sum(inline args: Int*): Int = ${ sumExpr('args) } -private def sumExpr(argsExpr: Expr[Seq[Int]])(using Quotes): Expr[Int] = - argsExpr match - case Varargs(args @ Exprs(argValues)) => - // args is of type Seq[Expr[Int]] - // argValues is of type Seq[Int] - Expr(argValues.sum) // precompute result of sum - case Varargs(argExprs) => // argExprs is of type Seq[Expr[Int]] - val staticSum: Int = argExprs.map(_.value.getOrElse(0)).sum - val dynamicSum: Seq[Expr[Int]] = argExprs.filter(_.value.isEmpty) - dynamicSum.foldLeft(Expr(staticSum))((acc, arg) => '{ $acc + $arg }) - case _ => - '{ $argsExpr.sum } +trait ExprMap: + def transform[T](e: Expr[T])(using Type[T])(using Quotes): Expr[T] + def transformChildren[T](e: Expr[T])(using Type[T])(using Quotes): Expr[T] = + ... ``` -### Quoted patterns - -Quoted pattens allow deconstructing complex code that contains a precise structure, types or methods. -Patterns `'{ ... }` can be placed in any location where Scala expects a pattern. - -For example +Users can extend the `ExprMap` trait and implement the `transform` method. +This interface is flexible and can implement top-down, bottom-up, or other transformations. ```scala -optimize { - sum(sum(1, a, 2), 3, b) -} // should be optimized to 6 + a + b -``` - -```scala -def sum(args: Int*): Int = args.sum -inline def optimize(inline arg: Int): Int = ${ optimizeExpr('arg) } -private def optimizeExpr(body: Expr[Int])(using Quotes): Expr[Int] = - body match - // Match a call to sum without any arguments - case '{ sum() } => Expr(0) - // Match a call to sum with an argument $n of type Int. - // n will be the Expr[Int] representing the argument. - case '{ sum($n) } => n - // Match a call to sum and extracts all its args in an `Expr[Seq[Int]]` - case '{ sum(${Varargs(args)}: _*) } => sumExpr(args) - case body => body - -private def sumExpr(args1: Seq[Expr[Int]])(using Quotes): Expr[Int] = - def flatSumArgs(arg: Expr[Int]): Seq[Expr[Int]] = arg match - case '{ sum(${Varargs(subArgs)}: _*) } => subArgs.flatMap(flatSumArgs) - case arg => Seq(arg) - val args2 = args1.flatMap(flatSumArgs) - val staticSum: Int = args2.map(_.value.getOrElse(0)).sum - val dynamicSum: Seq[Expr[Int]] = args2.filter(_.value.isEmpty) - dynamicSum.foldLeft(Expr(staticSum))((acc, arg) => '{ $acc + $arg }) +object OptimizeIdentity extends ExprMap: + def transform[T](e: Expr[T])(using Type[T])(using Quotes): Expr[T] = + transformChildren(e) match // bottom-up transformation + case '{ identity($x) } => x + case _ => e ``` -### Recovering precise types using patterns +The `transformChildren` method is implemented as a primitive that knows how to reach all the direct sub-expressions and calls `transform` on each one. +The type passed to `transform` is the expected type of this sub-expression in its expression. +For example while transforming `Some(1)` in `'{ val x: Option[Int] = Some(1); ...}` the type will be `Option[Int]` and not `Some[Int]`. +This implies that we can safely transform `Some(1)` into `None`. -Sometimes it is necessary to get a more precise type for an expression. This can be achieved using the following pattern match. +## Staged Implicit Summoning +When summoning implicit arguments using `summon`, we will find the given instances in the current scope. +It is possible to use `summon` to get staged implicit arguments by explicitly staging them first. +In the following example, we can pass an implicit `Ordering[T]` in a macro as an `Expr[Ordering[T]]` to its implementation. +Then we can splice it and give it implicitly in the next stage. ```scala -def f(expr: Expr[Any])(using Quotes) = expr match - case '{ $x: t } => - // If the pattern match succeeds, then there is - // some type `t` such that - // - `x` is bound to a variable of type `Expr[t]` - // - `t` is bound to a new type `t` and a given - // instance `Type[t]` is provided for it - // That is, we have `x: Expr[t]` and `given Type[t]`, - // for some (unknown) type `t`. -``` +inline def treeSetFor[T](using ord: Ordering[T]): Set[T] = + ${ setExpr[T](using 'ord) } -This might be used to then perform an implicit search as in: - -```scala -extension (inline sc: StringContext) - inline def showMe(inline args: Any*): String = ${ showMeExpr('sc, 'args) } - -private def showMeExpr(sc: Expr[StringContext], argsExpr: Expr[Seq[Any]])(using Quotes): Expr[String] = - import quotes.reflect.report - argsExpr match - case Varargs(argExprs) => - val argShowedExprs = argExprs.map { - case '{ $arg: tp } => - Expr.summon[Show[tp]] match - case Some(showExpr) => - '{ $showExpr.show($arg) } - case None => - report.error(s"could not find implicit for ${Type.show[Show[tp]]}", arg); '{???} - } - val newArgsExpr = Varargs(argShowedExprs) - '{ $sc.s($newArgsExpr: _*) } - case _ => - // `new StringContext(...).showMeExpr(args: _*)` not an explicit `showMeExpr"..."` - report.error(s"Args must be explicit", argsExpr) - '{???} - -trait Show[-T]: - def show(x: T): String - -// in a different file -given Show[Boolean] with - def show(b: Boolean) = "boolean!" - -println(showMe"${true}") +def setExpr[T:Type](using ord: Expr[Ordering[T]])(using Quotes): Expr[Set[T]] = + '{ given Ordering[T] = $ord; new TreeSet[T]() } ``` -### Open code patterns - -Quoted pattern matching also provides higher-order patterns to match open terms. If a quoted term contains a definition, -then the rest of the quote can refer to this definition. - -```scala -'{ - val x: Int = 4 - x * x -} -``` +We pass it as an implicit `Expr[Ordering[T]]` because there might be intermediate methods that can pass it along implicitly. -To match such a term we need to match the definition and the rest of the code, but we need to explicitly state that the rest of the code may refer to this definition. +An alternative is to summon implicit values in the scope where the macro is invoked. +Using the `Expr.summon` method we get an optional expression containing the implicit instance. +This provides the ability to search for implicit instances conditionally. ```scala -case '{ val y: Int = $x; $body(y): Int } => +def summon[T: Type](using Quotes): Option[Expr[T]] ``` -Here `$x` will match any closed expression while `$body(y)` will match an expression that is closed under `y`. Then -the subexpression of type `Expr[Int]` is bound to `body` as an `Expr[Int => Int]`. The extra argument represents the references to `y`. Usually this expression is used in combination with `Expr.betaReduce` to replace the extra argument. - ```scala -inline def eval(inline e: Int): Int = ${ evalExpr('e) } - -private def evalExpr(e: Expr[Int])(using Quotes): Expr[Int] = e match - case '{ val y: Int = $x; $body(y): Int } => - // body: Expr[Int => Int] where the argument represents - // references to y - evalExpr(Expr.betaReduce('{$body(${evalExpr(x)})})) - case '{ ($x: Int) * ($y: Int) } => - (x.value, y.value) match - case (Some(a), Some(b)) => Expr(a * b) - case _ => e - case _ => e -``` +inline def setFor[T]: Set[T] = + ${ setForExpr[T] } -```scala -eval { // expands to the code: (16: Int) - val x: Int = 4 - x * x -} +def setForExpr[T: Type]()(using Quotes): Expr[Set[T]] = + Expr.summon[Ordering[T]] match + case Some(ord) => + '{ new TreeSet[T]()($ord) } + case _ => + '{ new HashSet[T] } ``` -We can also close over several bindings using `$b(a1, a2, ..., an)`. -To match an actual application we can use braces on the function part `${b}(a1, a2, ..., an)`. - ## More details [More details](./macros-spec.md) + + +[^1]: [Scalable Metaprogramming in Scala 3](https://infoscience.epfl.ch/record/299370) +[^2]: [Semantics-preserving inlining for metaprogramming](https://dl.acm.org/doi/10.1145/3426426.3428486) +[^3]: Implemented in the Scala 3 Dotty project https://github.com/lampepfl/dotty. sbt library dependency `"org.scala-lang" %% "scala3-staging" % scalaVersion.value` +[^4]: Using the `-Xcheck-macros` compiler flag diff --git a/docs/_spec/TODOreference/metaprogramming/staging.md b/docs/_spec/TODOreference/metaprogramming/staging.md index e74d491402b5..6d9166e8249e 100644 --- a/docs/_spec/TODOreference/metaprogramming/staging.md +++ b/docs/_spec/TODOreference/metaprogramming/staging.md @@ -60,7 +60,7 @@ impose the following restrictions on the use of splices. The framework as discussed so far allows code to be staged, i.e. be prepared to be executed at a later stage. To run that code, there is another method in class `Expr` called `run`. Note that `$` and `run` both map from `Expr[T]` -to `T` but only `$` is subject to the [PCP](./macros.md#the-phase-consistency-principle), whereas `run` is just a normal method. +to `T` but only `$` is subject to the [Cross-Stage Safety](./macros.md#cross-stage-safety), whereas `run` is just a normal method. `scala.quoted.staging.run` provides a `Quotes` that can be used to show the expression in its scope. On the other hand `scala.quoted.staging.withQuotes` provides a `Quotes` without evaluating the expression. From 06b5684b47f4d49ae3d67cc11ee54905a81c35b5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 6 Mar 2023 11:42:27 +0100 Subject: [PATCH 286/657] Avoid creation of `@SplicedType` quote local refrences The type declarations annotated with `@SplicedType` are only meant for types that come from outside the quote. If the reference to the `Type[T]` is to a definition within the quote (i.e. its level is grater than 0) we can use it directly. The `@SplicedType` for that type will be generated in a future compilation phase when the `Type[T]` definition reaches level 0. Fixes #17026 --- compiler/src/dotty/tools/dotc/staging/HealType.scala | 6 ++++-- tests/pos-macros/i17026.scala | 3 +++ tests/pos-macros/i17026b.scala | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/pos-macros/i17026.scala create mode 100644 tests/pos-macros/i17026b.scala diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 44843dfa91ca..22008f381c32 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -4,6 +4,7 @@ package staging import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.staging.QuoteContext.* @@ -68,13 +69,14 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. * Emits an error if `T` cannot be healed and returns `T`. */ - protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = { + protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): Type = { val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp) val tag = ctx.typer.inferImplicitArg(reqType, pos.span) tag.tpe match case tp: TermRef => ctx.typer.checkStable(tp, pos, "type witness") - getQuoteTypeTags.getTagRef(tp) + if levelOf(tp.symbol) > 0 then tp.select(tpnme.Underlying) + else getQuoteTypeTags.getTagRef(tp) case _: SearchFailureType => report.error( ctx.typer.missingArgMsg(tag, reqType, "") diff --git a/tests/pos-macros/i17026.scala b/tests/pos-macros/i17026.scala new file mode 100644 index 000000000000..d8845ef1d086 --- /dev/null +++ b/tests/pos-macros/i17026.scala @@ -0,0 +1,3 @@ +import scala.quoted.* +def macroImpl(using Quotes) = + '{ def weird[A: Type](using Quotes) = Type.of[A] } diff --git a/tests/pos-macros/i17026b.scala b/tests/pos-macros/i17026b.scala new file mode 100644 index 000000000000..98a29066462e --- /dev/null +++ b/tests/pos-macros/i17026b.scala @@ -0,0 +1,7 @@ +import scala.quoted.* + +def macroImpl(using Quotes) = + '{ + def weird[A: ToExpr: Type](a: A)(using quotes: Quotes) = + '{ Some(${ Expr(a) }) } + } From 7fed80e4c3827bd3a7f8199bf702ab508790e660 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 14 Mar 2023 16:11:07 +0100 Subject: [PATCH 287/657] Add test for wunused Inlined call --- .../tools/dotc/transform/CheckUnused.scala | 3 +-- .../fatal-warnings/i15503i.scala | 22 ++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 35153dbf66e9..5e4ed6f6e0df 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -59,7 +59,6 @@ class CheckUnused extends MiniPhase: // ========== SETUP ============ override def prepareForUnit(tree: tpd.Tree)(using Context): Context = - println(tree) val data = UnusedData() val fresh = ctx.fresh.setProperty(_key, data) fresh @@ -76,7 +75,7 @@ class CheckUnused extends MiniPhase: traverser.traverse(tree) ctx - def prepareForInlined(tree: Inlined)(using Context): Context = + override def prepareForInlined(tree: tpd.Inlined)(using Context): Context = traverser.traverse(tree.call) ctx diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 9f8416146af4..3dd4d1fc61e7 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -209,7 +209,7 @@ package foo.test.i16925: _ = println(i) // OK } yield () -package foo.test.i16679: +package foo.test.i16679a: object myPackage: trait CaseClassName[A]: def name: String @@ -225,3 +225,23 @@ package foo.test.i16679: import myPackage.CaseClassName // OK case class CoolClass(i: Int) derives CaseClassName.CaseClassByStringName println(summon[CaseClassName[CoolClass]].name) + +package foo.test.i16679b: + object myPackage: + trait CaseClassName[A]: + def name: String + + object CaseClassName: + import scala.deriving.Mirror + inline final def derived[A](using inline A: Mirror.Of[A]): CaseClassName[A] = + new CaseClassName[A]: + def name: String = A.toString + + object Foo: + given x: myPackage.CaseClassName[secondPackage.CoolClass] = null + + object secondPackage: + import myPackage.CaseClassName // OK + import Foo.x + case class CoolClass(i: Int) + println(summon[myPackage.CaseClassName[CoolClass]]) From 421591fc792480033e28714bb2ecc60c32923d9b Mon Sep 17 00:00:00 2001 From: Flavio Brasil Date: Sat, 11 Mar 2023 14:08:13 -0800 Subject: [PATCH 288/657] optimization: avoid NotGiven allocations --- library/src/scala/util/NotGiven.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/src/scala/util/NotGiven.scala b/library/src/scala/util/NotGiven.scala index 99cc903d4426..973e709042cb 100644 --- a/library/src/scala/util/NotGiven.scala +++ b/library/src/scala/util/NotGiven.scala @@ -31,11 +31,13 @@ trait LowPriorityNotGiven { } object NotGiven extends LowPriorityNotGiven { + private val cachedValue = new NotGiven[Nothing]() + /** A value of type `NotGiven` to signal a successful search for `NotGiven[C]` (i.e. a failing * search for `C`). A reference to this value will be explicitly constructed by Dotty's * implicit search algorithm */ - def value: NotGiven[Nothing] = new NotGiven[Nothing]() + def value: NotGiven[Nothing] = cachedValue /** One of two ambiguous methods used to emulate negation in Scala 2 */ given amb1[T](using ev: T): NotGiven[T] = ??? From 64709f8710b7c4b9c5155a0b325297f50f134294 Mon Sep 17 00:00:00 2001 From: Decel <8268812+Decel@users.noreply.github.com> Date: Wed, 15 Mar 2023 03:22:30 +0100 Subject: [PATCH 289/657] Add a regression test --- tests/pos/i10369.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/pos/i10369.scala diff --git a/tests/pos/i10369.scala b/tests/pos/i10369.scala new file mode 100644 index 000000000000..8689c2833664 --- /dev/null +++ b/tests/pos/i10369.scala @@ -0,0 +1,15 @@ +type Upgrade[T] = T match + case Int => Double + case Char => String + case Boolean => Boolean + +val upgrade: [t] => t => Upgrade[t] = new PolyFunction: + def apply[T](x: T): Upgrade[T] = x match + case x: Int => x.toDouble + case x: Char => x.toString + case x: Boolean => !x + +val upgrade2: [t] => t => Upgrade[t] = [t] => (x: t) => x match + case x: Int => x.toDouble + case x: Char => x.toString + case x: Boolean => !x From 2d1ef6386a18f26735e0057a1216c67e7858962c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 15 Mar 2023 13:51:35 +0100 Subject: [PATCH 290/657] Fix references to class members defined in quotes If an inner quote selects a symbol that is defined in an outer quote we need to transform it or reject it. The issue is that the inner quote cannot contain a reference to the type of the class defined in the outer quote. Any such reference is erased the parents of that class that are statically know outside those quotes. If the selected symbol is overriding a symbol in one of those statically known classes, we can use that overridden symbol instead. If not we have to reject the code. Fixes #17103 --- .../dotty/tools/dotc/transform/Splicing.scala | 8 +++++++ tests/neg-macros/i17103.scala | 16 ++++++++++++++ tests/pos-macros/i17103a.scala | 21 +++++++++++++++++++ tests/pos-macros/i17103b.scala | 21 +++++++++++++++++++ tests/pos-macros/i7405b.scala | 6 +++++- 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 tests/neg-macros/i17103.scala create mode 100644 tests/pos-macros/i17103a.scala create mode 100644 tests/pos-macros/i17103b.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 7ecde9400445..47b65ba767bb 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -208,6 +208,14 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match + case tree: Select if tree.isTerm && isCaptured(tree.symbol) => + tree.symbol.allOverriddenSymbols.find(sym => !isCaptured(sym.owner)) match + case Some(sym) => + // virtualize call on overridden symbol that is not defined in a non static class + transform(tree.qualifier.select(sym)) + case _ => + report.error(em"Can not use reference to staged local ${tree.symbol} defined in an outer quote.\n\nThis can work if ${tree.symbol.owner} would extend a top level interface that defines ${tree.symbol}.", tree) + tree case tree: RefTree => if tree.isTerm then if isCaptured(tree.symbol) then diff --git a/tests/neg-macros/i17103.scala b/tests/neg-macros/i17103.scala new file mode 100644 index 000000000000..bd4b41d8b559 --- /dev/null +++ b/tests/neg-macros/i17103.scala @@ -0,0 +1,16 @@ +import scala.quoted.* + +def test(using Quotes): Expr[Unit] = + '{ + trait C: + def d: Int + val c: C = ??? + ${ + val expr = '{ + val cRef: c.type = ??? + cRef.d // error + () + } + expr + } + } \ No newline at end of file diff --git a/tests/pos-macros/i17103a.scala b/tests/pos-macros/i17103a.scala new file mode 100644 index 000000000000..ffd0c15f28b2 --- /dev/null +++ b/tests/pos-macros/i17103a.scala @@ -0,0 +1,21 @@ +import scala.quoted.* + +trait C0: + def d: Int + +def test(using Quotes): Expr[Unit] = + '{ + trait C1 extends C0: + def d: Int + trait C extends C1: + def d: Int + val c: C = ??? + ${ + val expr = '{ + val cRef: C = ??? + cRef.d // calls C0.d + () + } + expr + } + } diff --git a/tests/pos-macros/i17103b.scala b/tests/pos-macros/i17103b.scala new file mode 100644 index 000000000000..0fbe86f0cf73 --- /dev/null +++ b/tests/pos-macros/i17103b.scala @@ -0,0 +1,21 @@ +import scala.quoted.* + +trait C0: + def d: Int + +def test(using Quotes): Expr[Unit] = + '{ + trait C1 extends C0: + def d: Int + trait C extends C1: + def d: Int + val c: C = ??? + ${ + val expr = '{ + val cRef: c.type = ??? + cRef.d // calls C0.d + () + } + expr + } + } diff --git a/tests/pos-macros/i7405b.scala b/tests/pos-macros/i7405b.scala index df7218608e88..6c73c275e15f 100644 --- a/tests/pos-macros/i7405b.scala +++ b/tests/pos-macros/i7405b.scala @@ -3,7 +3,7 @@ import scala.quoted.* class Foo { def f(using Quotes): Expr[Any] = { '{ - trait X { + trait X extends A { type Y def y: Y = ??? } @@ -17,3 +17,7 @@ class Foo { } } } + +trait A: + type Y + def y: Y = ??? From 60b6ff04d0d25415840a9f1de88ea8518f0973a3 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 15 Mar 2023 11:35:57 +0100 Subject: [PATCH 291/657] Allow selectDynamic and applyDynamic to be an extension methods Allow selectDynamic and applyDynamic to be an extension methods when dispatching structurally. Fixes #17100 --- compiler/src/dotty/tools/dotc/typer/Dynamic.scala | 8 ++++---- tests/pos/i17100.scala | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 tests/pos/i17100.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 8ec4e9416151..1513872cf47a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -186,7 +186,7 @@ trait Dynamic { // ($qual: Selectable).$selectorName("$name") val base = untpd.Apply( - untpd.TypedSplice(selectable.select(selectorName)).withSpan(fun.span), + untpd.Select(untpd.TypedSplice(selectable), selectorName).withSpan(fun.span), (Literal(Constant(name.encode.toString)) :: Nil).map(untpd.TypedSplice(_))) val scall = @@ -219,19 +219,19 @@ trait Dynamic { extension (tree: Tree) /** The implementations of `selectDynamic` and `applyDynamic` in `scala.reflect.SelectDynamic` have no information about the expected return type of a value/method which was declared in the refinement, * only the JVM type after erasure can be obtained through reflection, e.g. - * + * * class Foo(val i: Int) extends AnyVal * class Reflective extends reflect.Selectable * val reflective = new Reflective { * def foo = Foo(1) // Foo at compile time, java.lang.Integer in reflection * } - * + * * Because of that reflective access cannot be implemented properly in `scala.reflect.SelectDynamic` itself * because it's not known there if the value should be wrapped in a value class constructor call or not. * Hence the logic of wrapping is performed here, relying on the fact that the implementations of `selectDynamic` and `applyDynamic` in `scala.reflect.SelectDynamic` are final. */ def maybeBoxingCast(tpe: Type) = - val maybeBoxed = + val maybeBoxed = if ValueClasses.isDerivedValueClass(tpe.classSymbol) && qual.tpe <:< defn.ReflectSelectableTypeRef then val genericUnderlying = ValueClasses.valueClassUnbox(tpe.classSymbol.asClass) val underlying = tpe.select(genericUnderlying).widen.resultType diff --git a/tests/pos/i17100.scala b/tests/pos/i17100.scala new file mode 100644 index 000000000000..2fd01bcbe5ff --- /dev/null +++ b/tests/pos/i17100.scala @@ -0,0 +1,10 @@ +trait Sel extends Selectable + +extension (s: Sel) + def selectDynamic(name: String) = ??? + def applyDynamic(name: String)(x: Int) = ??? + +val sel = (new Sel {}).asInstanceOf[Sel{ def foo: String; def bar(x: Int): Int }] +val foo = sel.selectDynamic("foo") +val foo2 = sel.foo +val foo3 = sel.bar(2) From a3242e82b20d9df8b8d48ba9e50b96f93009e5cb Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 15 Mar 2023 13:04:55 +0100 Subject: [PATCH 292/657] Update semanticDB expect files --- tests/pos/i17100.scala | 6 +++++- tests/semanticdb/expect/Advanced.expect.scala | 8 ++++---- tests/semanticdb/metac.expect | 6 +++++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/pos/i17100.scala b/tests/pos/i17100.scala index 2fd01bcbe5ff..1858e0383f8d 100644 --- a/tests/pos/i17100.scala +++ b/tests/pos/i17100.scala @@ -3,8 +3,12 @@ trait Sel extends Selectable extension (s: Sel) def selectDynamic(name: String) = ??? def applyDynamic(name: String)(x: Int) = ??? + def applyDynamic(name: String)() = ??? -val sel = (new Sel {}).asInstanceOf[Sel{ def foo: String; def bar(x: Int): Int }] +val sel = (new Sel {}).asInstanceOf[Sel{ def foo: String; def bar(x: Int): Int; def baz(): Int }] val foo = sel.selectDynamic("foo") val foo2 = sel.foo val foo3 = sel.bar(2) +val foo4 = sel.baz() + + diff --git a/tests/semanticdb/expect/Advanced.expect.scala b/tests/semanticdb/expect/Advanced.expect.scala index c701821a09b8..d36fcd611eef 100644 --- a/tests/semanticdb/expect/Advanced.expect.scala +++ b/tests/semanticdb/expect/Advanced.expect.scala @@ -25,11 +25,11 @@ class Wildcards/*<-advanced::Wildcards#*/ { object Test/*<-advanced::Test.*/ { val s/*<-advanced::Test.s.*/ = new Structural/*->advanced::Structural#*/ val s1/*<-advanced::Test.s1.*/ = s/*->advanced::Test.s.*/.s1/*->advanced::Structural#s1().*/ - val s1x/*<-advanced::Test.s1x.*/ = s/*->advanced::Test.s.*/.s1/*->advanced::Structural#s1().*/.x + val s1x/*<-advanced::Test.s1x.*/ = s/*->advanced::Test.s.*/.s1/*->advanced::Structural#s1().*/.x/*->scala::reflect::Selectable#selectDynamic().*/ val s2/*<-advanced::Test.s2.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/ - val s2x/*<-advanced::Test.s2x.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/.x + val s2x/*<-advanced::Test.s2x.*/ = s/*->advanced::Test.s.*/.s2/*->advanced::Structural#s2().*/.x/*->scala::reflect::Selectable#selectDynamic().*/ val s3/*<-advanced::Test.s3.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/ - val s3x/*<-advanced::Test.s3x.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/.m(???/*->scala::Predef.`???`().*/) + val s3x/*<-advanced::Test.s3x.*/ = s/*->advanced::Test.s.*/.s3/*->advanced::Structural#s3().*/.m/*->scala::reflect::Selectable#applyDynamic().*/(???/*->scala::Predef.`???`().*/) val e/*<-advanced::Test.e.*/ = new Wildcards/*->advanced::Wildcards#*/ val e1/*<-advanced::Test.e1.*/ = e/*->advanced::Test.e.*/.e1/*->advanced::Wildcards#e1().*/ @@ -45,7 +45,7 @@ object Test/*<-advanced::Test.*/ { // see: https://github.com/lampepfl/dotty/pull/14608#discussion_r835642563 lazy val foo/*<-advanced::Test.foo.*/: (reflect.Selectable/*->scala::reflect::Selectable#*/ { type A/*<-local16*/ = Int/*->scala::Int#*/ }) &/*->scala::`&`#*/ (reflect.Selectable/*->scala::reflect::Selectable#*/ { type A/*<-local17*/ = Int/*->scala::Int#*/; val a/*<-local18*/: A/*->local17*/ }) = ???/*->scala::Predef.`???`().*/ - def bar/*<-advanced::Test.bar().*/: foo/*->advanced::Test.foo.*/.A/*->local17*/ = foo/*->advanced::Test.foo.*/.a + def bar/*<-advanced::Test.bar().*/: foo/*->advanced::Test.foo.*/.A/*->local17*/ = foo/*->advanced::Test.foo.*/.a/*->scala::reflect::Selectable#selectDynamic().*/ } diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index c78d30e00863..f5556e28bd1b 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -49,7 +49,7 @@ Uri => Advanced.scala Text => empty Language => Scala Symbols => 60 entries -Occurrences => 134 entries +Occurrences => 138 entries Synthetics => 3 entries Symbols: @@ -187,18 +187,21 @@ Occurrences: [27:6..27:9): s1x <- advanced/Test.s1x. [27:12..27:13): s -> advanced/Test.s. [27:14..27:16): s1 -> advanced/Structural#s1(). +[27:16..27:18): .x -> scala/reflect/Selectable#selectDynamic(). [28:6..28:8): s2 <- advanced/Test.s2. [28:11..28:12): s -> advanced/Test.s. [28:13..28:15): s2 -> advanced/Structural#s2(). [29:6..29:9): s2x <- advanced/Test.s2x. [29:12..29:13): s -> advanced/Test.s. [29:14..29:16): s2 -> advanced/Structural#s2(). +[29:16..29:18): .x -> scala/reflect/Selectable#selectDynamic(). [30:6..30:8): s3 <- advanced/Test.s3. [30:11..30:12): s -> advanced/Test.s. [30:13..30:15): s3 -> advanced/Structural#s3(). [31:6..31:9): s3x <- advanced/Test.s3x. [31:12..31:13): s -> advanced/Test.s. [31:14..31:16): s3 -> advanced/Structural#s3(). +[31:16..31:18): .m -> scala/reflect/Selectable#applyDynamic(). [31:19..31:22): ??? -> scala/Predef.`???`(). [33:6..33:7): e <- advanced/Test.e. [33:14..33:23): Wildcards -> advanced/Wildcards# @@ -233,6 +236,7 @@ Occurrences: [47:11..47:14): foo -> advanced/Test.foo. [47:15..47:16): A -> local17 [47:19..47:22): foo -> advanced/Test.foo. +[47:22..47:24): .a -> scala/reflect/Selectable#selectDynamic(). [52:6..52:13): HKClass <- advanced/HKClass# [52:14..52:15): F <- advanced/HKClass#[F] [52:20..52:21): T <- advanced/HKClass#``().[F][T] From fa29ba5c03da550123299611aa7f4c32028472ce Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 15 Mar 2023 13:41:31 +0100 Subject: [PATCH 293/657] Also allow structural dispatch on Dynamic Given ```scala trait Sel extends Dynamic extension (s: Sel) def selectDynamic(name: String) = ??? ``` the following worked: ```scala val sel = new Sel {} val foo = sel.foo ``` but the following didn't: ```scala val sel2 = (new Sel {}).asInstanceOf[Sel{ def foo: String }] val foo2 = sel2.foo ``` The problem was that we recognized a structural dispatch and then required the qualifier to be an instance of `Selectable`. But in fact, `Dynamic` works just as well, and the mechanism is the same. It's just that `Dynamic` is less type safe then `Selectable`. --- compiler/src/dotty/tools/dotc/typer/Dynamic.scala | 2 +- tests/pos/i17100a.scala | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i17100a.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 1513872cf47a..717966923708 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -181,7 +181,7 @@ trait Dynamic { val vargss = termArgss(tree) def structuralCall(selectorName: TermName, classOfs: => List[Tree]) = { - val selectable = adapt(qual, defn.SelectableClass.typeRef) + val selectable = adapt(qual, defn.SelectableClass.typeRef | defn.DynamicClass.typeRef) // ($qual: Selectable).$selectorName("$name") val base = diff --git a/tests/pos/i17100a.scala b/tests/pos/i17100a.scala new file mode 100644 index 000000000000..abf74c80a4f5 --- /dev/null +++ b/tests/pos/i17100a.scala @@ -0,0 +1,12 @@ + +import scala.language.dynamics +trait Sel extends Dynamic + +extension (s: Sel) + def selectDynamic(name: String) = ??? + +val sel = new Sel {} +val foo = sel.foo +val sel2 = (new Sel {}).asInstanceOf[Sel{ def foo: String }] +val foo2 = sel2.foo + From 5fb69be4eaac058d1d19fb72eb79e04bb8fe7d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 15 Mar 2023 16:38:00 +0100 Subject: [PATCH 294/657] Fix #17042: Preserve the shape of secondary ctors in instrumentCoverage. --- .../dotc/transform/InstrumentCoverage.scala | 30 +++- tests/coverage/pos/Constructor.scala | 10 ++ .../coverage/pos/Constructor.scoverage.check | 145 ++++++++++++++---- 3 files changed, 154 insertions(+), 31 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala b/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala index c69b342b9a01..29572a4ae30d 100644 --- a/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala +++ b/compiler/src/dotty/tools/dotc/transform/InstrumentCoverage.scala @@ -11,6 +11,7 @@ import core.DenotTransformers.IdentityDenotTransformer import core.Symbols.{defn, Symbol} import core.Constants.Constant import core.NameOps.isContextFunction +import core.StdNames.nme import core.Types.* import coverage.* import typer.LiftCoverage @@ -325,7 +326,11 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: // Only transform the params (for the default values) and the rhs, not the name and tpt. val transformedParamss = transformParamss(tree.paramss) val transformedRhs = - if !sym.isOneOf(Accessor | Artifact | Synthetic) && !tree.rhs.isEmpty then + if tree.rhs.isEmpty then + tree.rhs + else if sym.isClassConstructor then + instrumentSecondaryCtor(tree) + else if !sym.isOneOf(Accessor | Artifact | Synthetic) then // If the body can be instrumented, do it (i.e. insert a "coverage call" at the beginning) // This is useful because methods can be stored and called later, or called by reflection, // and if the rhs is too simple to be instrumented (like `def f = this`), @@ -410,6 +415,24 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: val coverageCall = createInvokeCall(parent, pos) InstrumentedParts.singleExprTree(coverageCall, body) + /** Instruments the body of a secondary constructor DefDef. + * + * We must preserve the delegate constructor call as the first statement of + * the rhs Block, otherwise `HoistSuperArgs` will not be happy (see #17042). + */ + private def instrumentSecondaryCtor(ctorDef: DefDef)(using Context): Tree = + // compute position like in instrumentBody + val namePos = ctorDef.namePos + val pos = namePos.withSpan(namePos.span.withStart(ctorDef.span.start)) + val coverageCall = createInvokeCall(ctorDef, pos) + + ctorDef.rhs match + case b @ Block(delegateCtorCall :: stats, expr: Literal) => + cpy.Block(b)(transform(delegateCtorCall) :: coverageCall :: stats.mapConserve(transform), expr) + case rhs => + cpy.Block(rhs)(transform(rhs) :: coverageCall :: Nil, unitLiteral) + end instrumentSecondaryCtor + /** * Checks if the apply needs a lift in the coverage phase. * In case of a nested application, we have to lift all arguments @@ -447,9 +470,14 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer: /** Check if an Apply can be instrumented. Prevents this phase from generating incorrect code. */ private def canInstrumentApply(tree: Apply)(using Context): Boolean = + def isSecondaryCtorDelegateCall: Boolean = tree.fun match + case Select(This(_), nme.CONSTRUCTOR) => true + case _ => false + val sym = tree.symbol !sym.isOneOf(ExcludeMethodFlags) && !isCompilerIntrinsicMethod(sym) + && !(sym.isClassConstructor && isSecondaryCtorDelegateCall) && (tree.typeOpt match case AppliedType(tycon: NamedType, _) => /* If the last expression in a block is a context function, we'll try to diff --git a/tests/coverage/pos/Constructor.scala b/tests/coverage/pos/Constructor.scala index 251370ec8e6e..536bfa26f386 100644 --- a/tests/coverage/pos/Constructor.scala +++ b/tests/coverage/pos/Constructor.scala @@ -1,10 +1,20 @@ package covtest class C: + def this(arg: String) = { + this() + g() + } + + def this(x: Int) = + this(x.toString() + "foo") + def f(x: Int) = x def x = 1 f(x) + def g(): Int = 2 + object O: def g(y: Int) = y def y = 1 diff --git a/tests/coverage/pos/Constructor.scoverage.check b/tests/coverage/pos/Constructor.scoverage.check index 678da472fd4c..6a6742c9118d 100644 --- a/tests/coverage/pos/Constructor.scoverage.check +++ b/tests/coverage/pos/Constructor.scoverage.check @@ -24,10 +24,78 @@ covtest C Class covtest.C -f + 28 -33 +36 3 + +DefDef +false +0 +false +def this + +1 +Constructor.scala +covtest +C +Class +covtest.C + +69 +72 +5 +g +Apply +false +0 +false +g() + +2 +Constructor.scala +covtest +C +Class +covtest.C + +80 +88 +8 + +DefDef +false +0 +false +def this + +3 +Constructor.scala +covtest +C +Class +covtest.C + +108 +128 +9 ++ +Apply +false +0 +false +x.toString() + "foo" + +4 +Constructor.scala +covtest +C +Class +covtest.C +f +133 +138 +11 f DefDef false @@ -35,16 +103,16 @@ false false def f -1 +5 Constructor.scala covtest C Class covtest.C x -48 -53 -4 +153 +158 +12 x DefDef false @@ -52,16 +120,16 @@ false false def x -2 +6 Constructor.scala covtest C Class covtest.C -60 -64 -5 +165 +169 +13 f Apply false @@ -69,16 +137,16 @@ false false f(x) -3 +7 Constructor.scala covtest C Class covtest.C -62 -63 -5 +167 +168 +13 x Select false @@ -86,16 +154,33 @@ false false x -4 +8 +Constructor.scala +covtest +C +Class +covtest.C +g +173 +178 +15 +g +DefDef +false +0 +false +def g + +9 Constructor.scala covtest O$ Object covtest.O$ g -78 -83 -8 +203 +208 +18 g DefDef false @@ -103,16 +188,16 @@ false false def g -5 +10 Constructor.scala covtest O$ Object covtest.O$ y -98 -103 -9 +223 +228 +19 y DefDef false @@ -120,16 +205,16 @@ false false def y -6 +11 Constructor.scala covtest O$ Object covtest.O$ -110 -114 -10 +235 +239 +20 g Apply false @@ -137,16 +222,16 @@ false false g(y) -7 +12 Constructor.scala covtest O$ Object covtest.O$ -112 -113 -10 +237 +238 +20 y Ident false From 9afa629bc844340527361473e0326a64d9e6a5da Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 8 Mar 2023 18:40:14 +0000 Subject: [PATCH 295/657] AppliedTypes returned by baseType shouldn't have type lambdas as constructors In tests/pos/argDenot-alpakka.min.scala, we want `(([O] =>> Foo[O, S]) @uV)[Int]`.baseType(`Foo`) to return `Foo[Int]` rather than an applied type lambda, so that we can rely on the invariant that the type arguments of the type returned by baseType(cls) correspond to the type parameter of cls which `NamedType#argDenot` is implicitly relying on. This could be achieved by removing the initial if branch from the AppliedType case in baseTypeOf, since the recursive fallback will always work, but it makes sense to keep a special case for performance, so we just explicitly add as a condition to the fast-path that the type constructor of the AppliedType can't be a lambda. --- .../tools/dotc/core/SymDenotations.scala | 5 ++--- tests/pos/argDenot-alpakka.min.scala | 9 ++++++++ tests/pos/argDenot-alpakka.scala | 21 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 tests/pos/argDenot-alpakka.min.scala create mode 100644 tests/pos/argDenot-alpakka.scala diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 0f94d52f5be9..beeaa2ee922e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -2226,13 +2226,12 @@ object SymDenotations { def computeApplied = { btrCache(tp) = NoPrefix val baseTp = - if (tycon.typeSymbol eq symbol) tp - else (tycon.typeParams: @unchecked) match { + if (tycon.typeSymbol eq symbol) && !tycon.isLambdaSub then tp + else (tycon.typeParams: @unchecked) match case LambdaParam(_, _) :: _ => recur(tp.superType) case tparams: List[Symbol @unchecked] => recur(tycon).substApprox(tparams, args) - } record(tp, baseTp) baseTp } diff --git a/tests/pos/argDenot-alpakka.min.scala b/tests/pos/argDenot-alpakka.min.scala new file mode 100644 index 000000000000..0e509be59cfd --- /dev/null +++ b/tests/pos/argDenot-alpakka.min.scala @@ -0,0 +1,9 @@ +import scala.annotation.unchecked.uncheckedVariance as uV + +trait Test: + def test[S] = + val a: (([O] =>> Foo[O, S]) @uV)[Int] = ??? + a.m() + +class Foo[X, Y]: + def m(): Y = ??? diff --git a/tests/pos/argDenot-alpakka.scala b/tests/pos/argDenot-alpakka.scala new file mode 100644 index 000000000000..41d6ad52ac97 --- /dev/null +++ b/tests/pos/argDenot-alpakka.scala @@ -0,0 +1,21 @@ +import scala.annotation.unchecked.uncheckedVariance as uV + +trait Test: + def split[I, M](in: Flow[I, Byte, M]): SubFlow[Byte, M, in.Repr] + def test = + split(new Flow[Int, Byte, Unit]) + .via[Char] + .merge + .filter() + +trait FlowOps[+Out, +Mat]: + type Repr[+O] <: FlowOps[O, Mat] { type Repr[+O] = FlowOps.this.Repr[O] } + def via[O]: Repr[O] = ??? + def filter(): Repr[Out] = ??? + +class Flow[-In, +Out, +Mat] extends FlowOps[Out, Mat]: + type Repr[+O] = Flow[In @uV, O, Mat @uV] + +class SubFlow[+Out, +Mat, +F[+_]] extends FlowOps[Out, Mat]: + type Repr[+O] = SubFlow[O, Mat @uV, F @uV] + def merge: F[Out] = ??? From 22b4a549e086723f86d52649b893f9613cb2f50e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Mar 2023 13:35:45 +0100 Subject: [PATCH 296/657] Fix documentation of HOLE in TastyFormat --- tasty/src/dotty/tools/tasty/TastyFormat.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 23b74ed1d7c7..226fc14acb39 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -122,7 +122,8 @@ Standard-Section: "ASTs" TopLevelStat* MATCHtpt Length bound_Term? sel_Term CaseDef* -- sel match { CaseDef } where `bound` is optional upper bound of all rhs BYNAMEtpt underlying_Term -- => underlying SHAREDterm term_ASTRef -- Link to previously serialized term - HOLE Length idx_Nat arg_Tree* -- Hole where a splice goes with sequence number idx, splice is applied to arguments `arg`s + HOLE Length idx_Nat tpe_Type arg_Tree* -- Splice hole with index `idx`, the type of the hole `tpe`, type and term arguments of the hole `arg`s + CaseDef = CASEDEF Length pat_Term rhs_Tree guard_Tree? -- case pat if guard => rhs ImplicitArg = IMPLICITARG arg_Term -- implicit unapply argument From b223c20e358dacf47bbf63857aafac935c26d275 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Mar 2023 13:50:39 +0100 Subject: [PATCH 297/657] Improve implicit parameter error message with aliases Fixes #17122 --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 2 +- tests/neg/i17122.check | 5 +++++ tests/neg/i17122.scala | 7 +++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i17122.check create mode 100644 tests/neg/i17122.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c37da9fcf701..a9631ad45e28 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -969,7 +969,7 @@ trait Implicits: case Select(qual, nme.apply) if defn.isFunctionType(qual.tpe.widen) => val qt = qual.tpe.widen val qt1 = qt.dealiasKeepAnnots - def addendum = if (qt1 eq qt) "" else (i"\nThe required type is an alias of: $qt1") + def addendum = if (qt1 eq qt) "" else (i"\nWhere $qt is an alias of: $qt1") i"parameter of ${qual.tpe.widen}$addendum" case _ => i"${ if paramName.is(EvidenceParamName) then "an implicit parameter" diff --git a/tests/neg/i17122.check b/tests/neg/i17122.check new file mode 100644 index 000000000000..683908c5af0f --- /dev/null +++ b/tests/neg/i17122.check @@ -0,0 +1,5 @@ +-- [E172] Type Error: tests/neg/i17122.scala:7:14 ---------------------------------------------------------------------- +7 |def test = m() // error + | ^ + | No given instance of type A was found for parameter of C + | Where C is an alias of: (A) ?=> B diff --git a/tests/neg/i17122.scala b/tests/neg/i17122.scala new file mode 100644 index 000000000000..fcf9af106488 --- /dev/null +++ b/tests/neg/i17122.scala @@ -0,0 +1,7 @@ +case class A() +case class B() + +type C = A ?=> B +def m(): C = ??? + +def test = m() // error From 44fa111a3a7067d63674c0c0a80e83473dceee9c Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Fri, 17 Mar 2023 14:59:39 +0100 Subject: [PATCH 298/657] Check the status of coursier download in CoursierScalaTests.scala This should provide more insight on why #17119 happens. --- .../tools/coursier/CoursierScalaTests.scala | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala index 979fea0684b2..944bf1957d43 100644 --- a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala +++ b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala @@ -148,11 +148,11 @@ class CoursierScalaTests: object CoursierScalaTests: - def execCmd(command: String, options: String*): List[String] = + def execCmd(command: String, options: String*): (Int, List[String]) = val cmd = (command :: options.toList).toSeq.mkString(" ") val out = new ListBuffer[String] - cmd.!(ProcessLogger(out += _, out += _)) - out.toList + val code = cmd.!(ProcessLogger(out += _, out += _)) + (code, out.toList) def csScalaCmd(options: String*): List[String] = csCmd("dotty.tools.MainGenericRunner", options*) @@ -166,10 +166,16 @@ object CoursierScalaTests: case Nil => args case _ => "--" +: args val newJOpts = jOpts.map(s => s"--java-opt ${s.stripPrefix("-J")}").mkString(" ") - execCmd("./cs", (s"""launch "org.scala-lang:scala3-compiler_3:${sys.env("DOTTY_BOOTSTRAPPED_VERSION")}" $newJOpts --main-class "$entry" --property "scala.usejavacp=true"""" +: newOptions)*) + execCmd("./cs", (s"""launch "org.scala-lang:scala3-compiler_3:${sys.env("DOTTY_BOOTSTRAPPED_VERSION")}" $newJOpts --main-class "$entry" --property "scala.usejavacp=true"""" +: newOptions)*)._2 /** Get coursier script */ @BeforeClass def setup(): Unit = - val ver = execCmd("uname").head.replace('L', 'l').replace('D', 'd') - execCmd("curl", s"-fLo cs https://git.io/coursier-cli-$ver") #&& execCmd("chmod", "+x cs") + val ver = execCmd("uname")._2.head.replace('L', 'l').replace('D', 'd') + def runAndCheckCmd(cmd: String, options: String*): Unit = + val (code, out) = execCmd(cmd, options*) + if code != 0 then + fail(s"Failed to run $cmd ${options.mkString(" ")}, exit code: $code, output: ${out.mkString("\n")}") + + runAndCheckCmd("curl", s"-fLo cs https://git.io/coursier-cli-$ver") + runAndCheckCmd("chmod", "+x cs") From 00790452c5217b36aaa42b2885abd9652befbea0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Mar 2023 15:07:27 +0100 Subject: [PATCH 299/657] =?UTF-8?q?Avoid=20timeouts=20in=20community?= =?UTF-8?q?=E2=80=93build-C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Increased timeout due to timeouts when running on dotty community build --- community-build/community-projects/requests-scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/requests-scala b/community-build/community-projects/requests-scala index 6d4a223bc33d..2d7316a8f444 160000 --- a/community-build/community-projects/requests-scala +++ b/community-build/community-projects/requests-scala @@ -1 +1 @@ -Subproject commit 6d4a223bc33def14ae9a4def24a3f5c258451e8e +Subproject commit 2d7316a8f444c2f38795e5905f90837e651c79c3 From 1dc02d154cc9169d2743889e994f0fb075b37ac8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Mar 2023 15:22:06 +0100 Subject: [PATCH 300/657] Apply suggestions from code review Co-authored-by: anna herlihy --- .../reference/metaprogramming/macros-spec.md | 16 ++++++++-------- docs/_docs/reference/metaprogramming/macros.md | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/_docs/reference/metaprogramming/macros-spec.md b/docs/_docs/reference/metaprogramming/macros-spec.md index 6045354fdbbc..35a1b4b3d43a 100644 --- a/docs/_docs/reference/metaprogramming/macros-spec.md +++ b/docs/_docs/reference/metaprogramming/macros-spec.md @@ -24,7 +24,7 @@ ${ hello('name) } ${ hello('{name}) } ### Quotes Quotes come in four flavors: quoted identifiers, quoted blocks, quoted block patterns and quoted type patterns. -Scala 2 used quoted identifiers to represent `Symbol` literals. They were deprecated in Scala 3, allowing to use them for quotation. +Scala 2 used quoted identifiers to represent `Symbol` literals. They were deprecated in Scala 3, allowing the syntax to be used for quotation. ```scala SimpleExpr ::= ... | `'` alphaid // quoted identifier @@ -42,7 +42,7 @@ Lastly, the quoted type pattern simply contains a type. ### Splices Splices come in three flavors: spliced identifiers, spliced blocks and splice patterns. Scala specifies identifiers containing `$` as valid identifiers but reserves them for compiler and standard library use only. -Unfortunately, many libraries have used such identifiers in Scala~2. Therefore to mitigate the cost of migration, we still support them. +Unfortunately, many libraries have used such identifiers in Scala 2. Therefore to mitigate the cost of migration, we still support them. We work around this by only allowing spliced identifiers[^3] within quoted blocks or quoted block patterns (`inQuoteBlock`). Splice blocks and splice patterns can contain an arbitrary block or pattern respectively. They are distinguished based on their surrounding quote (`inQuotePattern`), a quote block will contain spliced blocks, and a quote block pattern will contain splice patterns. @@ -208,7 +208,7 @@ import scala.quoted.staging.* given Compiler = Compiler.make(getClass.getClassLoader) ``` -The classloader is needed for the compiler to know which dependencies have been loaded and to load the generated code using the same classloader. +The classloader is needed for the compiler to know which dependencies have been loaded and to load the generated code using the same classloader. Below is an example method `mkPower2` that is passed to `staging.run`: ```scala def mkPower2()(using Quotes): Expr[Double => Double] = ... @@ -229,7 +229,7 @@ To do this, the resulting `RunInstance` class is loaded in the JVM using Java Re Quotes and splices are primitive forms in the generated typed abstract syntax trees. These need to be type-checked with some extra rules, e.g., staging levels need to be checked and the references to generic types need to be adapted. -Finally, quoted expressions that will be generated at run-time need to be encoded (serialized) and decoded (deserialized). +Finally, quoted expressions that will be generated at run-time need to be encoded (serialized/pickled) and decoded (deserialized/unpickled). #### Typing Quoted Expressions @@ -473,7 +473,7 @@ In general, the splice normal form has the shape `${ .apply(*) }` ##### Function references normalization A reference to a function `f` that receives parameters is not a valid value in Scala. -Such a function reference `f` can be eta-expaned as `x => f(x)` to be used as a lambda value. +Such a function reference `f` can be eta-expanded as `x => f(x)` to be used as a lambda value. Therefore function references cannot be transformed by the normalization as directly as other expressions as we cannot represent `'{f}` with a method reference type. We can use the eta-expanded form of `f` in the normalized form. For example, consider the reference to `f` below. @@ -626,7 +626,7 @@ With these transformations, the contents of the quote or `Type.of` are guarantee The AST is pickled into TASTy, which is a sequence of bytes. This sequence of bytes needs to be instantiated in the bytecode, but unfortunately it cannot be dumped into the classfile as bytes. To reify it we encode the bytes into a Java `String`. -In the following examples we display this encoding in human readable form with the fictitious |tasty"..."| string literal. +In the following examples we display this encoding in human readable form with the fictitious `|tasty"..."|` string literal. ```scala // pickled AST bytes encoded in a base64 string @@ -697,8 +697,8 @@ As the type holes are at the start of the quote, they will have the first `N` in This implies that we can place the references in a sequence `Seq(t, u, ...)` where the index in the sequence is the same as the hole index. Lastly, the quote itself is replaced by a call to `QuoteUnpickler.unpickleExpr` which will unpickle the AST, evaluate the holes, i.e., splices, and wrap the resulting AST in an `Expr[Int]`. -This method takes takes the pickled |tasty"..."|, the types and the hole lambda. -Similarly, `Type.of` is replaced with a call to `QuoteUnpickler.unpickleType` but only receives the pickled |tasty"..."| and the types. +This method takes takes the pickled `|tasty"..."|`, the types and the hole lambda. +Similarly, `Type.of` is replaced with a call to `QuoteUnpickler.unpickleType` but only receives the pickled `|tasty"..."|` and the types. Because `QuoteUnpickler` is part of the self-type of the `Quotes` class, we have to cast the instance but know that this cast will always succeed. ```scala diff --git a/docs/_docs/reference/metaprogramming/macros.md b/docs/_docs/reference/metaprogramming/macros.md index 1233d0007bc5..a91e69d985f0 100644 --- a/docs/_docs/reference/metaprogramming/macros.md +++ b/docs/_docs/reference/metaprogramming/macros.md @@ -254,10 +254,10 @@ Therefore, while evaluating the quote, it is not possible to accidentally rebind #### Well-typed If a quote is well typed, then the generated code is well typed. This is a simple consequence of tracking the type of each expression. -An `Expr[T]` can only be created from a quote that contains an expression of type `T. +An `Expr[T]` can only be created from a quote that contains an expression of type `T`. Conversely, an `Expr[T]` can only be spliced in a location that expects a type `T. As mentioned before, `Expr` is covariant in its type parameter. -This means that an `Expr[T]` can contain an expression of a subtype of `T. +This means that an `Expr[T]` can contain an expression of a subtype of `T`. When spliced in a location that expects a type `T, these expressions also have a valid type. ### Cross-Stage Safety @@ -495,7 +495,7 @@ At run-time, when the pattern matches, the type of `t` and `u` will be known, an As `Expr` is covariant, the statically known type of the expression might not be the actual type. Type variables can also be used to recover the precise type of the expression. -``scala +```scala def let(x: Expr[Any])(using Quotes): Expr[Any] = x match case '{ $x: t } => From a8e150fad8d93f8703dd00ff8157735e7844b43d Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 17 Mar 2023 16:24:21 +0100 Subject: [PATCH 301/657] Scaladoc: fix inkuire generation for PolyTypes --- .../src/dotty/tools/scaladoc/tasty/InkuireSupport.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala index 0cdb3535c3ff..8a703cfb5d24 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/InkuireSupport.scala @@ -317,6 +317,8 @@ trait InkuireSupport(using DocContext) extends Resources: inner(tpe, vars) case tl @ TypeLambda(paramNames, _, resType) => Inkuire.TypeLambda(paramNames.map(Inkuire.TypeLambda.argument), inner(resType, vars)) //TODO [Inkuire] Type bounds + case pt @ PolyType(paramNames, _, resType) => + Inkuire.TypeLambda(paramNames.map(Inkuire.TypeLambda.argument), inner(resType, vars)) //TODO [Inkuire] Type bounds case r: Refinement => inner(r.info, vars) //TODO [Inkuire] Refinements case t @ AppliedType(tpe, typeList) => @@ -357,10 +359,8 @@ trait InkuireSupport(using DocContext) extends Resources: Inkuire.Type.unresolved //TODO [Inkuire] <- should be handled by Singleton case, but didn't work case MatchType(bond, sc, cases) => inner(sc, vars) - case ParamRef(TypeLambda(names, _, _), i) => - Inkuire.TypeLambda.argument(names(i)) - case ParamRef(m: MethodType, i) => - inner(m.paramTypes(i), vars) + case ParamRef(binder: LambdaType, i) => + Inkuire.TypeLambda.argument(binder.paramNames(i)) case RecursiveType(tp) => inner(tp, vars) case m@MethodType(_, typeList, resType) => From c8efe69ac26d2767e638641bd633533200a1eb87 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 18 Mar 2023 16:28:08 +0100 Subject: [PATCH 302/657] Align tests in TypeComparer and AsSeenFrom --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 07a573e9169d..45ccf80198e2 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -284,13 +284,13 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling given Context = ctx // optimization for performance val info2 = tp2.info - /** Does `tp2` have a singleton type or NoPrefix as a prefix? + /** Does `tp2` have a stable prefix or NoPrefix as a prefix? * If that's not the case, following an alias via asSeenFrom could be lossy * so we should not conclude `false` if comparing aliases fails. * See pos/i17064.scala for a test case */ def hasPrecisePrefix(tp: NamedType) = - tp.prefix.isSingleton || tp.prefix == NoPrefix + tp.prefix.isStable || tp.prefix == NoPrefix info2 match case info2: TypeAlias => From 10538994e3990c68e90c8bf3ec5270a304a3e0be Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 7 Mar 2023 13:23:26 +0100 Subject: [PATCH 303/657] Dealias quoted types when staging This change improves the code generation of the contents of splices. In the splicing phase, we have to find all types that are defined in the quote but used in the splice. When there are type aliases, we can end up with several `Type[T]` for the different aliases of `T`. By dealiasing during the staging phase (just before splicing phase) we make sure that the splicer phase will only generate one `Type[T]`. By dealiasing we also optimize some situations where a type from outside a quote is inserted in the quoted code and then used in one of its splice through an alias. In this situation we can use the outer `Type[T]` directly. --- .../tools/dotc/staging/CrossStageSafety.scala | 27 +++++++++++--- .../dotty/tools/dotc/staging/HealType.scala | 18 ++++----- tests/pos-macros/i8100b.scala | 37 +++++++++++++++++++ tests/run-macros/i12392.check | 2 +- tests/run-staging/quote-nested-3.check | 4 +- tests/run-staging/quote-nested-4.check | 4 +- tests/run-staging/quote-nested-6.check | 4 +- tests/run-staging/quote-owners-2.check | 2 +- .../run-staging/quote-unrolled-foreach.check | 8 ++-- tests/run-staging/shonan-hmm-simple.check | 6 +-- tests/run-staging/shonan-hmm.check | 24 ++++++------ 11 files changed, 95 insertions(+), 41 deletions(-) create mode 100644 tests/pos-macros/i8100b.scala diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 219b428ca8d4..16df5708993e 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -122,12 +122,29 @@ class CrossStageSafety extends TreeMapWithStages { val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), body2 :: Nil) else - val quotes = quote.args.mapConserve(transform) - body.tpe match - case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice => - // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` - ref(x) + object DirectTypeOfRef: + def unapply(body: Tree): Option[Tree] = + body.tpe match + case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice => + // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` + Some(ref(x).withSpan(quote.span)) + case _ => + body2 match + case Block(List(tdef: TypeDef), tpt: TypeTree) => + tpt.tpe match + case tpe: TypeRef if tpe.typeSymbol == tdef.symbol => + tdef.rhs.tpe.hiBound match + case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice => + // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` + Some(ref(x).withSpan(quote.span)) + case _ => None + case _ => None + case _ => None + + body match + case DirectTypeOfRef(ref) => ref case _ => + val quotes = quote.args.mapConserve(transform) // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` val TypeApply(fun, _) = quote.fun: @unchecked cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body2 :: Nil), quotes) diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 22008f381c32..7907c2e47542 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -32,7 +32,12 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { def apply(tp: Type): Type = tp match case tp: TypeRef => - healTypeRef(tp) + tp.underlying match + case TypeAlias(alias) + if !tp.symbol.isTypeSplice && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => + this.apply(alias) + case _ => + healTypeRef(tp) case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) => levelError(tp.symbol, tp, pos) case tp: AnnotatedType => @@ -46,11 +51,11 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { checkNotWildcardSplice(tp) if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix) case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) => - dealiasAndTryHeal(prefix.symbol, tp, pos) + tryHeal(prefix.symbol, tp, pos) case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => - dealiasAndTryHeal(tp.symbol, tp, pos) + tryHeal(tp.symbol, tp, pos) case prefix: ThisType if level > levelOf(prefix.cls) && !tp.symbol.isStatic => - dealiasAndTryHeal(tp.symbol, tp, pos) + tryHeal(tp.symbol, tp, pos) case _ => mapOver(tp) @@ -59,11 +64,6 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { case (tb: TypeBounds) :: _ => report.error(em"Cannot splice $splice because it is a wildcard type", pos) case _ => - private def dealiasAndTryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): Type = - val tp1 = tp.dealias - if tp1 != tp then apply(tp1) - else tryHeal(tp.symbol, tp, pos) - /** Try to heal reference to type `T` used in a higher level than its definition. * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. diff --git a/tests/pos-macros/i8100b.scala b/tests/pos-macros/i8100b.scala new file mode 100644 index 000000000000..ecba10e439d2 --- /dev/null +++ b/tests/pos-macros/i8100b.scala @@ -0,0 +1,37 @@ +import scala.quoted.* + +def f[T](using t: Type[T])(using Quotes) = + '{ + // @SplicedType type t$1 = t.Underlying + type T2 = T // type T2 = t$1 + ${ + + val t0: T = ??? + val t1: T2 = ??? // val t1: T = ??? + val tp1 = Type.of[T] // val tp1 = t + val tp2 = Type.of[T2] // val tp2 = t + '{ + // @SplicedType type t$2 = t.Underlying + val t3: T = ??? // val t3: t$2 = ??? + val t4: T2 = ??? // val t4: t$2 = ??? + } + } + } + +def g(using Quotes) = + '{ + type U + type U2 = U + ${ + + val u1: U = ??? + val u2: U2 = ??? // val u2: U = ??? + + val tp1 = Type.of[U] // val tp1 = Type.of[U] + val tp2 = Type.of[U2] // val tp2 = Type.of[U] + '{ + val u3: U = ??? + val u4: U2 = ??? // val u4: U = ??? + } + } + } diff --git a/tests/run-macros/i12392.check b/tests/run-macros/i12392.check index 54c7f5d06c3f..92bbfa65fb49 100644 --- a/tests/run-macros/i12392.check +++ b/tests/run-macros/i12392.check @@ -1 +1 @@ -scala.Option[scala.Predef.String] to scala.Option[scala.Int] +scala.Option[java.lang.String] to scala.Option[scala.Int] diff --git a/tests/run-staging/quote-nested-3.check b/tests/run-staging/quote-nested-3.check index 63bdda5c6c4c..c3dfba2d8abe 100644 --- a/tests/run-staging/quote-nested-3.check +++ b/tests/run-staging/quote-nested-3.check @@ -1,7 +1,7 @@ { - type T = scala.Predef.String + type T = java.lang.String val x: java.lang.String = "foo" - val z: T = x + val z: java.lang.String = x (x: java.lang.String) } diff --git a/tests/run-staging/quote-nested-4.check b/tests/run-staging/quote-nested-4.check index 895bd0ddc914..d31b6394dccd 100644 --- a/tests/run-staging/quote-nested-4.check +++ b/tests/run-staging/quote-nested-4.check @@ -1,5 +1,5 @@ ((q: scala.quoted.Quotes) ?=> { - val t: scala.quoted.Type[scala.Predef.String] = scala.quoted.Type.of[scala.Predef.String](q) + val t: scala.quoted.Type[java.lang.String] = scala.quoted.Type.of[java.lang.String](q) - (t: scala.quoted.Type[scala.Predef.String]) + (t: scala.quoted.Type[java.lang.String]) }) diff --git a/tests/run-staging/quote-nested-6.check b/tests/run-staging/quote-nested-6.check index 05c2bd4eb00c..2ae8b0d26e47 100644 --- a/tests/run-staging/quote-nested-6.check +++ b/tests/run-staging/quote-nested-6.check @@ -1,7 +1,7 @@ { - type T[X] = scala.List[X] + type T[X] = [A >: scala.Nothing <: scala.Any] => scala.collection.immutable.List[A][X] val x: java.lang.String = "foo" - val z: T[scala.Predef.String] = scala.List.apply[java.lang.String](x) + val z: [X >: scala.Nothing <: scala.Any] => scala.collection.immutable.List[X][java.lang.String] = scala.List.apply[java.lang.String](x) (x: java.lang.String) } diff --git a/tests/run-staging/quote-owners-2.check b/tests/run-staging/quote-owners-2.check index 323ce64b7bc7..49c09271779c 100644 --- a/tests/run-staging/quote-owners-2.check +++ b/tests/run-staging/quote-owners-2.check @@ -2,7 +2,7 @@ def ff: scala.Int = { val a: scala.collection.immutable.List[scala.Int] = { type T = scala.collection.immutable.List[scala.Int] - val b: T = scala.Nil.::[scala.Int](3) + val b: scala.collection.immutable.List[scala.Int] = scala.Nil.::[scala.Int](3) (b: scala.collection.immutable.List[scala.Int]) } diff --git a/tests/run-staging/quote-unrolled-foreach.check b/tests/run-staging/quote-unrolled-foreach.check index 8e58ab8eed51..3a72cd1b1311 100644 --- a/tests/run-staging/quote-unrolled-foreach.check +++ b/tests/run-staging/quote-unrolled-foreach.check @@ -8,7 +8,7 @@ } }) -((arr: scala.Array[scala.Predef.String], f: scala.Function1[scala.Predef.String, scala.Unit]) => { +((arr: scala.Array[java.lang.String], f: scala.Function1[java.lang.String, scala.Unit]) => { val size: scala.Int = arr.length var i: scala.Int = 0 while (i.<(size)) { @@ -18,7 +18,7 @@ } }) -((arr: scala.Array[scala.Predef.String], f: scala.Function1[scala.Predef.String, scala.Unit]) => { +((arr: scala.Array[java.lang.String], f: scala.Function1[java.lang.String, scala.Unit]) => { val size: scala.Int = arr.length var i: scala.Int = 0 while (i.<(size)) { @@ -41,7 +41,7 @@ ((arr: scala.Array[scala.Int], f: scala.Function1[scala.Int, scala.Unit]) => { val size: scala.Int = arr.length var i: scala.Int = 0 - if (size.%(3).!=(0)) throw new scala.Exception("...") else () + if (size.%(3).!=(0)) throw new java.lang.Exception("...") else () while (i.<(size)) { f.apply(arr.apply(i)) f.apply(arr.apply(i.+(1))) @@ -53,7 +53,7 @@ ((arr: scala.Array[scala.Int], f: scala.Function1[scala.Int, scala.Unit]) => { val size: scala.Int = arr.length var i: scala.Int = 0 - if (size.%(4).!=(0)) throw new scala.Exception("...") else () + if (size.%(4).!=(0)) throw new java.lang.Exception("...") else () while (i.<(size)) { f.apply(arr.apply(i.+(0))) f.apply(arr.apply(i.+(1))) diff --git a/tests/run-staging/shonan-hmm-simple.check b/tests/run-staging/shonan-hmm-simple.check index da437646482d..cbef88812dcd 100644 --- a/tests/run-staging/shonan-hmm-simple.check +++ b/tests/run-staging/shonan-hmm-simple.check @@ -6,7 +6,7 @@ Complex(4,3) 10 ((arr1: scala.Array[scala.Int], arr2: scala.Array[scala.Int]) => { - if (arr1.length.!=(arr2.length)) throw new scala.Exception("...") else () + if (arr1.length.!=(arr2.length)) throw new java.lang.Exception("...") else () var sum: scala.Int = 0 var i: scala.Int = 0 while (i.<(scala.Predef.intArrayOps(arr1).size)) { @@ -22,13 +22,13 @@ Complex(4,3) 10 ((arr: scala.Array[scala.Int]) => { - if (arr.length.!=(5)) throw new scala.Exception("...") else () + if (arr.length.!=(5)) throw new java.lang.Exception("...") else () arr.apply(0).+(arr.apply(2)).+(arr.apply(4)) }) 10 ((arr: scala.Array[Complex[scala.Int]]) => { - if (arr.length.!=(4)) throw new scala.Exception("...") else () + if (arr.length.!=(4)) throw new java.lang.Exception("...") else () Complex.apply[scala.Int](0.-(arr.apply(0).im).+(0.-(arr.apply(2).im)).+(arr.apply(3).re.*(2)), arr.apply(0).re.+(arr.apply(2).re).+(arr.apply(3).im.*(2))) }) Complex(4,3) diff --git a/tests/run-staging/shonan-hmm.check b/tests/run-staging/shonan-hmm.check index fa5206904962..9cb77f850155 100644 --- a/tests/run-staging/shonan-hmm.check +++ b/tests/run-staging/shonan-hmm.check @@ -35,8 +35,8 @@ List(25, 30, 20, 43, 44) ((vout: scala.Array[scala.Int], a: scala.Array[scala.Array[scala.Int]], v: scala.Array[scala.Int]) => { - if (3.!=(vout.length)) throw new scala.IndexOutOfBoundsException("3") else () - if (2.!=(v.length)) throw new scala.IndexOutOfBoundsException("2") else () + if (3.!=(vout.length)) throw new java.lang.IndexOutOfBoundsException("3") else () + if (2.!=(v.length)) throw new java.lang.IndexOutOfBoundsException("2") else () vout.update(0, 0.+(v.apply(0).*(a.apply(0).apply(0))).+(v.apply(1).*(a.apply(0).apply(1)))) vout.update(1, 0.+(v.apply(0).*(a.apply(1).apply(0))).+(v.apply(1).*(a.apply(1).apply(1)))) vout.update(2, 0.+(v.apply(0).*(a.apply(2).apply(0))).+(v.apply(1).*(a.apply(2).apply(1)))) @@ -95,8 +95,8 @@ List(25, 30, 20, 43, 44) array } ((vout: scala.Array[scala.Int], v: scala.Array[scala.Int]) => { - if (5.!=(vout.length)) throw new scala.IndexOutOfBoundsException("5") else () - if (5.!=(v.length)) throw new scala.IndexOutOfBoundsException("5") else () + if (5.!=(vout.length)) throw new java.lang.IndexOutOfBoundsException("5") else () + if (5.!=(v.length)) throw new java.lang.IndexOutOfBoundsException("5") else () vout.update(0, 0.+(v.apply(0).*(5)).+(v.apply(1).*(0)).+(v.apply(2).*(0)).+(v.apply(3).*(5)).+(v.apply(4).*(0))) vout.update(1, 0.+(v.apply(0).*(0)).+(v.apply(1).*(0)).+(v.apply(2).*(10)).+(v.apply(3).*(0)).+(v.apply(4).*(0))) vout.update(2, 0.+(v.apply(0).*(0)).+(v.apply(1).*(10)).+(v.apply(2).*(0)).+(v.apply(3).*(0)).+(v.apply(4).*(0))) @@ -158,8 +158,8 @@ List(25, 30, 20, 43, 44) array } ((vout: scala.Array[scala.Int], v: scala.Array[scala.Int]) => { - if (5.!=(vout.length)) throw new scala.IndexOutOfBoundsException("5") else () - if (5.!=(v.length)) throw new scala.IndexOutOfBoundsException("5") else () + if (5.!=(vout.length)) throw new java.lang.IndexOutOfBoundsException("5") else () + if (5.!=(v.length)) throw new java.lang.IndexOutOfBoundsException("5") else () vout.update(0, v.apply(0).*(5).+(v.apply(3).*(5))) vout.update(1, v.apply(2).*(10)) vout.update(2, v.apply(1).*(10)) @@ -221,8 +221,8 @@ List(25, 30, 20, 43, 44) array } ((vout: scala.Array[scala.Int], v: scala.Array[scala.Int]) => { - if (5.!=(vout.length)) throw new scala.IndexOutOfBoundsException("5") else () - if (5.!=(v.length)) throw new scala.IndexOutOfBoundsException("5") else () + if (5.!=(vout.length)) throw new java.lang.IndexOutOfBoundsException("5") else () + if (5.!=(v.length)) throw new java.lang.IndexOutOfBoundsException("5") else () vout.update(0, v.apply(0).*(5).+(v.apply(3).*(5))) vout.update(1, v.apply(2).*(10)) vout.update(2, v.apply(1).*(10)) @@ -243,8 +243,8 @@ List(25, 30, 20, 43, 44) ((vout: scala.Array[scala.Int], v: scala.Array[scala.Int]) => { - if (5.!=(vout.length)) throw new scala.IndexOutOfBoundsException("5") else () - if (5.!=(v.length)) throw new scala.IndexOutOfBoundsException("5") else () + if (5.!=(vout.length)) throw new java.lang.IndexOutOfBoundsException("5") else () + if (5.!=(v.length)) throw new java.lang.IndexOutOfBoundsException("5") else () vout.update(0, v.apply(0).*(5).+(v.apply(3).*(5))) vout.update(1, v.apply(2).*(10)) vout.update(2, v.apply(1).*(10)) @@ -282,8 +282,8 @@ List(25, 30, 20, 43, 44) array } ((vout: scala.Array[scala.Int], v: scala.Array[scala.Int]) => { - if (5.!=(vout.length)) throw new scala.IndexOutOfBoundsException("5") else () - if (5.!=(v.length)) throw new scala.IndexOutOfBoundsException("5") else () + if (5.!=(vout.length)) throw new java.lang.IndexOutOfBoundsException("5") else () + if (5.!=(v.length)) throw new java.lang.IndexOutOfBoundsException("5") else () vout.update(0, v.apply(0).*(5).+(v.apply(3).*(5))) vout.update(1, v.apply(2).*(10)) vout.update(2, v.apply(1).*(10)) From d479de60052056744b42d16266fffd12261ad9bb Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 7 Mar 2023 17:00:37 +0100 Subject: [PATCH 304/657] Avoid transforming the body in optimization case --- .../tools/dotc/staging/CrossStageSafety.scala | 56 ++++++++----------- .../tools/dotc/staging/DirectTypeOf.scala | 25 +++++++++ 2 files changed, 48 insertions(+), 33 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/staging/DirectTypeOf.scala diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 16df5708993e..98e060488f43 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -107,47 +107,37 @@ class CrossStageSafety extends TreeMapWithStages { val stripAnnotsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val contextWithQuote = - if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext) - else quoteContext - val body1 = transform(body)(using contextWithQuote) - val body2 = + def transformBody() = + val contextWithQuote = + if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext) + else quoteContext + val transformedBody = transform(body)(using contextWithQuote) taggedTypes.getTypeTags match - case Nil => body1 - case tags => tpd.Block(tags, body1).withSpan(body.span) + case Nil => transformedBody + case tags => tpd.Block(tags, transformedBody).withSpan(body.span) if body.isTerm then + val transformedBody = transformBody() // `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T2]()` val TypeApply(fun, targs) = quote.fun: @unchecked val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), body2 :: Nil) + cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), transformedBody :: Nil) else - object DirectTypeOfRef: - def unapply(body: Tree): Option[Tree] = - body.tpe match - case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice => - // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` - Some(ref(x).withSpan(quote.span)) - case _ => - body2 match - case Block(List(tdef: TypeDef), tpt: TypeTree) => - tpt.tpe match - case tpe: TypeRef if tpe.typeSymbol == tdef.symbol => - tdef.rhs.tpe.hiBound match - case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice => - // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` - Some(ref(x).withSpan(quote.span)) - case _ => None - case _ => None - case _ => None - - body match - case DirectTypeOfRef(ref) => ref + body.tpe match + case DirectTypeOf(termRef) => + // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` + ref(termRef).withSpan(quote.span) case _ => - val quotes = quote.args.mapConserve(transform) - // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` - val TypeApply(fun, _) = quote.fun: @unchecked - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body2 :: Nil), quotes) + transformBody() match + case DirectTypeOf.Healed(termRef) => + // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` + ref(termRef).withSpan(quote.span) + case transformedBody => + val quotes = quote.args.mapConserve(transform) + // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` + val TypeApply(fun, _) = quote.fun: @unchecked + cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes) + } /** Transform splice diff --git a/compiler/src/dotty/tools/dotc/staging/DirectTypeOf.scala b/compiler/src/dotty/tools/dotc/staging/DirectTypeOf.scala new file mode 100644 index 000000000000..488d8ff2a88e --- /dev/null +++ b/compiler/src/dotty/tools/dotc/staging/DirectTypeOf.scala @@ -0,0 +1,25 @@ +package dotty.tools.dotc.staging + +import dotty.tools.dotc.ast.{tpd, untpd} +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ + +object DirectTypeOf: + import tpd.* + + /** Matches `x.Underlying` and extracts the TermRef to `x` */ + def unapply(tpe: Type)(using Context): Option[TermRef] = tpe match + case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice => Some(x) + case _ => None + + object Healed: + /** Matches `{ @SplicedType type T = x.Underlying; T }` and extracts the TermRef to `x` */ + def unapply(body: Tree)(using Context): Option[TermRef] = + body match + case Block(List(tdef: TypeDef), tpt: TypeTree) => + tpt.tpe match + case tpe: TypeRef if tpe.typeSymbol == tdef.symbol => + DirectTypeOf.unapply(tdef.rhs.tpe.hiBound) + case _ => None + case _ => None From f8fb548300850aebaece5c15a13632289a563c67 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Thu, 23 Mar 2023 10:55:28 +0100 Subject: [PATCH 305/657] Skip Java using run test when scalajs --- compiler/test/dotty/tools/vulpix/ParallelTesting.scala | 7 +++++-- tests/run/i17021.ext-java/Test.scala | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 62b1b88984bc..3799a2335a78 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -751,8 +751,11 @@ trait ParallelTesting extends RunnerOrchestration { self => case _ => } case Failure(output) => - echo(s"Test '${testSource.title}' failed with output:") - echo(output) + if output == "" then + echo(s"Test '${testSource.title}' failed with no output") + else + echo(s"Test '${testSource.title}' failed with output:") + echo(output) failTestSource(testSource) case Timeout => echo("failed because test " + testSource.title + " timed out") diff --git a/tests/run/i17021.ext-java/Test.scala b/tests/run/i17021.ext-java/Test.scala index 3741fa2dfadb..2cca286c3801 100644 --- a/tests/run/i17021.ext-java/Test.scala +++ b/tests/run/i17021.ext-java/Test.scala @@ -1,3 +1,4 @@ +// scalajs: --skip // Derives from run/i17021.defs // but with a Java protected member // and fixed calling code, that uses super From e96b422b06577b5838f961adc26b51e0a913512a Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Thu, 23 Mar 2023 11:31:28 +0100 Subject: [PATCH 306/657] Fix #17119: Download Coursier from GitHub directly git.io is a URL shortener which, it seems, is unreliable. --- .../dotty/tools/coursier/CoursierScalaTests.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala index 944bf1957d43..8f9a9bd69a50 100644 --- a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala +++ b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala @@ -170,12 +170,16 @@ object CoursierScalaTests: /** Get coursier script */ @BeforeClass def setup(): Unit = - val ver = execCmd("uname")._2.head.replace('L', 'l').replace('D', 'd') + val launcherLocation = "https://github.com/coursier/launchers/raw/master" + val launcherName = execCmd("uname")._2.head.toLowerCase match + case "linux" => "cs-x86_64-pc-linux" + case "darwin" => "cs-x86_64-apple-darwin" + case other => fail(s"Unsupported OS for coursier launcher: $other") def runAndCheckCmd(cmd: String, options: String*): Unit = val (code, out) = execCmd(cmd, options*) if code != 0 then fail(s"Failed to run $cmd ${options.mkString(" ")}, exit code: $code, output: ${out.mkString("\n")}") - runAndCheckCmd("curl", s"-fLo cs https://git.io/coursier-cli-$ver") + runAndCheckCmd("curl", s"-fLo cs $launcherLocation/$launcherName") runAndCheckCmd("chmod", "+x cs") From 36da313ddfc62d1356d2d1b8d37acf33cfad0794 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 23 Mar 2023 14:05:31 +0100 Subject: [PATCH 307/657] Add regression test for #15709 Closes #15709 --- tests/pos-macros/i15709.scala | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/pos-macros/i15709.scala diff --git a/tests/pos-macros/i15709.scala b/tests/pos-macros/i15709.scala new file mode 100644 index 000000000000..845ed35d1a55 --- /dev/null +++ b/tests/pos-macros/i15709.scala @@ -0,0 +1,4 @@ +import quoted.* + +inline def foo(s: Singleton): Unit = ${ fooImpl('s) } +def fooImpl(s: Expr[Singleton])(using Quotes) = '{} From 03a8f644c2b99073c2cca79e4d2c01539fa3391a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pa=C5=82ka?= Date: Wed, 22 Mar 2023 13:09:33 +0100 Subject: [PATCH 308/657] Improve subtyping check for not yet eta-expanded higher kinded types --- .../src/dotty/tools/dotc/core/TypeComparer.scala | 2 +- tests/pos/i16183/Lib_1.scala | 14 ++++++++++++++ tests/pos/i16183/Test_2.scala | 6 ++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i16183/Lib_1.scala create mode 100644 tests/pos/i16183/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 9dda18c72f26..ac6a6b9481d8 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -580,7 +580,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling val base = nonExprBaseType(tp1, cls2) if (base.typeSymbol == cls2) return true } - else if tp1.isLambdaSub && !tp1.isAnyKind then + else if tp1.typeParams.nonEmpty && !tp1.isAnyKind then return recur(tp1, EtaExpansion(tp2)) fourthTry } diff --git a/tests/pos/i16183/Lib_1.scala b/tests/pos/i16183/Lib_1.scala new file mode 100644 index 000000000000..824af18e1d40 --- /dev/null +++ b/tests/pos/i16183/Lib_1.scala @@ -0,0 +1,14 @@ +package pkg + +trait Foo1[A] +trait Foo2[A] extends Foo1[A] + +trait Bar[F[_]] +object Bar { + implicit val bar: Bar[pkg.Foo2] = ??? +} + +trait Qux +object Qux { + implicit def qux[F[_]](implicit bar: Bar[F]): F[Qux] = ??? +} \ No newline at end of file diff --git a/tests/pos/i16183/Test_2.scala b/tests/pos/i16183/Test_2.scala new file mode 100644 index 000000000000..c8c5cbed838c --- /dev/null +++ b/tests/pos/i16183/Test_2.scala @@ -0,0 +1,6 @@ +import pkg._ + +object Test { + implicitly[Foo2[Qux]] + implicitly[Foo1[Qux]] +} \ No newline at end of file From 1828c8a8a7d5b8827886bf63b3a791ff574c339a Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 20 Mar 2023 15:31:07 +0100 Subject: [PATCH 309/657] Add tests for extensions's signatures --- project/Build.scala | 2 +- .../src/tests/extensionParams.scala | 32 +++++++++++++------ .../scaladoc/signatures/SignatureTest.scala | 20 +++++++++--- .../TranslatableSignaturesTestCases.scala | 2 +- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 82b7fee64879..1ec68edbdc1b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -361,7 +361,7 @@ object Build { // Settings used when compiling dotty with a non-bootstrapped dotty lazy val commonBootstrappedSettings = commonDottySettings ++ NoBloopExport.settings ++ Seq( // To enable support of scaladoc and language-server projects you need to change this to true and use sbt as your build server - bspEnabled := false, + // bspEnabled := false, (Compile / unmanagedSourceDirectories) += baseDirectory.value / "src-bootstrapped", version := dottyVersion, diff --git a/scaladoc-testcases/src/tests/extensionParams.scala b/scaladoc-testcases/src/tests/extensionParams.scala index 9276bf41f067..950dc57dbe93 100644 --- a/scaladoc-testcases/src/tests/extensionParams.scala +++ b/scaladoc-testcases/src/tests/extensionParams.scala @@ -10,11 +10,11 @@ extension [A](a: A)(using Int) def f1[B](b: B): (A, B) = ??? -extension [A](a: A)(using Int) +extension [A](a: A)(using String) def f2(b: A): (A, A) = ??? -extension [A](a: A)(using Int) +extension [A](a: A)(using Number) def f3(using String)(b: A): (A, A) = ??? @@ -22,7 +22,7 @@ extension (a: Char)(using Int) def f4(using String)(b: Int): Unit = ??? -extension (a: Char)(using Int) +extension (a: String)(using Int) def f5[B](using String)(b: B): Unit = ??? @@ -34,15 +34,15 @@ extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Num def f7[B, C](b: B)(c: C): (A, B) = ??? -extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) +extension [A <: List[Char]](using String)(using Number)(a: A)(using Int)(using Unit) def f8(b: Any)(c: Any): Any = ??? -extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) +extension [A <: List[Char]](using Unit)(using String)(a: A)(using Int)(using Number) def f9[B, C](using Int)(b: B)(c: C): (A, B) = ??? -extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) +extension [A <: List[Char]](using Number)(using Unit)(a: A)(using Int)(using String) def f10(using Int)(b: Any)(c: Any): Any = ??? @@ -53,11 +53,25 @@ extension (using String)(using Unit)(a: Animal)(using Int)(using Number) def f11(b: Any)(c: Any): Any = ??? +extension (a: Int) + def f13(): Any + = ??? + +extension (using Unit)(a: Int) + def f14(): Any + = ??? + +extension (a: Int)(using Unit) + def f15(): Any + = ??? + import scala.language.experimental.clauseInterleaving -extension (using String)(using Unit)(a: Animal)(using Int)(using Number) - def f13(b: Any)[T](c: T): T +extension (using String)(using Int)(a: Animal)(using Unit)(using Number) + def f16(b: Any)[T](c: T): T = ??? - def f14[D](b: D)[T](c: T): T + def f17[D](b: D)[T](c: T): T = ??? + + diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala index 4d8a9f46f21e..9c831d6a7349 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala @@ -82,11 +82,18 @@ abstract class SignatureTest( private def startWithAnyOfThese(c: String*) = c.exists(s.startsWith) private def compactWhitespaces = whitespaceRegex.replaceAllIn(s, " ") + private var counter = 0 private def findName(signature: String, kinds: Seq[String]): Option[String] = for - kindMatch <- kinds.flatMap(k => s"\\b$k\\b".r.findFirstMatchIn(signature)).headOption + kindMatch <- kinds.flatMap(k =>s"\\b$k\\b".r.findFirstMatchIn(signature)).headOption afterKind <- Option(kindMatch.after(0)) // to filter out nulls - nameMatch <- identifierRegex.findFirstMatchIn(afterKind) + nameMatch <- identifierRegex.findFirstMatchIn( + if kindMatch.group(0).contains("extension") + then + signature + else + afterKind + ) yield nameMatch.group(1) private def signaturesFromSources(source: Source, kinds: Seq[String]): Seq[SignatureRes] = @@ -110,6 +117,11 @@ abstract class SignatureTest( def processFile(path: Path): Unit = if filterFunc(path) then val document = Jsoup.parse(IO.read(path)) + val documentable = document.select(".groupHeader").forEach { element => + val signature = element.select(".groupHeader").eachText.asScala.mkString("") + val all = s"$signature" + signatures += all + } val content = document.select(".documentableElement").forEach { elem => val annotations = elem.select(".annotations").eachText.asScala.mkString("") val other = elem.select(".header .other-modifiers").eachText.asScala.mkString("") @@ -122,13 +134,13 @@ abstract class SignatureTest( val all = s"$annotations$other $sigPrefix$signature".trim() signatures += all } - + counter = 0 IO.foreachFileIn(output, processFile) signatures.result object SignatureTest { val classlikeKinds = Seq("class", "object", "trait", "enum") // TODO add docs for packages - val members = Seq("type", "def", "val", "var", "given") + val members = Seq("type", "def", "val", "var", "given", "extension") val all = classlikeKinds ++ members } diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala index 49316b08dbc0..302ee39d6351 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala @@ -39,7 +39,7 @@ class PackageObjectSymbolSignatures extends SignatureTest("packageObjectSymbolSi class MergedPackageSignatures extends SignatureTest("mergedPackage", SignatureTest.all.filterNot(_ == "object"), sourceFiles = List("mergedPackage1", "mergedPackage2", "mergedPackage3")) -class ExtensionMethodSignature extends SignatureTest("extensionMethodSignatures", SignatureTest.all) +class ExtensionMethodSignature extends SignatureTest("extensionMethodSignatures", SignatureTest.all.filterNot(_ == "extension")) class ExtensionMethodParamsSignature extends SignatureTest("extensionParams", SignatureTest.all) From 6a05f66064ee3cc426c6142abb4dd4658a6518b1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 8 Mar 2023 13:54:34 +0100 Subject: [PATCH 310/657] Implement getScaladoc2Type without using tree show Showing the tree or type does not return a reliable representation. The only way to create this correctly is be creating a custom printer that prints in the required format. --- .../tasty/Scaladoc2AnchorCreator.scala | 70 +++++++++++++++++-- .../dotty/tools/scaladoc/tasty/SymOps.scala | 2 +- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/Scaladoc2AnchorCreator.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/Scaladoc2AnchorCreator.scala index ba59f77495b1..3c34a1c9bba9 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/Scaladoc2AnchorCreator.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/Scaladoc2AnchorCreator.scala @@ -7,10 +7,66 @@ import scala.util.matching.Regex object Scaladoc2AnchorCreator: - def getScaladoc2Type(using Quotes)(t: reflect.Tree) = - import reflect.* - val regex = t match - case d: DefDef => "def" - case t: TypeDef => "type" - case v: ValDef => "val|var" - t.show(using Printer.TreeShortCode).split(regex, 2)(1).replace(" ","") + def getScaladoc2Type(using Quotes)(sym: quotes.reflect.Symbol) = signatureAnchor(sym) + + /** Creates the signature anchor + * + * - `X` for a `type X ...` + * - `x:X` for a `val x: X` + * - `f[U1,...](x1:T1,...)(impliciti1:U1,impliciti2:U2,...)...:R` for a `def f[U1, ...](x1: T1, ...)(implicit i1: U1, i2: U2...)...: R` + * + * Types are printed without their paths. No spaces are printed in the output. + */ + private def signatureAnchor(using Quotes)(sym: quotes.reflect.Symbol): String = + import quotes.reflect.* + def signatureType(tp: quotes.reflect.TypeRepr): String = + tp match + case mt @ MethodType(paramNames, paramTypes, res) => + val implicitPrefix = if mt.isImplicit then "implicit" else "" + val closeClause = res match + case _: MethodOrPoly => ")" + case _ => "):" + paramNames.zip(paramTypes.map(signatureType)) + .map((name, tpe) => s"$implicitPrefix$name:$tpe") + .mkString("(", ",", closeClause) + signatureType(res) + case PolyType(paramNames, paramBounds, res) => + val closeClause = res match + case _: MethodOrPoly => "]" + case _ => "]:" + paramNames.zip(paramBounds.map(signatureType)) + .map((name, tpe) => s"$name$tpe") + .mkString("[", ",", closeClause) + signatureType(res) + case TypeLambda(paramNames, paramBounds, res) => + paramNames.zip(paramBounds.map(signatureType)) + .map((name, tpe) => s"$name$tpe") + .mkString("[", ",", "]") + "=>" + signatureType(res) + case ByNameType(tp) => + ":" + signatureType(tp) + case TypeBounds(low, hi) => + val lowBound = if low =:= defn.NothingClass.typeRef then "" else ">:" + signatureType(low) + val hiBound = if low =:= defn.AnyClass.typeRef then "" else "<:" + signatureType(hi) + lowBound + hiBound + case tp: ParamRef => + tp.binder match + case binder: MethodType => binder.paramNames(tp.paramNum) + ".type" + case binder: PolyType => binder.paramNames(tp.paramNum) + case binder: LambdaType => binder.paramNames(tp.paramNum) + case AppliedType(tycon, args) => + args.map { + case tp: TypeBounds => "_" + signatureType(tp) + case tp => signatureType(tp) + }.mkString(signatureType(tycon) + "[", ",", "]") + case tp: AnnotatedType => + signatureType(tp.underlying) + "@" + tp.annotation.symbol.owner.name + case tp: ThisType => + signatureType(tp.tref) + ".this" + case tp: TypeRef => + tp.name + case tp => + // TODO handle other cases without using show (show does not have a stable representation) + tp.show(using Printer.TypeReprShortCode).replace(" ","") + + sym match + case sym if sym.isType => sym.name + case sym if sym.flags.is(Flags.Method) => sym.name + signatureType(sym.info) + case sym => sym.name + ":" + signatureType(sym.info) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index 584ef1f54267..5bc1b98a7fff 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -235,7 +235,7 @@ class SymOpsWithLinkCache: def constructPathForScaladoc2: String = val l = escapeUrl(location.mkString("/")) val scaladoc2Anchor = if anchor.isDefined then { - "#" + getScaladoc2Type(sym.tree) + "#" + getScaladoc2Type(sym) } else "" docURL + l + extension + scaladoc2Anchor From 1c2ceae379b62f298935cf280430ac7b01b50a78 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 6 Mar 2023 17:26:15 +0100 Subject: [PATCH 311/657] Fix type aliases in beta-reduction of polyfunctions Fixes #17052 --- .../tools/dotc/transform/BetaReduce.scala | 2 +- .../tools/dotc/transform/TreeChecker.scala | 5 ++++ .../runtime/impl/printers/SourceCode.scala | 28 +++++++++++-------- tests/pos/i17052.scala | 2 ++ 4 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 tests/pos/i17052.scala diff --git a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala index c586bb06cc29..b8cbb4367db4 100644 --- a/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala +++ b/compiler/src/dotty/tools/dotc/transform/BetaReduce.scala @@ -117,7 +117,7 @@ object BetaReduce: case ref @ TypeRef(NoPrefix, _) => ref.symbol case _ => - val binding = TypeDef(newSymbol(ctx.owner, tparam.name, EmptyFlags, targ.tpe, coord = targ.span)).withSpan(targ.span) + val binding = TypeDef(newSymbol(ctx.owner, tparam.name, EmptyFlags, TypeAlias(targ.tpe), coord = targ.span)).withSpan(targ.span) bindings += binding binding.symbol diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 52942ea719f9..96d6da39a913 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -533,6 +533,11 @@ object TreeChecker { i"owner chain = ${tree.symbol.ownersIterator.toList}%, %, ctxOwners = ${ctx.outersIterator.map(_.owner).toList}%, %") } + override def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = { + assert(sym.info.isInstanceOf[ClassInfo | TypeBounds], i"wrong type, expect a template or type bounds for ${sym.fullName}, but found: ${sym.info}") + super.typedTypeDef(tdef, sym) + } + override def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(using Context): Tree = { val TypeDef(_, impl @ Template(constr, _, _, _)) = cdef: @unchecked assert(cdef.symbol == cls) diff --git a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala index e934c1930163..a6a773adc9ba 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala @@ -1345,18 +1345,22 @@ object SourceCode { } private def printBoundsTree(bounds: TypeBoundsTree)(using elideThis: Option[Symbol]): this.type = { - bounds.low match { - case Inferred() => - case low => - this += " >: " - printTypeTree(low) - } - bounds.hi match { - case Inferred() => this - case hi => - this += " <: " - printTypeTree(hi) - } + if bounds.low.tpe == bounds.hi.tpe then + this += " = " + printTypeTree(bounds.low) + else + bounds.low match { + case Inferred() => + case low => + this += " >: " + printTypeTree(low) + } + bounds.hi match { + case Inferred() => this + case hi => + this += " <: " + printTypeTree(hi) + } } private def printBounds(bounds: TypeBounds)(using elideThis: Option[Symbol]): this.type = { diff --git a/tests/pos/i17052.scala b/tests/pos/i17052.scala new file mode 100644 index 000000000000..4bb8cf493d0b --- /dev/null +++ b/tests/pos/i17052.scala @@ -0,0 +1,2 @@ +def test[F[_]](fAny: F[Any]) = + { [X] => (fx: F[X]) => { val fx2: F[X] = fx; () } }.apply[Any](fAny) From d17da7a733e9eda1e2618792609da07aa84dd50b Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Sat, 25 Mar 2023 14:57:05 +0100 Subject: [PATCH 312/657] Simplify hasPrecisePrefix check `NoPrefix.isStable` already returns true. --- compiler/src/dotty/tools/dotc/core/TypeComparer.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 45ccf80198e2..465978d329e6 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -284,18 +284,18 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling given Context = ctx // optimization for performance val info2 = tp2.info - /** Does `tp2` have a stable prefix or NoPrefix as a prefix? + /** Does `tp2` have a stable prefix? * If that's not the case, following an alias via asSeenFrom could be lossy * so we should not conclude `false` if comparing aliases fails. * See pos/i17064.scala for a test case */ - def hasPrecisePrefix(tp: NamedType) = - tp.prefix.isStable || tp.prefix == NoPrefix + def hasStablePrefix(tp: NamedType) = + tp.prefix.isStable info2 match case info2: TypeAlias => if recur(tp1, info2.alias) then return true - if tp2.asInstanceOf[TypeRef].canDropAlias && hasPrecisePrefix(tp2) then + if tp2.asInstanceOf[TypeRef].canDropAlias && hasStablePrefix(tp2) then return false case _ => tp1 match @@ -303,7 +303,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling tp1.info match { case info1: TypeAlias => if recur(info1.alias, tp2) then return true - if tp1.asInstanceOf[TypeRef].canDropAlias && hasPrecisePrefix(tp2) then + if tp1.asInstanceOf[TypeRef].canDropAlias && hasStablePrefix(tp2) then return false case _ => } From e3aeed198717e6764f2a27b70a22a03b03a87a0b Mon Sep 17 00:00:00 2001 From: David Hua Date: Sun, 26 Mar 2023 01:23:43 -0400 Subject: [PATCH 313/657] Add test for cacheResult flag --- tests/init/pos/recursive.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/init/pos/recursive.scala diff --git a/tests/init/pos/recursive.scala b/tests/init/pos/recursive.scala new file mode 100644 index 000000000000..74b658330e03 --- /dev/null +++ b/tests/init/pos/recursive.scala @@ -0,0 +1,9 @@ +class A { + def p(cb: Int => Int): Int = cb(0) + + val q: List[Int] = { + def f(x: Int): Int => Int = y => p(f(y)) + List(1, 2).map(f(3)) + } + val n: Int = 4 +} \ No newline at end of file From 8af1b29a88ac71e3eea910b206c2b8c165bc35a8 Mon Sep 17 00:00:00 2001 From: David Hua Date: Sun, 26 Mar 2023 01:33:40 -0400 Subject: [PATCH 314/657] Reformat the output when printing a function to console in init checker --- compiler/src/dotty/tools/dotc/transform/init/Semantic.scala | 2 +- tests/init/neg/closureLeak.check | 2 +- tests/init/neg/t3273.check | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala index b8d867e96a7e..9412cef6efc8 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Semantic.scala @@ -69,7 +69,7 @@ object Semantic: val argsText = if args.nonEmpty then ", args = " + args.map(_.show).mkString("(", ", ", ")") else "" "a non-transitively initialized (Warm) object of type (" + klass.show + ") { outer = " + outer.show + argsText + " }" case Fun(expr, thisV, klass) => - "a function where \"this\" is (" + thisV.show + ") and the function owner is an object of type (" + klass.show + ")" + "a function where \"this\" is (" + thisV.show + ")" case RefSet(values) => values.map(_.show).mkString("Set { ", ", ", " }") case Hot => diff --git a/tests/init/neg/closureLeak.check b/tests/init/neg/closureLeak.check index 2437509faa73..a90acaa8ed00 100644 --- a/tests/init/neg/closureLeak.check +++ b/tests/init/neg/closureLeak.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/closureLeak.scala:11:14 ----------------------------------------------------------------------- 11 | l.foreach(a => a.addX(this)) // error | ^^^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (class Outer) where initialization checking started) and the function owner is an object of type (class Outer). Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (class Outer) where initialization checking started). Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: |-> class Outer { [ closureLeak.scala:1 ] | ^ |-> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ] diff --git a/tests/init/neg/t3273.check b/tests/init/neg/t3273.check index 2e4d8db50ebb..0fe7ea78871c 100644 --- a/tests/init/neg/t3273.check +++ b/tests/init/neg/t3273.check @@ -1,7 +1,7 @@ -- Error: tests/init/neg/t3273.scala:4:42 ------------------------------------------------------------------------------ 4 | val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error | ^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (object Test) where initialization checking started) and the function owner is an object of type (object Test). Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (object Test) where initialization checking started). Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: |-> object Test { [ t3273.scala:3 ] | ^ |-> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ] @@ -14,7 +14,7 @@ -- Error: tests/init/neg/t3273.scala:5:61 ------------------------------------------------------------------------------ 5 | val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (object Test) where initialization checking started) and the function owner is an object of type (object Test). Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: + |Could not verify that the method argument is transitively initialized (Hot). It was found to be a function where "this" is (the original object of type (object Test) where initialization checking started). Only transitively initialized arguments may be passed to methods (except constructors). Calling trace: |-> object Test { [ t3273.scala:3 ] | ^ |-> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ] From 0625d1c62e0fb4ce05a3ce6fa6b6a1640ea15d75 Mon Sep 17 00:00:00 2001 From: Lucas Leblanc <44496264+Dedelweiss@users.noreply.github.com> Date: Mon, 27 Mar 2023 07:56:18 +0200 Subject: [PATCH 315/657] Fix: Add Doctype to be in standard mode (#17087) In this PR, I added the `` in the HtmlRenderer file. I combined in the def page content the tag html with all the content of the page (head, body,...) and the Doctype. ## Result : Screenshot 2023-03-13 at 16 03 43 (On chrome) Screenshot 2023-03-13 at 16 04 36 Fixes #16803 --- .../src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala index 719033959b47..dbb3b6bc6014 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala @@ -41,7 +41,7 @@ class HtmlRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx: Do case _ => Nil) :+ (Attr("data-pathToRoot") := pathToRoot(page.link.dri)) - html(attrs: _*)( + val htmlTag = html(attrs: _*)( head((mkHead(page) :+ docHead):_*), body( if !page.hasFrame then docBody @@ -49,6 +49,10 @@ class HtmlRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx: Do ) ) + val doctypeTag = s"" + val finalTag = raw(doctypeTag + htmlTag.toString) + finalTag + override def render(): Unit = val renderedResources = renderResources() super.render() From 36db085c7821a4daf1cef8567dc06aca2fc1175b Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 27 Mar 2023 12:56:56 +0200 Subject: [PATCH 316/657] Improve "constructor proxy shadows outer" handling - Avoid an error if outer reference cannot be applied - Switch to outer reference if reference is not to a function in an application - Improve error message with hints how to fix it --- .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 31 ++++++++ .../src/dotty/tools/dotc/typer/Typer.scala | 44 +++++++---- .../explain/constructor-proxy-shadowing.check | 75 +++++++++++++++++++ .../explain/constructor-proxy-shadowing.scala | 16 ++++ tests/neg/constructor-proxy-shadowing.scala | 10 --- tests/pos/constr-proxy-shadowing.scala | 7 ++ 7 files changed, 161 insertions(+), 23 deletions(-) create mode 100644 tests/neg-custom-args/explain/constructor-proxy-shadowing.check create mode 100644 tests/neg-custom-args/explain/constructor-proxy-shadowing.scala delete mode 100644 tests/neg/constructor-proxy-shadowing.scala create mode 100644 tests/pos/constr-proxy-shadowing.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index b72ec2a19d76..65ebeb02c667 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -190,6 +190,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case InlineGivenShouldNotBeFunctionID // errorNumber 174 case ValueDiscardingID // errorNumber 175 case UnusedNonUnitValueID // errorNumber 176 + case ConstrProxyShadowsID // errorNumber 177 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 4d5e77ef2c49..9875c508c9fd 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1305,6 +1305,37 @@ extends SyntaxMsg(VarArgsParamMustComeLastID) { import typer.Typer.BindingPrec +class ConstrProxyShadows(proxy: TermRef, shadowed: Type, shadowedIsApply: Boolean)(using Context) + extends ReferenceMsg(ConstrProxyShadowsID), NoDisambiguation: + + def clsString(using Context) = proxy.symbol.companionClass.showLocated + def shadowedString(using Context) = shadowed.termSymbol.showLocated + def appClause = if shadowedIsApply then " the apply method of" else "" + def appSuffix = if shadowedIsApply then ".apply" else "" + + def msg(using Context) = + i"""Reference to constructor proxy for $clsString + |shadows outer reference to $shadowedString + | + |The instance needs to be created with an explicit `new`.""" + + def explain(using Context) = + i"""There is an ambiguity in the meaning of the call + | + | ${proxy.symbol.name}(...) + | + |It could mean creating an instance of $clsString with + | + | new ${proxy.symbol.companionClass.name}(...) + | + |Or it could mean calling$appClause $shadowedString as in + | + | ${shadowed.termSymbol.name}$appSuffix(...) + | + |To disambiguate, use an explicit `new` if you mean the former, + |or use a full prefix for ${shadowed.termSymbol.name} if you mean the latter.""" +end ConstrProxyShadows + class AmbiguousReference(name: Name, newPrec: BindingPrec, prevPrec: BindingPrec, prevCtx: Context)(using Context) extends ReferenceMsg(AmbiguousReferenceID), NoDisambiguation { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 8e8c8b2da1ca..45e8d2da00ca 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -544,22 +544,40 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer unimported = saved1 foundUnderScala2 = saved2 - def checkNotShadowed(ownType: Type) = ownType match - case ownType: TermRef if ownType.symbol.is(ConstructorProxy) => - val shadowed = findRef(name, pt, EmptyFlags, ConstructorProxy, tree.srcPos) - if shadowed.exists then - report.error( - em"""Reference to constructor proxy for ${ownType.symbol.companionClass.showLocated} - |shadows outer reference to ${shadowed.termSymbol.showLocated}""", tree.srcPos) - case _ => + /** Normally, returns `ownType` except if `ownType` is a constructor proxy, + * and there is another shadowed type accessible with the same name that is not: + * - if the prototype is an application: + * - if the shadowed type has a method alternative or an apply method, + * issue an ambiguity error + * - otherwise again return `ownType` + * - if the prototype is not an application, return the shadowed type + */ + def checkNotShadowed(ownType: Type): Type = + ownType match + case ownType: TermRef if ownType.symbol.is(ConstructorProxy) => + findRef(name, pt, EmptyFlags, ConstructorProxy, tree.srcPos) match + case shadowed: TermRef => + pt match + case pt: FunOrPolyProto => + def err(shadowedIsApply: Boolean) = + report.error(ConstrProxyShadows(ownType, shadowed, shadowedIsApply), tree.srcPos) + if shadowed.denot.hasAltWith(sd => sd.symbol.is(Method, butNot = Accessor)) then + err(shadowedIsApply = false) + else if shadowed.member(nme.apply).hasAltWith(_.symbol.is(Method, butNot = Accessor)) then + err(shadowedIsApply = true) + case _ => + return shadowed + case shadowed => + case _ => + ownType def setType(ownType: Type): Tree = - checkNotShadowed(ownType) - val tree1 = ownType match - case ownType: NamedType if !prefixIsElidable(ownType) => - ref(ownType).withSpan(tree.span) + val checkedType = checkNotShadowed(ownType) + val tree1 = checkedType match + case checkedType: NamedType if !prefixIsElidable(checkedType) => + ref(checkedType).withSpan(tree.span) case _ => - tree.withType(ownType) + tree.withType(checkedType) val tree2 = toNotNullTermRef(tree1, pt) checkLegalValue(tree2, pt) tree2 diff --git a/tests/neg-custom-args/explain/constructor-proxy-shadowing.check b/tests/neg-custom-args/explain/constructor-proxy-shadowing.check new file mode 100644 index 000000000000..db223ba33640 --- /dev/null +++ b/tests/neg-custom-args/explain/constructor-proxy-shadowing.check @@ -0,0 +1,75 @@ +-- [E177] Reference Error: tests/neg-custom-args/explain/constructor-proxy-shadowing.scala:10:12 ----------------------- +10 | val x = A22("") // error: shadowing + | ^^^ + | Reference to constructor proxy for class A22 in class A + | shadows outer reference to method A22 in object Test + | + | The instance needs to be created with an explicit `new`. + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | There is an ambiguity in the meaning of the call + | + | A22(...) + | + | It could mean creating an instance of class A22 in class A with + | + | new A22(...) + | + | Or it could mean calling method A22 in object Test as in + | + | A22(...) + | + | To disambiguate, use an explicit `new` if you mean the former, + | or use a full prefix for A22 if you mean the latter. + -------------------------------------------------------------------------------------------------------------------- +-- [E177] Reference Error: tests/neg-custom-args/explain/constructor-proxy-shadowing.scala:11:12 ----------------------- +11 | val y = A33("") // error: shadowing + | ^^^ + | Reference to constructor proxy for class A33 in class A + | shadows outer reference to object A33 in object Test + | + | The instance needs to be created with an explicit `new`. + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | There is an ambiguity in the meaning of the call + | + | A33(...) + | + | It could mean creating an instance of class A33 in class A with + | + | new A33(...) + | + | Or it could mean calling the apply method of object A33 in object Test as in + | + | A33.apply(...) + | + | To disambiguate, use an explicit `new` if you mean the former, + | or use a full prefix for A33 if you mean the latter. + -------------------------------------------------------------------------------------------------------------------- +-- [E177] Reference Error: tests/neg-custom-args/explain/constructor-proxy-shadowing.scala:16:8 ------------------------ +16 |val x = Seq(3) // error: shadowing + | ^^^ + | Reference to constructor proxy for class Seq + | shadows outer reference to getter Seq in package scala + | + | The instance needs to be created with an explicit `new`. + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | There is an ambiguity in the meaning of the call + | + | Seq(...) + | + | It could mean creating an instance of class Seq with + | + | new Seq(...) + | + | Or it could mean calling the apply method of getter Seq in package scala as in + | + | Seq.apply(...) + | + | To disambiguate, use an explicit `new` if you mean the former, + | or use a full prefix for Seq if you mean the latter. + -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg-custom-args/explain/constructor-proxy-shadowing.scala b/tests/neg-custom-args/explain/constructor-proxy-shadowing.scala new file mode 100644 index 000000000000..c47fc2f4859b --- /dev/null +++ b/tests/neg-custom-args/explain/constructor-proxy-shadowing.scala @@ -0,0 +1,16 @@ + +object Test extends App { + def A22(s: String): String = s + class A33(s: String) + object A33: + def apply(s: String) = ??? + class A { + class A22(s: String) + class A33(s: String) + val x = A22("") // error: shadowing + val y = A33("") // error: shadowing + } +} + +class Seq(n: Int) +val x = Seq(3) // error: shadowing diff --git a/tests/neg/constructor-proxy-shadowing.scala b/tests/neg/constructor-proxy-shadowing.scala deleted file mode 100644 index 857ef986cb79..000000000000 --- a/tests/neg/constructor-proxy-shadowing.scala +++ /dev/null @@ -1,10 +0,0 @@ - -object Test extends App { - def A22(s: String): String = s - class A { - class A22(s: String) { - def run = s - } - val x = A22("") // error: shadowing - } -} \ No newline at end of file diff --git a/tests/pos/constr-proxy-shadowing.scala b/tests/pos/constr-proxy-shadowing.scala new file mode 100644 index 000000000000..3a3b7cdfec2a --- /dev/null +++ b/tests/pos/constr-proxy-shadowing.scala @@ -0,0 +1,7 @@ +class Number(n: Int) +val x = Number(3) + +class Seq(n: Int) +val y = Seq + + From 80b165c568699da0265f2bd0b105906d9491c8c3 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 27 Mar 2023 15:38:47 +0200 Subject: [PATCH 317/657] Change the rules for constructor proxy shadowing in reference docs --- .../reference/other-new-features/creator-applications.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/_docs/reference/other-new-features/creator-applications.md b/docs/_docs/reference/other-new-features/creator-applications.md index 81f09d897955..8b1de02b2f25 100644 --- a/docs/_docs/reference/other-new-features/creator-applications.md +++ b/docs/_docs/reference/other-new-features/creator-applications.md @@ -47,8 +47,12 @@ be selected with `apply` (or be applied to arguments, in which case the `apply` inserted). Constructor proxies are also not allowed to shadow normal definitions. That is, -if an identifier resolves to a constructor proxy, and the same identifier is also -defined or imported in some other scope, an ambiguity is reported. +an ambiguity is reported, if + + - an identifier resolves to a constructor proxy, + - the same identifier is also defined or imported in some other scope, + - the other reference can be applied to a (possibly empty) parameter list. That + is, it refers either to a method or to a value containing an apply method as member. ## Motivation From fa9648259d29a3c8bfcf8f1123bfa19b31931a84 Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Mon, 27 Jun 2022 16:22:15 +0200 Subject: [PATCH 318/657] Port GenBCode refactor Scala#6012 --- .../tools/backend/jvm/BCodeBodyBuilder.scala | 2 +- .../tools/backend/jvm/BCodeHelpers.scala | 95 +-- .../tools/backend/jvm/BCodeIdiomatic.scala | 41 +- .../tools/backend/jvm/BCodeSkelBuilder.scala | 2 +- .../src/dotty/tools/backend/jvm/BTypes.scala | 18 +- .../tools/backend/jvm/BTypesFromSymbols.scala | 43 +- .../tools/backend/jvm/BackendUtils.scala | 181 +++++ .../tools/backend/jvm/BytecodeWriters.scala | 147 ---- .../tools/backend/jvm/ClassfileWriter.scala | 142 ++++ .../src/dotty/tools/backend/jvm/CodeGen.scala | 181 +++++ .../dotty/tools/backend/jvm/CoreBTypes.scala | 256 +++---- .../backend/jvm/DottyBackendInterface.scala | 2 +- .../dotty/tools/backend/jvm/GenBCode.scala | 702 ++---------------- .../backend/jvm/GenericSignatureVisitor.scala | 6 +- .../tools/backend/jvm/PostProcessor.scala | 117 +++ .../jvm/PostProcessorFrontendAccess.scala | 79 ++ .../tools/dotc/config/ScalaSettings.scala | 2 +- docs/_docs/internals/backend.md | 56 +- 18 files changed, 937 insertions(+), 1135 deletions(-) create mode 100644 compiler/src/dotty/tools/backend/jvm/BackendUtils.scala delete mode 100644 compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala create mode 100644 compiler/src/dotty/tools/backend/jvm/ClassfileWriter.scala create mode 100644 compiler/src/dotty/tools/backend/jvm/CodeGen.scala create mode 100644 compiler/src/dotty/tools/backend/jvm/PostProcessor.scala create mode 100644 compiler/src/dotty/tools/backend/jvm/PostProcessorFrontendAccess.scala diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala index 1d559c9950f1..e7b5a0dad1bf 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala @@ -1297,7 +1297,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder { .toList // `StringConcatFactory` only got added in JDK 9, so use `StringBuilder` for lower - if (classfileVersion < asm.Opcodes.V9) { + if (backendUtils.classfileVersion < asm.Opcodes.V9) { // Estimate capacity needed for the string builder val approxBuilderSize = concatArguments.view.map { diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala index 3cf7d88b9282..c36c8c546635 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala @@ -42,18 +42,19 @@ import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions * @version 1.0 * */ -trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { +trait BCodeHelpers extends BCodeIdiomatic { // for some reason singleton types aren't allowed in constructor calls. will need several casts in code to enforce - //import global._ - //import bTypes._ - //import coreBTypes._ import bTypes._ import tpd._ import coreBTypes._ import int.{_, given} import DottyBackendInterface._ + // We need to access GenBCode phase to get access to post-processor components. + // At this point it should always be initialized already. + protected lazy val backendUtils = genBCodePhase.asInstanceOf[GenBCode].postProcessor.backendUtils + def ScalaATTRName: String = "Scala" def ScalaSignatureATTRName: String = "ScalaSig" @@ -64,96 +65,12 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { val bCodeAsmCommon: BCodeAsmCommon[int.type] = new BCodeAsmCommon(int) - /* - * must-single-thread - */ - def getFileForClassfile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { - getFile(base, clsName, suffix) - } - - /* - * must-single-thread - */ - def getOutFolder(csym: Symbol, cName: String): AbstractFile = { - try { - outputDirectory - } catch { - case ex: Throwable => - report.error(em"Couldn't create file for class $cName\n${ex.getMessage}", ctx.source.atSpan(csym.span)) - null - } - } - final def traitSuperAccessorName(sym: Symbol): String = { val nameString = sym.javaSimpleName.toString if (sym.name == nme.TRAIT_CONSTRUCTOR) nameString else nameString + "$" } - // ----------------------------------------------------------------------------------------- - // finding the least upper bound in agreement with the bytecode verifier (given two internal names handed by ASM) - // Background: - // http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf - // http://comments.gmane.org/gmane.comp.java.vm.languages/2293 - // https://issues.scala-lang.org/browse/SI-3872 - // ----------------------------------------------------------------------------------------- - - /* An `asm.ClassWriter` that uses `jvmWiseLUB()` - * The internal name of the least common ancestor of the types given by inameA and inameB. - * It's what ASM needs to know in order to compute stack map frames, http://asm.ow2.org/doc/developer-guide.html#controlflow - */ - final class CClassWriter(flags: Int) extends asm.ClassWriter(flags) { - - /** - * This method is thread-safe: it depends only on the BTypes component, which does not depend - * on global. TODO @lry move to a different place where no global is in scope, on bTypes. - */ - override def getCommonSuperClass(inameA: String, inameB: String): String = { - val a = classBTypeFromInternalName(inameA) - val b = classBTypeFromInternalName(inameB) - val lub = a.jvmWiseLUB(b) - val lubName = lub.internalName - assert(lubName != "scala/Any") - lubName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things. - } - } - - /* - * must-single-thread - */ - def initBytecodeWriter(): BytecodeWriter = { - (None: Option[AbstractFile] /*getSingleOutput*/) match { // todo: implement - case Some(f) if f.hasExtension("jar") => - new DirectToJarfileWriter(f.file) - case _ => - factoryNonJarBytecodeWriter() - } - } - - /* - * Populates the InnerClasses JVM attribute with `refedInnerClasses`. See also the doc on inner - * classes in BTypes.scala. - * - * `refedInnerClasses` may contain duplicates, need not contain the enclosing inner classes of - * each inner class it lists (those are looked up and included). - * - * This method serializes in the InnerClasses JVM attribute in an appropriate order, - * not necessarily that given by `refedInnerClasses`. - * - * can-multi-thread - */ - final def addInnerClasses(jclass: asm.ClassVisitor, declaredInnerClasses: List[ClassBType], refedInnerClasses: List[ClassBType]): Unit = { - // sorting ensures nested classes are listed after their enclosing class thus satisfying the Eclipse Java compiler - val allNestedClasses = new mutable.TreeSet[ClassBType]()(Ordering.by(_.internalName)) - allNestedClasses ++= declaredInnerClasses - refedInnerClasses.foreach(allNestedClasses ++= _.enclosingNestedClassesChain) - for nestedClass <- allNestedClasses - do { - // Extract the innerClassEntry - we know it exists, enclosingNestedClassesChain only returns nested classes. - val Some(e) = nestedClass.innerClassAttributeEntry: @unchecked - jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.flags) - } - } /* * can-multi-thread @@ -680,7 +597,7 @@ trait BCodeHelpers extends BCodeIdiomatic with BytecodeWriters { val mirrorClass = new asm.tree.ClassNode mirrorClass.visit( - classfileVersion, + backendUtils.classfileVersion, bType.info.flags, mirrorName, null /* no java-generic-signature */, diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala b/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala index b86efb7cacb1..42f8ef7f4ef6 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala @@ -19,52 +19,13 @@ import dotty.tools.dotc.report */ trait BCodeIdiomatic { val int: DottyBackendInterface - final lazy val bTypes = new BTypesFromSymbols[int.type](int) + val bTypes: BTypesFromSymbols[int.type] import int.{_, given} import bTypes._ import coreBTypes._ - - lazy val target = - val releaseValue = Option(ctx.settings.javaOutputVersion.value).filter(_.nonEmpty) - val targetValue = Option(ctx.settings.XuncheckedJavaOutputVersion.value).filter(_.nonEmpty) - val defaultTarget = "8" - (releaseValue, targetValue) match - case (Some(release), None) => release - case (None, Some(target)) => target - case (Some(release), Some(_)) => - report.warning(s"The value of ${ctx.settings.XuncheckedJavaOutputVersion.name} was overridden by ${ctx.settings.javaOutputVersion.name}") - release - case (None, None) => "8" // least supported version by default - - - // Keep synchronized with `minTargetVersion` and `maxTargetVersion` in ScalaSettings - lazy val classfileVersion: Int = target match { - case "8" => asm.Opcodes.V1_8 - case "9" => asm.Opcodes.V9 - case "10" => asm.Opcodes.V10 - case "11" => asm.Opcodes.V11 - case "12" => asm.Opcodes.V12 - case "13" => asm.Opcodes.V13 - case "14" => asm.Opcodes.V14 - case "15" => asm.Opcodes.V15 - case "16" => asm.Opcodes.V16 - case "17" => asm.Opcodes.V17 - case "18" => asm.Opcodes.V18 - case "19" => asm.Opcodes.V19 - case "20" => asm.Opcodes.V20 - } - - lazy val majorVersion: Int = (classfileVersion & 0xFF) - lazy val emitStackMapFrame = (majorVersion >= 50) - - val extraProc: Int = - import GenBCodeOps.addFlagIf - asm.ClassWriter.COMPUTE_MAXS - .addFlagIf(emitStackMapFrame, asm.ClassWriter.COMPUTE_FRAMES) - lazy val JavaStringBuilderClassName = jlStringBuilderRef.internalName val CLASS_CONSTRUCTOR_NAME = "" diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index f7afaae22f76..0a11fb898b48 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -271,7 +271,7 @@ trait BCodeSkelBuilder extends BCodeHelpers { val flags = javaFlags(claszSymbol) val thisSignature = getGenericSignature(claszSymbol, claszSymbol.owner) - cnode.visit(classfileVersion, flags, + cnode.visit(backendUtils.classfileVersion, flags, thisName, thisSignature, superClass, interfaceNames.toArray) diff --git a/compiler/src/dotty/tools/backend/jvm/BTypes.scala b/compiler/src/dotty/tools/backend/jvm/BTypes.scala index 57bd343b6658..5539bf44aa17 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypes.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypes.scala @@ -14,7 +14,9 @@ import scala.tools.asm * This representation is immutable and independent of the compiler data structures, hence it can * be queried by concurrent threads. */ -abstract class BTypes { +abstract class BTypes { self => + val frontendAccess: PostProcessorFrontendAccess + import frontendAccess.{frontendSynch} val int: DottyBackendInterface import int.given @@ -37,10 +39,7 @@ abstract class BTypes { */ def classBTypeFromInternalName(internalName: String) = classBTypeFromInternalNameMap(internalName) - // Some core BTypes are required here, in class BType, where no Global instance is available. - // The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual - // implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol. - val coreBTypes: CoreBTypesProxyGlobalIndependent[this.type] + val coreBTypes: CoreBTypes { val bTypes: self.type} import coreBTypes._ /** @@ -862,3 +861,12 @@ abstract class BTypes { */ /*final*/ case class MethodNameAndType(name: String, methodType: MethodBType) } + +object BTypes { + /** + * A marker for strings that represent class internal names. + * Ideally the type would be incompatible with String, for example by making it a value class. + * But that would create overhead in a Collection[InternalName]. + */ + type InternalName = String +} diff --git a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala index 54dafe6f0032..884dd19ee64f 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala @@ -14,20 +14,14 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.core.StdNames +import dotty.tools.dotc.core.Phases /** * This class mainly contains the method classBTypeFromSymbol, which extracts the necessary * information from a symbol and its type to create the corresponding ClassBType. It requires * access to the compiler (global parameter). - * - * The mixin CoreBTypes defines core BTypes that are used in the backend. Building these BTypes - * uses classBTypeFromSymbol, hence requires access to the compiler (global). - * - * BTypesFromSymbols extends BTypes because the implementation of BTypes requires access to some - * of the core btypes. They are declared in BTypes as abstract members. Note that BTypes does - * not have access to the compiler instance. */ -class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes { +class BTypesFromSymbols[I <: DottyBackendInterface](val int: I, val frontendAccess: PostProcessorFrontendAccess) extends BTypes { import int.{_, given} import DottyBackendInterface.{symExtensions, _} @@ -37,39 +31,18 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes { val bCodeAsmCommon: BCodeAsmCommon[int.type ] = new BCodeAsmCommon(int) import bCodeAsmCommon._ - // Why the proxy, see documentation of class [[CoreBTypes]]. - val coreBTypes: CoreBTypesProxy[this.type] = new CoreBTypesProxy[this.type](this) - import coreBTypes._ - - final def intializeCoreBTypes(): Unit = { - coreBTypes.setBTypes(new CoreBTypes[this.type](this)) - } - - private[this] val perRunCaches: Caches = new Caches { - def newAnyRefMap[K <: AnyRef, V](): mutable.AnyRefMap[K, V] = new mutable.AnyRefMap[K, V]() - def newWeakMap[K, V](): mutable.WeakHashMap[K, V] = new mutable.WeakHashMap[K, V]() - def recordCache[T <: Clearable](cache: T): T = cache - def newMap[K, V](): mutable.HashMap[K, V] = new mutable.HashMap[K, V]() - def newSet[K](): mutable.Set[K] = new mutable.HashSet[K] - } - - // TODO remove abstraction - private abstract class Caches { - def recordCache[T <: Clearable](cache: T): T - def newWeakMap[K, V](): collection.mutable.WeakHashMap[K, V] - def newMap[K, V](): collection.mutable.HashMap[K, V] - def newSet[K](): collection.mutable.Set[K] - def newAnyRefMap[K <: AnyRef, V](): collection.mutable.AnyRefMap[K, V] + val coreBTypes = new CoreBTypesFromSymbols[I]{ + val bTypes: BTypesFromSymbols.this.type = BTypesFromSymbols.this } + import coreBTypes._ - @threadUnsafe protected lazy val classBTypeFromInternalNameMap = { - perRunCaches.recordCache(collection.concurrent.TrieMap.empty[String, ClassBType]) - } + @threadUnsafe protected lazy val classBTypeFromInternalNameMap = + collection.concurrent.TrieMap.empty[String, ClassBType] /** * Cache for the method classBTypeFromSymbol. */ - @threadUnsafe private lazy val convertedClasses = perRunCaches.newMap[Symbol, ClassBType]() + @threadUnsafe private lazy val convertedClasses = collection.mutable.HashMap.empty[Symbol, ClassBType] /** * The ClassBType for a class symbol `sym`. diff --git a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala new file mode 100644 index 000000000000..d54364b1675f --- /dev/null +++ b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala @@ -0,0 +1,181 @@ +package dotty.tools.backend.jvm + +import scala.tools.asm +import scala.tools.asm.Handle +import scala.tools.asm.tree.InvokeDynamicInsnNode +import asm.tree.ClassNode +import scala.collection.mutable +import scala.jdk.CollectionConverters._ +import dotty.tools.dotc.report + +import scala.language.unsafeNulls + +/** + * This component hosts tools and utilities used in the backend that require access to a `BTypes` + * instance. + */ +class BackendUtils(val postProcessor: PostProcessor) { + import postProcessor.{bTypes, frontendAccess} + import frontendAccess.{compilerSettings} + import bTypes.* + import coreBTypes.jliLambdaMetaFactoryAltMetafactoryHandle + + // Keep synchronized with `minTargetVersion` and `maxTargetVersion` in ScalaSettings + lazy val classfileVersion: Int = compilerSettings.target match { + case "8" => asm.Opcodes.V1_8 + case "9" => asm.Opcodes.V9 + case "10" => asm.Opcodes.V10 + case "11" => asm.Opcodes.V11 + case "12" => asm.Opcodes.V12 + case "13" => asm.Opcodes.V13 + case "14" => asm.Opcodes.V14 + case "15" => asm.Opcodes.V15 + case "16" => asm.Opcodes.V16 + case "17" => asm.Opcodes.V17 + case "18" => asm.Opcodes.V18 + case "19" => asm.Opcodes.V19 + case "20" => asm.Opcodes.V20 + } + + lazy val extraProc: Int = { + import GenBCodeOps.addFlagIf + val majorVersion: Int = (classfileVersion & 0xFF) + val emitStackMapFrame = (majorVersion >= 50) + asm.ClassWriter.COMPUTE_MAXS + .addFlagIf(emitStackMapFrame, asm.ClassWriter.COMPUTE_FRAMES) + } + + def collectSerializableLambdas(classNode: ClassNode): Array[Handle] = { + val indyLambdaBodyMethods = new mutable.ArrayBuffer[Handle] + for (m <- classNode.methods.asScala) { + val iter = m.instructions.iterator + while (iter.hasNext) { + val insn = iter.next() + insn match { + case indy: InvokeDynamicInsnNode + if indy.bsm == jliLambdaMetaFactoryAltMetafactoryHandle => + import java.lang.invoke.LambdaMetafactory.FLAG_SERIALIZABLE + val metafactoryFlags = indy.bsmArgs(3).asInstanceOf[Integer].toInt + val isSerializable = (metafactoryFlags & FLAG_SERIALIZABLE) != 0 + if isSerializable then + val implMethod = indy.bsmArgs(1).asInstanceOf[Handle] + indyLambdaBodyMethods += implMethod + case _ => + } + } + } + indyLambdaBodyMethods.toArray + } + + /* + * Add: + * + * private static Object $deserializeLambda$(SerializedLambda l) { + * try return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup$0](l) + * catch { + * case i: IllegalArgumentException => + * try return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup$1](l) + * catch { + * case i: IllegalArgumentException => + * ... + * return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup${NUM_GROUPS-1}](l) + * } + * + * We use invokedynamic here to enable caching within the deserializer without needing to + * host a static field in the enclosing class. This allows us to add this method to interfaces + * that define lambdas in default methods. + * + * SI-10232 we can't pass arbitrary number of method handles to the final varargs parameter of the bootstrap + * method due to a limitation in the JVM. Instead, we emit a separate invokedynamic bytecode for each group of target + * methods. + */ + def addLambdaDeserialize(classNode: ClassNode, implMethodsArray: Array[Handle]): Unit = { + import asm.Opcodes._ + import bTypes._ + import coreBTypes._ + + val cw = classNode + + // Make sure to reference the ClassBTypes of all types that are used in the code generated + // here (e.g. java/util/Map) are initialized. Initializing a ClassBType adds it to + // `classBTypeFromInternalNameMap`. When writing the classfile, the asm ClassWriter computes + // stack map frames and invokes the `getCommonSuperClass` method. This method expects all + // ClassBTypes mentioned in the source code to exist in the map. + + val serlamObjDesc = MethodBType(jliSerializedLambdaRef :: Nil, ObjectRef).descriptor + + val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", serlamObjDesc, null, null) + def emitLambdaDeserializeIndy(targetMethods: Seq[Handle]): Unit = { + mv.visitVarInsn(ALOAD, 0) + mv.visitInvokeDynamicInsn("lambdaDeserialize", serlamObjDesc, jliLambdaDeserializeBootstrapHandle, targetMethods: _*) + } + + val targetMethodGroupLimit = 255 - 1 - 3 // JVM limit. See See MAX_MH_ARITY in CallSite.java + val groups: Array[Array[Handle]] = implMethodsArray.grouped(targetMethodGroupLimit).toArray + val numGroups = groups.length + + import scala.tools.asm.Label + val initialLabels = Array.fill(numGroups - 1)(new Label()) + val terminalLabel = new Label + def nextLabel(i: Int) = if (i == numGroups - 2) terminalLabel else initialLabels(i + 1) + + for ((label, i) <- initialLabels.iterator.zipWithIndex) { + mv.visitTryCatchBlock(label, nextLabel(i), nextLabel(i), jlIllegalArgExceptionRef.internalName) + } + for ((label, i) <- initialLabels.iterator.zipWithIndex) { + mv.visitLabel(label) + emitLambdaDeserializeIndy(groups(i).toIndexedSeq) + mv.visitInsn(ARETURN) + } + mv.visitLabel(terminalLabel) + emitLambdaDeserializeIndy(groups(numGroups - 1).toIndexedSeq) + mv.visitInsn(ARETURN) + } + + /** + * Visit the class node and collect all referenced nested classes. + */ + def collectNestedClasses(classNode: ClassNode): (List[ClassBType], List[ClassBType]) = { + // type InternalName = String + val c = new NestedClassesCollector[ClassBType](nestedOnly = true) { + def declaredNestedClasses(internalName: InternalName): List[ClassBType] = + bTypes.classBTypeFromInternalName(internalName).info.memberClasses + + def getClassIfNested(internalName: InternalName): Option[ClassBType] = { + val c = bTypes.classBTypeFromInternalName(internalName) + Option.when(c.isNestedClass)(c) + } + + def raiseError(msg: String, sig: String, e: Option[Throwable]): Unit = { + // don't crash on invalid generic signatures + } + } + c.visit(classNode) + (c.declaredInnerClasses.toList, c.referredInnerClasses.toList) + } + + /* + * Populates the InnerClasses JVM attribute with `refedInnerClasses`. See also the doc on inner + * classes in BTypes.scala. + * + * `refedInnerClasses` may contain duplicates, need not contain the enclosing inner classes of + * each inner class it lists (those are looked up and included). + * + * This method serializes in the InnerClasses JVM attribute in an appropriate order, + * not necessarily that given by `refedInnerClasses`. + * + * can-multi-thread + */ + final def addInnerClasses(jclass: asm.ClassVisitor, declaredInnerClasses: List[ClassBType], refedInnerClasses: List[ClassBType]): Unit = { + // sorting ensures nested classes are listed after their enclosing class thus satisfying the Eclipse Java compiler + val allNestedClasses = new mutable.TreeSet[ClassBType]()(Ordering.by(_.internalName)) + allNestedClasses ++= declaredInnerClasses + refedInnerClasses.foreach(allNestedClasses ++= _.enclosingNestedClassesChain) + for nestedClass <- allNestedClasses + do { + // Extract the innerClassEntry - we know it exists, enclosingNestedClassesChain only returns nested classes. + val Some(e) = nestedClass.innerClassAttributeEntry: @unchecked + jclass.visitInnerClass(e.name, e.outerName, e.innerName, e.flags) + } + } +} diff --git a/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala b/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala deleted file mode 100644 index 551d4f8d809e..000000000000 --- a/compiler/src/dotty/tools/backend/jvm/BytecodeWriters.scala +++ /dev/null @@ -1,147 +0,0 @@ -package dotty.tools -package backend -package jvm - -import scala.language.unsafeNulls - -import java.io.{ DataOutputStream, FileOutputStream, IOException, File as JFile } -import java.nio.channels.ClosedByInterruptException -import dotty.tools.io._ -import dotty.tools.dotc.report - - -/** Can't output a file due to the state of the file system. */ -class FileConflictException(msg: String, val file: AbstractFile) extends IOException(msg) - -/** For the last mile: turning generated bytecode in memory into - * something you can use. Has implementations for writing to class - * files, jars, and disassembled/javap output. - */ -trait BytecodeWriters { - val int: DottyBackendInterface - import int.{_, given} - - /** - * @param clsName cls.getName - */ - def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { - def ensureDirectory(dir: AbstractFile): AbstractFile = - if (dir.isDirectory) dir - else throw new FileConflictException(s"${base.path}/$clsName$suffix: ${dir.path} is not a directory", dir) - var dir = base - val pathParts = clsName.split("[./]").toList - for (part <- pathParts.init) dir = ensureDirectory(dir) subdirectoryNamed part - ensureDirectory(dir) fileNamed pathParts.last + suffix - } - def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile = - getFile(outputDirectory, clsName, suffix) - - def factoryNonJarBytecodeWriter(): BytecodeWriter = { - val emitAsmp = None - val doDump = dumpClasses - (emitAsmp.isDefined, doDump.isDefined) match { - case (false, false) => new ClassBytecodeWriter { } - case (false, true ) => new ClassBytecodeWriter with DumpBytecodeWriter { } - case (true, false) => new ClassBytecodeWriter with AsmpBytecodeWriter - case (true, true ) => new ClassBytecodeWriter with AsmpBytecodeWriter with DumpBytecodeWriter { } - } - } - - trait BytecodeWriter { - def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile): Unit - def close(): Unit = () - } - - class DirectToJarfileWriter(jfile: JFile) extends BytecodeWriter { - val writer = new Jar(jfile).jarWriter() - - def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile): Unit = { - assert(outfile == null, - "The outfile formal param is there just because ClassBytecodeWriter overrides this method and uses it.") - val path = jclassName + ".class" - val out = writer.newOutputStream(path) - - try out.write(jclassBytes, 0, jclassBytes.length) - finally out.flush() - - report.informProgress("added " + label + path + " to jar") - } - override def close() = writer.close() - } - - /* - * The ASM textual representation for bytecode overcomes disadvantages of javap output in three areas: - * (a) pickle dingbats undecipherable to the naked eye; - * (b) two constant pools, while having identical contents, are displayed differently due to physical layout. - * (c) stack maps (classfile version 50 and up) are displayed in encoded form by javap, - * their expansion by ASM is more readable. - * - * */ - trait AsmpBytecodeWriter extends BytecodeWriter { - import scala.tools.asm - - private val baseDir = new Directory(None.get).createDirectory() // FIXME missing directoy - // new needed here since resolution of user-defined `apply` methods is ambiguous, and we want the constructor. - - private def emitAsmp(jclassBytes: Array[Byte], asmpFile: dotty.tools.io.File): Unit = { - val pw = asmpFile.printWriter() - try { - val cnode = new ClassNode1() - val cr = new asm.ClassReader(jclassBytes) - cr.accept(cnode, 0) - val trace = new scala.tools.asm.util.TraceClassVisitor(new java.io.PrintWriter(new java.io.StringWriter())) - cnode.accept(trace) - trace.p.print(pw) - } - finally pw.close() - } - - abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile): Unit = { - super.writeClass(label, jclassName, jclassBytes, outfile) - - val segments = jclassName.split("[./]") - val asmpFile = segments.foldLeft(baseDir: Path)(_ / _).changeExtension("asmp").toFile - - asmpFile.parent.createDirectory() - emitAsmp(jclassBytes, asmpFile) - } - } - - trait ClassBytecodeWriter extends BytecodeWriter { - def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile): Unit = { - assert(outfile != null, - "Precisely this override requires its invoker to hand out a non-null AbstractFile.") - val outstream = new DataOutputStream(outfile.bufferedOutput) - - try outstream.write(jclassBytes, 0, jclassBytes.length) - catch case ex: ClosedByInterruptException => - try - outfile.delete() // don't leave an empty or half-written classfile around after an interrupt - catch - case _: Throwable => - throw ex - finally outstream.close() - report.informProgress("wrote '" + label + "' to " + outfile) - } - } - - trait DumpBytecodeWriter extends BytecodeWriter { - val baseDir = Directory(dumpClasses.get).createDirectory() - - abstract override def writeClass(label: String, jclassName: String, jclassBytes: Array[Byte], outfile: AbstractFile): Unit = { - super.writeClass(label, jclassName, jclassBytes, outfile) - - val pathName = jclassName - val dumpFile = pathName.split("[./]").foldLeft(baseDir: Path) (_ / _).changeExtension("class").toFile - dumpFile.parent.createDirectory() - val outstream = new DataOutputStream(new FileOutputStream(dumpFile.path)) - - try outstream.write(jclassBytes, 0, jclassBytes.length) - finally outstream.close() - } - } - - private def dumpClasses: Option[String] = - if (ctx.settings.Ydumpclasses.isDefault) None - else Some(ctx.settings.Ydumpclasses.value) -} diff --git a/compiler/src/dotty/tools/backend/jvm/ClassfileWriter.scala b/compiler/src/dotty/tools/backend/jvm/ClassfileWriter.scala new file mode 100644 index 000000000000..08e84de92dca --- /dev/null +++ b/compiler/src/dotty/tools/backend/jvm/ClassfileWriter.scala @@ -0,0 +1,142 @@ +package dotty.tools.backend.jvm + +import java.io.{DataOutputStream, IOException, PrintWriter, StringWriter} +import java.nio.file.Files +import java.util.jar.Attributes.Name + +import scala.tools.asm.ClassReader +import scala.tools.asm.tree.ClassNode +import dotty.tools.io.* +import dotty.tools.dotc.core.Decorators.* +import dotty.tools.dotc.util.NoSourcePosition +import java.nio.charset.StandardCharsets +import java.nio.channels.ClosedByInterruptException +import BTypes.InternalName +import scala.language.unsafeNulls + +class ClassfileWriter(frontendAccess: PostProcessorFrontendAccess) { + import frontendAccess.{backendReporting, compilerSettings} + + // if non-null, classfiles are additionally written to this directory + private val dumpOutputDir: AbstractFile = getDirectoryOrNull(compilerSettings.dumpClassesDirectory) + + // if non-null, classfiles are written to a jar instead of the output directory + private val jarWriter: JarWriter | Null = compilerSettings.outputDirectory match { + case jar: JarArchive => + val mainClass = compilerSettings.mainClass.orElse { + // If no main class was specified, see if there's only one + // entry point among the classes going into the jar. + frontendAccess.getEntryPoints match { + case name :: Nil => + backendReporting.log(i"Unique entry point: setting Main-Class to $name") + Some(name) + case names => + if names.isEmpty then backendReporting.warning(em"No Main-Class designated or discovered.") + else backendReporting.warning(em"No Main-Class due to multiple entry points:\n ${names.mkString("\n ")}") + None + } + } + jar.underlyingSource.map{ source => + if jar.isEmpty then + val jarMainAttrs = mainClass.map(Name.MAIN_CLASS -> _).toList + new Jar(source.file).jarWriter(jarMainAttrs: _*) + else + // Writing to non-empty JAR might be an undefined behaviour, e.g. in case if other files where + // created using `AbstractFile.bufferedOutputStream`instead of JarWritter + backendReporting.warning(em"Tried to write to non-empty JAR: $source") + null + }.orNull + + case _ => null + } + + private def getDirectoryOrNull(dir: Option[String]): AbstractFile = + dir.map(d => new PlainDirectory(Directory(d))).orNull + + private def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { + if (base.file != null) { + fastGetFile(base, clsName, suffix) + } else { + def ensureDirectory(dir: AbstractFile): AbstractFile = + if (dir.isDirectory) dir + else throw new FileConflictException(s"${base.path}/$clsName$suffix: ${dir.path} is not a directory", dir) + var dir = base + val pathParts = clsName.split("[./]").toList + for (part <- pathParts.init) dir = ensureDirectory(dir) subdirectoryNamed part + ensureDirectory(dir) fileNamed pathParts.last + suffix + } + } + + private def fastGetFile(base: AbstractFile, clsName: String, suffix: String) = { + val index = clsName.lastIndexOf('/') + val (packageName, simpleName) = if (index > 0) { + (clsName.substring(0, index), clsName.substring(index + 1)) + } else ("", clsName) + val directory = base.file.toPath.resolve(packageName) + new PlainFile(Path(directory.resolve(simpleName + suffix))) + } + + private def writeBytes(outFile: AbstractFile, bytes: Array[Byte]): Unit = { + if (outFile.file != null) { + val outPath = outFile.file.toPath + try Files.write(outPath, bytes) + catch { + case _: java.nio.file.NoSuchFileException => + Files.createDirectories(outPath.getParent) + Files.write(outPath, bytes) + } + } else { + val out = new DataOutputStream(outFile.bufferedOutput) + try out.write(bytes, 0, bytes.length) + finally out.close() + } + } + + def writeClass(className: InternalName, bytes: Array[Byte], sourceFile: AbstractFile): AbstractFile | Null = try { + // val writeStart = Statistics.startTimer(BackendStats.bcodeWriteTimer) + val outFile = writeToJarOrFile(className, bytes, ".class") + // Statistics.stopTimer(BackendStats.bcodeWriteTimer, writeStart) + + if (dumpOutputDir != null) { + val dumpFile = getFile(dumpOutputDir, className, ".class") + writeBytes(dumpFile, bytes) + } + outFile + } catch { + case e: FileConflictException => + backendReporting.error(em"error writing $className: ${e.getMessage}") + null + case e: java.nio.file.FileSystemException => + if compilerSettings.debug then e.printStackTrace() + backendReporting.error(em"error writing $className: ${e.getClass.getName} ${e.getMessage}") + null + } + + def writeTasty(className: InternalName, bytes: Array[Byte]): Unit = + writeToJarOrFile(className, bytes, ".tasty") + + private def writeToJarOrFile(className: InternalName, bytes: Array[Byte], suffix: String): AbstractFile | Null = { + if jarWriter == null then + val outFolder = compilerSettings.outputDirectory + val outFile = getFile(outFolder, className, suffix) + try writeBytes(outFile, bytes) + catch case ex: ClosedByInterruptException => + try outFile.delete() // don't leave an empty or half-written files around after an interrupt + catch case _: Throwable => () + finally throw ex + outFile + else + val path = className + suffix + val out = jarWriter.newOutputStream(path) + try out.write(bytes, 0, bytes.length) + finally out.flush() + null + } + + def close(): Unit = { + if (jarWriter != null) jarWriter.close() + } +} + +/** Can't output a file due to the state of the file system. */ +class FileConflictException(msg: String, val file: AbstractFile) extends IOException(msg) diff --git a/compiler/src/dotty/tools/backend/jvm/CodeGen.scala b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala new file mode 100644 index 000000000000..c9f9e4e23d90 --- /dev/null +++ b/compiler/src/dotty/tools/backend/jvm/CodeGen.scala @@ -0,0 +1,181 @@ +package dotty.tools.backend.jvm + +import scala.language.unsafeNulls + +import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.ast.Trees.{PackageDef, ValDef} +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Phases.Phase + +import scala.collection.mutable +import scala.jdk.CollectionConverters._ +import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.interfaces +import dotty.tools.dotc.report + +import java.util.Optional +import dotty.tools.dotc.sbt.ExtractDependencies +import dotty.tools.dotc.core._ +import Contexts._ +import Phases._ +import Symbols._ +import StdNames.nme + +import java.io.DataOutputStream +import java.nio.channels.ClosedByInterruptException + +import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler } + +import scala.tools.asm +import scala.tools.asm.tree._ +import tpd._ +import dotty.tools.io.AbstractFile +import dotty.tools.dotc.util.NoSourcePosition + + +class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)( val bTypes: BTypesFromSymbols[int.type]) { self => + import DottyBackendInterface.symExtensions + import bTypes._ + import int.given + + private lazy val mirrorCodeGen = Impl.JMirrorBuilder() + + def genUnit(unit: CompilationUnit): GeneratedDefs = { + val generatedClasses = mutable.ListBuffer.empty[GeneratedClass] + val generatedTasty = mutable.ListBuffer.empty[GeneratedTasty] + + def genClassDef(cd: TypeDef): Unit = + try + val sym = cd.symbol + val sourceFile = unit.source.file + + def registerGeneratedClass(classNode: ClassNode, isArtifact: Boolean): Unit = + generatedClasses += GeneratedClass(classNode, sourceFile, isArtifact, onFileCreated(classNode, sym, unit.source)) + + val plainC = genClass(cd, unit) + registerGeneratedClass(plainC, isArtifact = false) + + val attrNode = + if !sym.isTopLevelModuleClass then plainC + else if sym.companionClass == NoSymbol then + val mirrorC = genMirrorClass(sym, unit) + registerGeneratedClass(mirrorC, isArtifact = true) + mirrorC + else + report.log(s"No mirror class for module with linked class: ${sym.fullName}", NoSourcePosition) + plainC + + if sym.isClass then + genTastyAndSetAttributes(sym, attrNode) + catch + case ex: Throwable => + ex.printStackTrace() + report.error(s"Error while emitting ${unit.source}\n${ex.getMessage}", NoSourcePosition) + + + def genTastyAndSetAttributes(claszSymbol: Symbol, store: ClassNode): Unit = + import Impl.createJAttribute + for (binary <- unit.pickled.get(claszSymbol.asClass)) { + generatedTasty += GeneratedTasty(store, binary) + val tasty = + val uuid = new TastyHeaderUnpickler(binary()).readHeader() + val lo = uuid.getMostSignificantBits + val hi = uuid.getLeastSignificantBits + + // TASTY attribute is created but only the UUID bytes are stored in it. + // A TASTY attribute has length 16 if and only if the .tasty file exists. + val buffer = new TastyBuffer(16) + buffer.writeUncompressedLong(lo) + buffer.writeUncompressedLong(hi) + buffer.bytes + + val dataAttr = createJAttribute(nme.TASTYATTR.mangledString, tasty, 0, tasty.length) + store.visitAttribute(dataAttr) + } + + def genClassDefs(tree: Tree): Unit = + tree match { + case EmptyTree => () + case PackageDef(_, stats) => stats foreach genClassDefs + case ValDef(_, _, _) => () // module val not emitted + case td: TypeDef => genClassDef(td) + } + + genClassDefs(unit.tpdTree) + GeneratedDefs(generatedClasses.toList, generatedTasty.toList) + } + + // Creates a callback that will be evaluated in PostProcessor after creating a file + private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: interfaces.SourceFile): AbstractFile => Unit = clsFile => { + val (fullClassName, isLocal) = atPhase(sbtExtractDependenciesPhase) { + (ExtractDependencies.classNameAsString(claszSymbol), claszSymbol.isLocal) + } + + val className = cls.name.replace('/', '.') + if (ctx.compilerCallback != null) + ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(clsFile), className) + + if (ctx.sbtCallback != null) { + val jSourceFile = sourceFile.jfile.orElse(null) + val cb = ctx.sbtCallback + if (isLocal) cb.generatedLocalClass(jSourceFile, clsFile.file) + else cb.generatedNonLocalClass(jSourceFile, clsFile.file, className, fullClassName) + } + } + + /** Convert a `dotty.tools.io.AbstractFile` into a + * `dotty.tools.dotc.interfaces.AbstractFile`. + */ + private def convertAbstractFile(absfile: dotty.tools.io.AbstractFile): interfaces.AbstractFile = + new interfaces.AbstractFile { + override def name = absfile.name + override def path = absfile.path + override def jfile = Optional.ofNullable(absfile.file) + } + + private def genClass(cd: TypeDef, unit: CompilationUnit): ClassNode = { + val b = new Impl.PlainClassBuilder(unit) + b.genPlainClass(cd) + val cls = b.cnode + checkForCaseConflict(cls.name, cd.symbol) + cls + } + + private def genMirrorClass(classSym: Symbol, unit: CompilationUnit): ClassNode = { + val cls = mirrorCodeGen.genMirrorClass(classSym, unit) + checkForCaseConflict(cls.name, classSym) + cls + } + + private val lowerCaseNames = mutable.HashMap.empty[String, Symbol] + private def checkForCaseConflict(javaClassName: String, classSymbol: Symbol) = { + val lowerCaseName = javaClassName.toLowerCase + lowerCaseNames.get(lowerCaseName) match { + case None => + lowerCaseNames.put(lowerCaseName, classSymbol) + case Some(dupClassSym) => + // Order is not deterministic so we enforce lexicographic order between the duplicates for error-reporting + val (cl1, cl2) = + if (classSymbol.effectiveName.toString < dupClassSym.effectiveName.toString) (classSymbol, dupClassSym) + else (dupClassSym, classSymbol) + val same = classSymbol.effectiveName.toString == dupClassSym.effectiveName.toString + atPhase(typerPhase) { + if same then + // FIXME: This should really be an error, but then FromTasty tests fail + report.warning(s"${cl1.show} and ${cl2.showLocated} produce classes that overwrite one another", cl1.sourcePos) + else + report.warning(s"${cl1.show} differs only in case from ${cl2.showLocated}. " + + "Such classes will overwrite one another on case-insensitive filesystems.", cl1.sourcePos) + } + } + } + + sealed transparent trait ImplEarlyInit{ + val int: self.int.type = self.int + val bTypes: self.bTypes.type = self.bTypes + protected val primitives: DottyPrimitives = self.primitives + } + object Impl extends ImplEarlyInit with BCodeSyncAndTry { + class PlainClassBuilder(unit: CompilationUnit) extends SyncAndTryBuilder(unit) + } +} diff --git a/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala b/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala index d5fce3f53627..30ad6b29b9f0 100644 --- a/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala +++ b/compiler/src/dotty/tools/backend/jvm/CoreBTypes.scala @@ -7,38 +7,58 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.transform.Erasure import scala.tools.asm.{Handle, Opcodes} import dotty.tools.dotc.core.StdNames +import BTypes.InternalName + +abstract class CoreBTypes { + val bTypes: BTypes + import bTypes._ + + def primitiveTypeMap: Map[Symbol, PrimitiveBType] + + def boxedClasses: Set[ClassBType] + + def boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] + + def boxResultType: Map[Symbol, ClassBType] + + def unboxResultType: Map[Symbol, PrimitiveBType] + + def srNothingRef : ClassBType + def srNullRef : ClassBType + + def ObjectRef : ClassBType + def StringRef : ClassBType + def jlStringBuilderRef : ClassBType + def jlStringBufferRef : ClassBType + def jlCharSequenceRef : ClassBType + def jlClassRef : ClassBType + def jlThrowableRef : ClassBType + def jlCloneableRef : ClassBType + def jiSerializableRef : ClassBType + def jlClassCastExceptionRef : ClassBType + def jlIllegalArgExceptionRef : ClassBType + def jliSerializedLambdaRef : ClassBType + + def srBoxesRuntimeRef: ClassBType + + def jliLambdaMetaFactoryMetafactoryHandle : Handle + def jliLambdaMetaFactoryAltMetafactoryHandle : Handle + def jliLambdaDeserializeBootstrapHandle : Handle + def jliStringConcatFactoryMakeConcatWithConstantsHandle: Handle + + def asmBoxTo : Map[BType, MethodNameAndType] + def asmUnboxTo: Map[BType, MethodNameAndType] + + def typeOfArrayOp: Map[Int, BType] +} + +abstract class CoreBTypesFromSymbols[I <: DottyBackendInterface] extends CoreBTypes { + val bTypes: BTypesFromSymbols[I] -/** - * Core BTypes and some other definitions. The initialization of these definitions requies access - * to symbols / types (global). - * - * The symbols used to initialize the ClassBTypes may change from one compiler run to the next. To - * make sure the definitions are consistent with the symbols in the current run, the - * `intializeCoreBTypes` method in BTypesFromSymbols creates a new instance of CoreBTypes in each - * compiler run. - * - * The class BTypesFromSymbols does not directly reference CoreBTypes, but CoreBTypesProxy. The - * reason is that having a `var bTypes: CoreBTypes` would not allow `import bTypes._`. Instead, the - * proxy class holds a `CoreBTypes` in a variable field and forwards to this instance. - * - * The definitions in `CoreBTypes` need to be lazy vals to break an initialization cycle. When - * creating a new instance to assign to the proxy, the `classBTypeFromSymbol` invoked in the - * constructor will actucally go through the proxy. The lazy vals make sure the instance is assigned - * in the proxy before the fields are initialized. - * - * Note: if we did not re-create the core BTypes on each compiler run, BType.classBTypeFromInternalNameMap - * could not be a perRunCache anymore: the classes defeined here need to be in that map, they are - * added when the ClassBTypes are created. The per run cache removes them, so they would be missing - * in the second run. - */ -class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTypes: BTFS) { import bTypes._ import int.given import DottyBackendInterface._ - - //import global._ - //import rootMirror.{requiredClass, getClassIfDefined} - //import definitions._ + import dotty.tools.dotc.core.Contexts.Context /** * Maps primitive types to their corresponding PrimitiveBType. The map is defined lexically above @@ -56,31 +76,21 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp defn.DoubleClass -> DOUBLE ) - private lazy val BOXED_UNIT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Void]) - private lazy val BOXED_BOOLEAN : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Boolean]) - private lazy val BOXED_BYTE : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Byte]) - private lazy val BOXED_SHORT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Short]) - private lazy val BOXED_CHAR : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Character]) - private lazy val BOXED_INT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Integer]) - private lazy val BOXED_LONG : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Long]) - private lazy val BOXED_FLOAT : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Float]) - private lazy val BOXED_DOUBLE : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Double]) - /** * Map from primitive types to their boxed class type. Useful when pushing class literals onto the * operand stack (ldc instruction taking a class literal), see genConstant. */ lazy val boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = Map( - UNIT -> BOXED_UNIT, - BOOL -> BOXED_BOOLEAN, - BYTE -> BOXED_BYTE, - SHORT -> BOXED_SHORT, - CHAR -> BOXED_CHAR, - INT -> BOXED_INT, - LONG -> BOXED_LONG, - FLOAT -> BOXED_FLOAT, - DOUBLE -> BOXED_DOUBLE - ) + UNIT -> classBTypeFromSymbol(requiredClass[java.lang.Void]), + BOOL -> classBTypeFromSymbol(requiredClass[java.lang.Boolean]), + BYTE -> classBTypeFromSymbol(requiredClass[java.lang.Byte]), + SHORT -> classBTypeFromSymbol(requiredClass[java.lang.Short]), + CHAR -> classBTypeFromSymbol(requiredClass[java.lang.Character]), + INT -> classBTypeFromSymbol(requiredClass[java.lang.Integer]), + LONG -> classBTypeFromSymbol(requiredClass[java.lang.Long]), + FLOAT -> classBTypeFromSymbol(requiredClass[java.lang.Float]), + DOUBLE -> classBTypeFromSymbol(requiredClass[java.lang.Double]) + ) lazy val boxedClasses: Set[ClassBType] = boxedClassOfPrimitive.values.toSet @@ -114,33 +124,35 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp * names of NothingClass and NullClass can't be emitted as-is. * TODO @lry Once there's a 2.11.3 starr, use the commented argument list. The current starr crashes on the type literal `scala.runtime.Nothing$` */ - lazy val srNothingRef : ClassBType = classBTypeFromSymbol(requiredClass("scala.runtime.Nothing$")) // (requiredClass[scala.runtime.Nothing$]) - lazy val srNullRef : ClassBType = classBTypeFromSymbol(requiredClass("scala.runtime.Null$")) // (requiredClass[scala.runtime.Null$]) - - lazy val ObjectRef : ClassBType = classBTypeFromSymbol(defn.ObjectClass) - lazy val StringRef : ClassBType = classBTypeFromSymbol(defn.StringClass) - lazy val jlStringBuilderRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.StringBuilder]) - lazy val jlStringBufferRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.StringBuffer]) - lazy val jlCharSequenceRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.CharSequence]) - lazy val jlClassRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Class[_]]) - lazy val jlThrowableRef : ClassBType = classBTypeFromSymbol(defn.ThrowableClass) - lazy val jlCloneableRef : ClassBType = classBTypeFromSymbol(defn.JavaCloneableClass) // java/lang/Cloneable - lazy val jioSerializableRef : ClassBType = classBTypeFromSymbol(requiredClass[java.io.Serializable]) // java/io/Serializable - lazy val jlClassCastExceptionRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.ClassCastException]) // java/lang/ClassCastException - lazy val jlIllegalArgExceptionRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.IllegalArgumentException]) - lazy val jliSerializedLambdaRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda]) - - lazy val srBoxesRunTimeRef: ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime]) - - private lazy val jliCallSiteRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.CallSite]) - private lazy val jliLambdaMetafactoryRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory]) - private lazy val jliMethodHandleRef : ClassBType = classBTypeFromSymbol(defn.MethodHandleClass) - private lazy val jliMethodHandlesLookupRef : ClassBType = classBTypeFromSymbol(defn.MethodHandlesLookupClass) - private lazy val jliMethodTypeRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType]) - private lazy val jliStringConcatFactoryRef : ClassBType = classBTypeFromSymbol(requiredClass("java.lang.invoke.StringConcatFactory")) // since JDK 9 - private lazy val srLambdaDeserialize : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize]) - - lazy val jliLambdaMetaFactoryMetafactoryHandle: Handle = new Handle( + lazy val srNothingRef : ClassBType = classBTypeFromSymbol(requiredClass("scala.runtime.Nothing$")) + lazy val srNullRef : ClassBType = classBTypeFromSymbol(requiredClass("scala.runtime.Null$")) + + lazy val ObjectRef : ClassBType = classBTypeFromSymbol(defn.ObjectClass) + lazy val StringRef : ClassBType = classBTypeFromSymbol(defn.StringClass) + + lazy val jlStringBuilderRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.StringBuilder]) + lazy val jlStringBufferRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.StringBuffer]) + lazy val jlCharSequenceRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.CharSequence]) + lazy val jlClassRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.Class[_]]) + lazy val jlThrowableRef : ClassBType = classBTypeFromSymbol(defn.ThrowableClass) + lazy val jlCloneableRef : ClassBType = classBTypeFromSymbol(defn.JavaCloneableClass) + lazy val jiSerializableRef : ClassBType = classBTypeFromSymbol(requiredClass[java.io.Serializable]) + lazy val jlClassCastExceptionRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.ClassCastException]) + lazy val jlIllegalArgExceptionRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.IllegalArgumentException]) + lazy val jliSerializedLambdaRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.SerializedLambda]) + + lazy val srBoxesRuntimeRef: ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime]) + + private lazy val jliCallSiteRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.CallSite]) + private lazy val jliLambdaMetafactoryRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory]) + private lazy val jliMethodHandleRef : ClassBType = classBTypeFromSymbol(defn.MethodHandleClass) + private lazy val jliMethodHandlesLookupRef : ClassBType = classBTypeFromSymbol(defn.MethodHandlesLookupClass) + private lazy val jliMethodTypeRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType]) + private lazy val jliStringConcatFactoryRef : ClassBType = classBTypeFromSymbol(requiredClass("java.lang.invoke.StringConcatFactory")) // since JDK 9 + + lazy val srLambdaDeserialize : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize]) + + lazy val jliLambdaMetaFactoryMetafactoryHandle = new Handle( Opcodes.H_INVOKESTATIC, jliLambdaMetafactoryRef.internalName, "metafactory", @@ -150,7 +162,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp ).descriptor, /* itf = */ false) - lazy val jliLambdaMetaFactoryAltMetafactoryHandle: Handle = new Handle( + lazy val jliLambdaMetaFactoryAltMetafactoryHandle = new Handle( Opcodes.H_INVOKESTATIC, jliLambdaMetafactoryRef.internalName, "altMetafactory", @@ -159,7 +171,7 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp jliCallSiteRef ).descriptor, /* itf = */ false) - + lazy val jliLambdaDeserializeBootstrapHandle: Handle = new Handle( Opcodes.H_INVOKESTATIC, srLambdaDeserialize.internalName, @@ -179,19 +191,19 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp jliCallSiteRef ).descriptor, /* itf = */ false) - + /** * Methods in scala.runtime.BoxesRuntime */ lazy val asmBoxTo : Map[BType, MethodNameAndType] = Map( - BOOL -> MethodNameAndType("boxToBoolean", MethodBType(List(BOOL), BOXED_BOOLEAN)), - BYTE -> MethodNameAndType("boxToByte", MethodBType(List(BYTE), BOXED_BYTE)), - CHAR -> MethodNameAndType("boxToCharacter", MethodBType(List(CHAR), BOXED_CHAR)), - SHORT -> MethodNameAndType("boxToShort", MethodBType(List(SHORT), BOXED_SHORT)), - INT -> MethodNameAndType("boxToInteger", MethodBType(List(INT), BOXED_INT)), - LONG -> MethodNameAndType("boxToLong", MethodBType(List(LONG), BOXED_LONG)), - FLOAT -> MethodNameAndType("boxToFloat", MethodBType(List(FLOAT), BOXED_FLOAT)), - DOUBLE -> MethodNameAndType("boxToDouble", MethodBType(List(DOUBLE), BOXED_DOUBLE)) + BOOL -> MethodNameAndType("boxToBoolean", MethodBType(List(BOOL), boxedClassOfPrimitive(BOOL))), + BYTE -> MethodNameAndType("boxToByte", MethodBType(List(BYTE), boxedClassOfPrimitive(BYTE))), + CHAR -> MethodNameAndType("boxToCharacter", MethodBType(List(CHAR), boxedClassOfPrimitive(CHAR))), + SHORT -> MethodNameAndType("boxToShort", MethodBType(List(SHORT), boxedClassOfPrimitive(SHORT))), + INT -> MethodNameAndType("boxToInteger", MethodBType(List(INT), boxedClassOfPrimitive(INT))), + LONG -> MethodNameAndType("boxToLong", MethodBType(List(LONG), boxedClassOfPrimitive(LONG))), + FLOAT -> MethodNameAndType("boxToFloat", MethodBType(List(FLOAT), boxedClassOfPrimitive(FLOAT))), + DOUBLE -> MethodNameAndType("boxToDouble", MethodBType(List(DOUBLE), boxedClassOfPrimitive(DOUBLE))) ) lazy val asmUnboxTo: Map[BType, MethodNameAndType] = Map( @@ -220,75 +232,3 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTyp ) } } - -/** - * This trait make some core BTypes availalbe that don't depend on a Global instance. Some core - * BTypes are required to be accessible in the BTypes trait, which does not have access to Global. - * - * BTypes cannot refer to CoreBTypesProxy because some of its members depend on global, for example - * the type Symbol in - * def primitiveTypeMap: Map[Symbol, PrimitiveBType] - */ -trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] { - val bTypes: BTS - import bTypes._ - - def boxedClasses: Set[ClassBType] - - def srNothingRef : ClassBType - def srNullRef : ClassBType - - def ObjectRef : ClassBType - def jlCloneableRef : ClassBType - def jiSerializableRef : ClassBType -} - -/** - * See comment in class [[CoreBTypes]]. - */ -final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: DottyBackendInterface]](val bTypes: BTFS) extends CoreBTypesProxyGlobalIndependent[BTFS] { - import bTypes._ - - private var _coreBTypes: CoreBTypes[bTypes.type] = _ - def setBTypes(coreBTypes: CoreBTypes[BTFS]): Unit = { - _coreBTypes = coreBTypes.asInstanceOf[CoreBTypes[bTypes.type]] - } - - def primitiveTypeMap: Map[Symbol, PrimitiveBType] = _coreBTypes.primitiveTypeMap - - def boxedClasses: Set[ClassBType] = _coreBTypes.boxedClasses - - def boxedClassOfPrimitive: Map[PrimitiveBType, ClassBType] = _coreBTypes.boxedClassOfPrimitive - - def boxResultType: Map[Symbol, ClassBType] = _coreBTypes.boxResultType - - def unboxResultType: Map[Symbol, PrimitiveBType] = _coreBTypes.unboxResultType - - def srNothingRef : ClassBType = _coreBTypes.srNothingRef - def srNullRef : ClassBType = _coreBTypes.srNullRef - - def ObjectRef : ClassBType = _coreBTypes.ObjectRef - def StringRef : ClassBType = _coreBTypes.StringRef - def jlStringBuilderRef : ClassBType = _coreBTypes.jlStringBuilderRef - def jlStringBufferRef : ClassBType = _coreBTypes.jlStringBufferRef - def jlCharSequenceRef : ClassBType = _coreBTypes.jlCharSequenceRef - def jlClassRef : ClassBType = _coreBTypes.jlClassRef - def jlThrowableRef : ClassBType = _coreBTypes.jlThrowableRef - def jlCloneableRef : ClassBType = _coreBTypes.jlCloneableRef - def jiSerializableRef : ClassBType = _coreBTypes.jioSerializableRef - def jlClassCastExceptionRef : ClassBType = _coreBTypes.jlClassCastExceptionRef - def jlIllegalArgExceptionRef : ClassBType = _coreBTypes.jlIllegalArgExceptionRef - def jliSerializedLambdaRef : ClassBType = _coreBTypes.jliSerializedLambdaRef - - def srBoxesRuntimeRef: ClassBType = _coreBTypes.srBoxesRunTimeRef - - def jliLambdaMetaFactoryMetafactoryHandle : Handle = _coreBTypes.jliLambdaMetaFactoryMetafactoryHandle - def jliLambdaMetaFactoryAltMetafactoryHandle : Handle = _coreBTypes.jliLambdaMetaFactoryAltMetafactoryHandle - def jliLambdaDeserializeBootstrapHandle : Handle = _coreBTypes.jliLambdaDeserializeBootstrapHandle - def jliStringConcatFactoryMakeConcatWithConstantsHandle: Handle = _coreBTypes.jliStringConcatFactoryMakeConcatWithConstantsHandle - - def asmBoxTo : Map[BType, MethodNameAndType] = _coreBTypes.asmBoxTo - def asmUnboxTo: Map[BType, MethodNameAndType] = _coreBTypes.asmUnboxTo - - def typeOfArrayOp: Map[Int, BType] = _coreBTypes.typeOfArrayOp -} diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index f8f683a429f6..b2278c3f0ce8 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -25,7 +25,7 @@ import StdNames.nme import NameKinds.{LazyBitMapName, LazyLocalName} import Names.Name -class DottyBackendInterface(val outputDirectory: AbstractFile, val superCallsMap: ReadOnlyMap[Symbol, Set[ClassSymbol]])(using val ctx: Context) { +class DottyBackendInterface(val superCallsMap: ReadOnlyMap[Symbol, Set[ClassSymbol]])(using val ctx: Context) { private val desugared = new java.util.IdentityHashMap[Type, tpd.Select] diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index e788c2b2a4ec..469a6ea57679 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -1,43 +1,16 @@ package dotty.tools.backend.jvm -import scala.language.unsafeNulls - import dotty.tools.dotc.CompilationUnit -import dotty.tools.dotc.ast.Trees.{PackageDef, ValDef} -import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Phases.Phase - -import scala.collection.mutable -import scala.jdk.CollectionConverters._ -import dotty.tools.dotc.transform.SymUtils._ -import dotty.tools.dotc.interfaces import dotty.tools.dotc.report - -import dotty.tools.dotc.util.SourceFile -import java.util.Optional - import dotty.tools.dotc.core._ -import dotty.tools.dotc.sbt.ExtractDependencies +import dotty.tools.dotc.interfaces.CompilerCallback import Contexts._ -import Phases._ import Symbols._ -import Decorators.em - -import java.io.DataOutputStream -import java.nio.channels.ClosedByInterruptException - -import dotty.tools.tasty.{ TastyBuffer, TastyHeaderUnpickler } - -import scala.tools.asm -import scala.tools.asm.Handle -import scala.tools.asm.tree._ -import tpd._ -import StdNames._ import dotty.tools.io._ -import scala.tools.asm.MethodTooLargeException -import scala.tools.asm.ClassTooLargeException +import scala.collection.mutable -class GenBCode extends Phase { +class GenBCode extends Phase { self => override def phaseName: String = GenBCode.name @@ -52,618 +25,85 @@ class GenBCode extends Phase { private val entryPoints = new mutable.HashSet[String]() def registerEntryPoint(s: String): Unit = entryPoints += s - private var myOutput: AbstractFile = _ - - private def outputDir(using Context): AbstractFile = { - if (myOutput eq null) - myOutput = ctx.settings.outputDir.value - myOutput + private var _backendInterface: DottyBackendInterface = _ + def backendInterface(using ctx: Context): DottyBackendInterface = { + if _backendInterface eq null then + // Enforce usage of FreshContext so we would be able to modify compilation unit between runs + val backendCtx = ctx match + case fc: FreshContext => fc + case ctx => ctx.fresh + _backendInterface = DottyBackendInterface(superCallsMap)(using backendCtx) + _backendInterface } - private var myPrimitives: DottyPrimitives = null - - override def run(using Context): Unit = - if myPrimitives == null then myPrimitives = new DottyPrimitives(ctx) - new GenBCodePipeline( - DottyBackendInterface(outputDir, superCallsMap), - myPrimitives - ).run(ctx.compilationUnit.tpdTree) - - - override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] = { - outputDir match - case jar: JarArchive => - updateJarManifestWithMainClass(jar, entryPoints.toList) - case _ => - try super.runOn(units) - finally outputDir match { - case jar: JarArchive => - if (ctx.run.nn.suspendedUnits.nonEmpty) - // If we close the jar the next run will not be able to write on the jar. - // But if we do not close it we cannot use it as part of the macro classpath of the suspended files. - report.error("Can not suspend and output to a jar at the same time. See suspension with -Xprint-suspension.") - - jar.close() - case _ => - } + private var _codeGen: CodeGen = _ + def codeGen(using Context): CodeGen = { + if _codeGen eq null then + val int = backendInterface + val dottyPrimitives = new DottyPrimitives(ctx) + _codeGen = new CodeGen(int, dottyPrimitives)(bTypes.asInstanceOf[BTypesFromSymbols[int.type]]) + _codeGen } - private def updateJarManifestWithMainClass(jarArchive: JarArchive, entryPoints: List[String])(using Context): Unit = - val mainClass = Option.when(!ctx.settings.XmainClass.isDefault)(ctx.settings.XmainClass.value).orElse { - entryPoints match - case List(mainClass) => - Some(mainClass) - case Nil => - report.warning("No Main-Class designated or discovered.") - None - case mcs => - report.warning(s"No Main-Class due to multiple entry points:\n ${mcs.mkString("\n ")}") - None - } - mainClass.map { mc => - val manifest = Jar.WManifest() - manifest.mainClass = mc - val file = jarArchive.subdirectoryNamed("META-INF").fileNamed("MANIFEST.MF") - val os = file.output - manifest.underlying.write(os) - os.close() - } - end updateJarManifestWithMainClass -} - -object GenBCode { - val name: String = "genBCode" - val description: String = "generate JVM bytecode" -} - -class GenBCodePipeline(val int: DottyBackendInterface, val primitives: DottyPrimitives)(using Context) extends BCodeSyncAndTry { - import DottyBackendInterface.symExtensions - - private var tree: Tree = _ - - private val sourceFile: SourceFile = ctx.compilationUnit.source - - /** Convert a `dotty.tools.io.AbstractFile` into a - * `dotty.tools.dotc.interfaces.AbstractFile`. - */ - private def convertAbstractFile(absfile: dotty.tools.io.AbstractFile): interfaces.AbstractFile = - new interfaces.AbstractFile { - override def name = absfile.name - override def path = absfile.path - override def jfile = Optional.ofNullable(absfile.file) - } - - final class PlainClassBuilder(cunit: CompilationUnit) extends SyncAndTryBuilder(cunit) - -// class BCodePhase() { - - private var bytecodeWriter : BytecodeWriter = null - private var mirrorCodeGen : JMirrorBuilder = null - - /* ---------------- q1 ---------------- */ - - case class Item1(arrivalPos: Int, cd: TypeDef, cunit: CompilationUnit) { - def isPoison: Boolean = { arrivalPos == Int.MaxValue } + private var _bTypes: BTypesFromSymbols[DottyBackendInterface] = _ + def bTypes(using Context): BTypesFromSymbols[DottyBackendInterface] = { + if _bTypes eq null then + _bTypes = BTypesFromSymbols(backendInterface, frontendAccess) + _bTypes } - private val poison1 = Item1(Int.MaxValue, null, ctx.compilationUnit) - private val q1 = new java.util.LinkedList[Item1] - /* ---------------- q2 ---------------- */ - - case class SubItem2(classNode: asm.tree.ClassNode, - file: dotty.tools.io.AbstractFile) - - case class Item2(arrivalPos: Int, - mirror: SubItem2, - plain: SubItem2) { - def isPoison: Boolean = { arrivalPos == Int.MaxValue } + private var _frontendAccess: PostProcessorFrontendAccess | Null = _ + def frontendAccess(using Context): PostProcessorFrontendAccess = { + if _frontendAccess eq null then + _frontendAccess = PostProcessorFrontendAccess.Impl(backendInterface, entryPoints) + _frontendAccess.nn } - private val poison2 = Item2(Int.MaxValue, null, null) - private val q2 = new _root_.java.util.LinkedList[Item2] - - /* ---------------- q3 ---------------- */ - - /* - * An item of queue-3 (the last queue before serializing to disk) contains three of these - * (one for each of mirror and plain classes). - * - * @param jclassName internal name of the class - * @param jclassBytes bytecode emitted for the class SubItem3 represents - */ - case class SubItem3( - jclassName: String, - jclassBytes: Array[Byte], - jclassFile: dotty.tools.io.AbstractFile - ) - - case class Item3(arrivalPos: Int, - mirror: SubItem3, - plain: SubItem3) { - - def isPoison: Boolean = { arrivalPos == Int.MaxValue } - } - private val i3comparator = new java.util.Comparator[Item3] { - override def compare(a: Item3, b: Item3) = { - if (a.arrivalPos < b.arrivalPos) -1 - else if (a.arrivalPos == b.arrivalPos) 0 - else 1 - } + private var _postProcessor: PostProcessor | Null = _ + def postProcessor(using Context): PostProcessor = { + if _postProcessor eq null then + _postProcessor = new PostProcessor(frontendAccess, bTypes) + _postProcessor.nn } - private val poison3 = Item3(Int.MaxValue, null, null) - private val q3 = new java.util.PriorityQueue[Item3](1000, i3comparator) - - /* - * Pipeline that takes ClassDefs from queue-1, lowers them into an intermediate form, placing them on queue-2 - */ - class Worker1(needsOutFolder: Boolean) { - - private val lowerCaseNames = mutable.HashMap.empty[String, Symbol] - private def checkForCaseConflict(javaClassName: String, classSymbol: Symbol) = { - val lowerCaseName = javaClassName.toLowerCase - lowerCaseNames.get(lowerCaseName) match { - case None => - lowerCaseNames.put(lowerCaseName, classSymbol) - case Some(dupClassSym) => - // Order is not deterministic so we enforce lexicographic order between the duplicates for error-reporting - val (cl1, cl2) = - if (classSymbol.effectiveName.toString < dupClassSym.effectiveName.toString) (classSymbol, dupClassSym) - else (dupClassSym, classSymbol) - val same = classSymbol.effectiveName.toString == dupClassSym.effectiveName.toString - atPhase(typerPhase) { - if (same) - report.warning( // FIXME: This should really be an error, but then FromTasty tests fail - s"${cl1.show} and ${cl2.showLocated} produce classes that overwrite one another", cl1.sourcePos) - else - report.warning(s"${cl1.show} differs only in case from ${cl2.showLocated}. " + - "Such classes will overwrite one another on case-insensitive filesystems.", cl1.sourcePos) - } - } - } - - def run(): Unit = { - while (true) { - val item = q1.poll - if (item.isPoison) { - q2 add poison2 - return - } - else { - try { /*withCurrentUnit(item.cunit)*/(visit(item)) } - catch { - case ex: InterruptedException => - throw ex - case ex: Throwable => - println(s"Error while emitting ${item.cunit.source.file.name}") - throw ex - } - } - } - } - - /* - * Checks for duplicate internal names case-insensitively, - * builds ASM ClassNodes for mirror and plain classes; - * enqueues them in queue-2. - * - */ - def visit(item: Item1): Boolean = { - val Item1(arrivalPos, cd, cunit) = item - val claszSymbol = cd.symbol - - // -------------- mirror class, if needed -------------- - val mirrorC = - if (claszSymbol.isTopLevelModuleClass) { - if (claszSymbol.companionClass == NoSymbol) { - mirrorCodeGen.genMirrorClass(claszSymbol, cunit) - } else { - report.log(s"No mirror class for module with linked class: ${claszSymbol.showFullName}") - null - } - } else null - - // -------------- "plain" class -------------- - val pcb = new PlainClassBuilder(cunit) - pcb.genPlainClass(cd) - val outF = if (needsOutFolder) getOutFolder(claszSymbol, pcb.thisName) else null; - val plainC = pcb.cnode - - if (claszSymbol.isClass) // @DarkDimius is this test needed here? - for (binary <- ctx.compilationUnit.pickled.get(claszSymbol.asClass)) { - val store = if (mirrorC ne null) mirrorC else plainC - val tasty = - val outTastyFile = getFileForClassfile(outF, store.name, ".tasty") - val outstream = new DataOutputStream(outTastyFile.bufferedOutput) - try outstream.write(binary()) - catch case ex: ClosedByInterruptException => - try - outTastyFile.delete() // don't leave an empty or half-written tastyfile around after an interrupt - catch - case _: Throwable => - throw ex - finally outstream.close() - - val uuid = new TastyHeaderUnpickler(binary()).readHeader() - val lo = uuid.getMostSignificantBits - val hi = uuid.getLeastSignificantBits - - // TASTY attribute is created but only the UUID bytes are stored in it. - // A TASTY attribute has length 16 if and only if the .tasty file exists. - val buffer = new TastyBuffer(16) - buffer.writeUncompressedLong(lo) - buffer.writeUncompressedLong(hi) - buffer.bytes - - val dataAttr = createJAttribute(nme.TASTYATTR.mangledString, tasty, 0, tasty.length) - store.visitAttribute(dataAttr) - } - - - // ----------- create files - - val classNodes = List(mirrorC, plainC) - val classFiles = classNodes.map(cls => - if (outF != null && cls != null) { - try { - checkForCaseConflict(cls.name, claszSymbol) - getFileForClassfile(outF, cls.name, ".class") - } catch { - case e: FileConflictException => - report.error(em"error writing ${cls.name}: ${e.getMessage}") - null - } - } else null - ) - - // ----------- compiler and sbt's callbacks - - val (fullClassName, isLocal) = atPhase(sbtExtractDependenciesPhase) { - (ExtractDependencies.classNameAsString(claszSymbol), claszSymbol.isLocal) - } - - for ((cls, clsFile) <- classNodes.zip(classFiles)) { - if (cls != null) { - val className = cls.name.replace('/', '.') - if (ctx.compilerCallback != null) - ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(clsFile), className) - if (ctx.sbtCallback != null) { - if (isLocal) - ctx.sbtCallback.generatedLocalClass(sourceFile.jfile.orElse(null), clsFile.file) - else { - ctx.sbtCallback.generatedNonLocalClass(sourceFile.jfile.orElse(null), clsFile.file, - className, fullClassName) - } - } - } - } - - // ----------- hand over to pipeline-2 - - val item2 = - Item2(arrivalPos, - SubItem2(mirrorC, classFiles(0)), - SubItem2(plainC, classFiles(1))) - - q2 add item2 // at the very end of this method so that no Worker2 thread starts mutating before we're done. - } // end of method visit(Item1) - - } // end of class BCodePhase.Worker1 - - /* - * Pipeline that takes ClassNodes from queue-2. The unit of work depends on the optimization level: - * - * (a) no optimization involves: - * - converting the plain ClassNode to byte array and placing it on queue-3 - */ - class Worker2 { - import bTypes.ClassBType - import bTypes.coreBTypes.jliLambdaMetaFactoryAltMetafactoryHandle - // lazy val localOpt = new LocalOpt(new Settings()) - - private def localOptimizations(classNode: ClassNode): Unit = { - // BackendStats.timed(BackendStats.methodOptTimer)(localOpt.methodOptimizations(classNode)) - } - - - /* Return an array of all serializable lambdas in this class */ - private def collectSerializableLambdas(classNode: ClassNode): Array[Handle] = { - val indyLambdaBodyMethods = new mutable.ArrayBuffer[Handle] - for (m <- classNode.methods.asScala) { - val iter = m.instructions.iterator - while (iter.hasNext) { - val insn = iter.next() - insn match { - case indy: InvokeDynamicInsnNode - if indy.bsm == jliLambdaMetaFactoryAltMetafactoryHandle => - import java.lang.invoke.LambdaMetafactory.FLAG_SERIALIZABLE - val metafactoryFlags = indy.bsmArgs(3).asInstanceOf[Integer].toInt - val isSerializable = (metafactoryFlags & FLAG_SERIALIZABLE) != 0 - if isSerializable then - val implMethod = indy.bsmArgs(1).asInstanceOf[Handle] - indyLambdaBodyMethods += implMethod - case _ => - } - } - } - indyLambdaBodyMethods.toArray - } - - /* - * Add: - * - * private static Object $deserializeLambda$(SerializedLambda l) { - * try return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup$0](l) - * catch { - * case i: IllegalArgumentException => - * try return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup$1](l) - * catch { - * case i: IllegalArgumentException => - * ... - * return indy[scala.runtime.LambdaDeserialize.bootstrap, targetMethodGroup${NUM_GROUPS-1}](l) - * } - * - * We use invokedynamic here to enable caching within the deserializer without needing to - * host a static field in the enclosing class. This allows us to add this method to interfaces - * that define lambdas in default methods. - * - * SI-10232 we can't pass arbitrary number of method handles to the final varargs parameter of the bootstrap - * method due to a limitation in the JVM. Instead, we emit a separate invokedynamic bytecode for each group of target - * methods. - */ - private def addLambdaDeserialize(classNode: ClassNode, implMethodsArray: Array[Handle]): Unit = { - import asm.Opcodes._ - import bTypes._ - import coreBTypes._ - - val cw = classNode - - // Make sure to reference the ClassBTypes of all types that are used in the code generated - // here (e.g. java/util/Map) are initialized. Initializing a ClassBType adds it to - // `classBTypeFromInternalNameMap`. When writing the classfile, the asm ClassWriter computes - // stack map frames and invokes the `getCommonSuperClass` method. This method expects all - // ClassBTypes mentioned in the source code to exist in the map. - - val serlamObjDesc = MethodBType(jliSerializedLambdaRef :: Nil, ObjectRef).descriptor - - val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", serlamObjDesc, null, null) - def emitLambdaDeserializeIndy(targetMethods: Seq[Handle]): Unit = { - mv.visitVarInsn(ALOAD, 0) - mv.visitInvokeDynamicInsn("lambdaDeserialize", serlamObjDesc, jliLambdaDeserializeBootstrapHandle, targetMethods: _*) - } - - val targetMethodGroupLimit = 255 - 1 - 3 // JVM limit. See See MAX_MH_ARITY in CallSite.java - val groups: Array[Array[Handle]] = implMethodsArray.grouped(targetMethodGroupLimit).toArray - val numGroups = groups.length - - import scala.tools.asm.Label - val initialLabels = Array.fill(numGroups - 1)(new Label()) - val terminalLabel = new Label - def nextLabel(i: Int) = if (i == numGroups - 2) terminalLabel else initialLabels(i + 1) - - for ((label, i) <- initialLabels.iterator.zipWithIndex) { - mv.visitTryCatchBlock(label, nextLabel(i), nextLabel(i), jlIllegalArgExceptionRef.internalName) - } - for ((label, i) <- initialLabels.iterator.zipWithIndex) { - mv.visitLabel(label) - emitLambdaDeserializeIndy(groups(i).toIndexedSeq) - mv.visitInsn(ARETURN) - } - mv.visitLabel(terminalLabel) - emitLambdaDeserializeIndy(groups(numGroups - 1).toIndexedSeq) - mv.visitInsn(ARETURN) - } - - private def setInnerClasses(classNode: ClassNode): Unit = if (classNode != null) { - classNode.innerClasses.clear() - val (declared, referred) = collectNestedClasses(classNode) - addInnerClasses(classNode, declared, referred) - } - - /** - * Visit the class node and collect all referenced nested classes. - */ - private def collectNestedClasses(classNode: ClassNode): (List[ClassBType], List[ClassBType]) = { - // type InternalName = String - val c = new NestedClassesCollector[ClassBType](nestedOnly = true) { - def declaredNestedClasses(internalName: InternalName): List[ClassBType] = - bTypes.classBTypeFromInternalName(internalName).info.memberClasses - - def getClassIfNested(internalName: InternalName): Option[ClassBType] = { - val c = bTypes.classBTypeFromInternalName(internalName) - Option.when(c.isNestedClass)(c) - } - - def raiseError(msg: String, sig: String, e: Option[Throwable]): Unit = { - // don't crash on invalid generic signatures - } - } - c.visit(classNode) - (c.declaredInnerClasses.toList, c.referredInnerClasses.toList) - } - - def run(): Unit = { - while (true) { - val item = q2.poll - if (item.isPoison) { - q3 add poison3 - return - } - else { - try { - val plainNode = item.plain.classNode - localOptimizations(plainNode) - val serializableLambdas = collectSerializableLambdas(plainNode) - if (serializableLambdas.nonEmpty) - addLambdaDeserialize(plainNode, serializableLambdas) - setInnerClasses(plainNode) - setInnerClasses(item.mirror.classNode) - addToQ3(item) - } catch { - case ex: InterruptedException => - throw ex - case ex: Throwable => - println(s"Error while emitting ${item.plain.classNode.name}") - throw ex - } + override def run(using ctx: Context): Unit = + // CompilationUnit is the only component that will differ between each run invocation + // We need to update it to have correct source positions. + // FreshContext is always enforced when creating backend interface + backendInterface.ctx + .asInstanceOf[FreshContext] + .setCompilationUnit(ctx.compilationUnit) + val generated = codeGen.genUnit(ctx.compilationUnit) + // In Scala 2, the backend might use global optimizations which might delay post-processing to build the call graph. + // In Scala 3, we don't perform backend optimizations and always perform post-processing immediately. + // https://github.com/scala/scala/pull/6057 + postProcessor.postProcessAndSendToDisk(generated) + (ctx.compilerCallback: CompilerCallback | Null) match { + case cb: CompilerCallback => cb.onSourceCompiled(ctx.source) + case null => () + } + + override def runOn(units: List[CompilationUnit])(using ctx:Context): List[CompilationUnit] = { + try super.runOn(units) + finally + // frontendAccess and postProcessor are created lazilly, clean them up only if they were initialized + if _frontendAccess ne null then + frontendAccess.compilerSettings.outputDirectory match { + case jar: JarArchive => + if (ctx.run.nn.suspendedUnits.nonEmpty) + // If we close the jar the next run will not be able to write on the jar. + // But if we do not close it we cannot use it as part of the macro classpath of the suspended files. + report.error("Can not suspend and output to a jar at the same time. See suspension with -Xprint-suspension.") + + jar.close() + case _ => () } - } - } - - private def addToQ3(item: Item2) = { - - def getByteArray(cn: asm.tree.ClassNode): Array[Byte] = { - val cw = new CClassWriter(extraProc) - cn.accept(cw) - cw.toByteArray - } - - val Item2(arrivalPos, SubItem2(mirror, mirrorFile), SubItem2(plain, plainFile)) = item - - val mirrorC = if (mirror == null) null else SubItem3(mirror.name, getByteArray(mirror), mirrorFile) - val plainC = SubItem3(plain.name, getByteArray(plain), plainFile) - - if (AsmUtils.traceSerializedClassEnabled && plain.name.contains(AsmUtils.traceSerializedClassPattern)) { - if (mirrorC != null) AsmUtils.traceClass(mirrorC.jclassBytes) - AsmUtils.traceClass(plainC.jclassBytes) - } - - q3 add Item3(arrivalPos, mirrorC, plainC) - } - - } // end of class BCodePhase.Worker2 - - var arrivalPos: Int = 0 - - /* - * A run of the BCodePhase phase comprises: - * - * (a) set-up steps (most notably supporting maps in `BCodeTypes`, - * but also "the" writer where class files in byte-array form go) - * - * (b) building of ASM ClassNodes, their optimization and serialization. - * - * (c) tear down (closing the classfile-writer and clearing maps) - * - */ - def run(t: Tree)(using Context): Unit = { - this.tree = t - - // val bcodeStart = Statistics.startTimer(BackendStats.bcodeTimer) - - // val initStart = Statistics.startTimer(BackendStats.bcodeInitTimer) - arrivalPos = 0 // just in case - // scalaPrimitives.init() - bTypes.intializeCoreBTypes() - // Statistics.stopTimer(BackendStats.bcodeInitTimer, initStart) - - // initBytecodeWriter invokes fullName, thus we have to run it before the typer-dependent thread is activated. - bytecodeWriter = initBytecodeWriter() - mirrorCodeGen = new JMirrorBuilder - - val needsOutfileForSymbol = bytecodeWriter.isInstanceOf[ClassBytecodeWriter] - buildAndSendToDisk(needsOutfileForSymbol) - - // closing output files. - bytecodeWriter.close() - // Statistics.stopTimer(BackendStats.bcodeTimer, bcodeStart) - - if (ctx.compilerCallback != null) - ctx.compilerCallback.onSourceCompiled(sourceFile) - - /* TODO Bytecode can be verified (now that all classfiles have been written to disk) - * - * (1) asm.util.CheckAdapter.verify() - * public static void verify(ClassReader cr, ClassLoader loader, boolean dump, PrintWriter pw) - * passing a custom ClassLoader to verify inter-dependent classes. - * Alternatively, - * - an offline-bytecode verifier could be used (e.g. Maxine brings one as separate tool). - * - -Xverify:all - * - * (2) if requested, check-java-signatures, over and beyond the syntactic checks in `getGenericSignature()` - * - */ + if _postProcessor ne null then + postProcessor.classfileWriter.close() } +} - /* - * Sequentially: - * (a) place all ClassDefs in queue-1 - * (b) dequeue one at a time from queue-1, convert it to ASM ClassNode, place in queue-2 - * (c) dequeue one at a time from queue-2, convert it to byte-array, place in queue-3 - * (d) serialize to disk by draining queue-3. - */ - private def buildAndSendToDisk(needsOutFolder: Boolean)(using Context) = { - try - feedPipeline1() - // val genStart = Statistics.startTimer(BackendStats.bcodeGenStat) - (new Worker1(needsOutFolder)).run() - // Statistics.stopTimer(BackendStats.bcodeGenStat, genStart) - - (new Worker2).run() - - // val writeStart = Statistics.startTimer(BackendStats.bcodeWriteTimer) - drainQ3() - // Statistics.stopTimer(BackendStats.bcodeWriteTimer, writeStart) - catch - case e: MethodTooLargeException => - val method = - s"${e.getClassName.replaceAll("/", ".")}.${e.getMethodName}" - val msg = - em"Generated bytecode for method '$method' is too large. Size: ${e.getCodeSize} bytes. Limit is 64KB" - report.error(msg) - case e: ClassTooLargeException => - val msg = - em"Class '${e.getClassName.replaceAll("/", ".")}' is too large. Constant pool size: ${e.getConstantPoolCount}. Limit is 64K entries" - report.error(msg) - - } - - /* Feed pipeline-1: place all ClassDefs on q1, recording their arrival position. */ - private def feedPipeline1() = { - def gen(tree: Tree): Unit = { - tree match { - case EmptyTree => () - case PackageDef(_, stats) => stats foreach gen - case ValDef(name, tpt, rhs) => () // module val not emitted - case cd: TypeDef => - q1 add Item1(arrivalPos, cd, int.ctx.compilationUnit) - arrivalPos += 1 - } - } - gen(tree) - q1 add poison1 - } - - /* Pipeline that writes classfile representations to disk. */ - private def drainQ3() = { - - def sendToDisk(cfr: SubItem3): Unit = { - if (cfr != null){ - val SubItem3(jclassName, jclassBytes, jclassFile) = cfr - bytecodeWriter.writeClass(jclassName, jclassName, jclassBytes, jclassFile) - } - } - - var moreComing = true - // `expected` denotes the arrivalPos whose Item3 should be serialized next - var expected = 0 - - while (moreComing) { - val incoming = q3.poll - moreComing = !incoming.isPoison - if (moreComing) { - val item = incoming - sendToDisk(item.mirror) - sendToDisk(item.plain) - expected += 1 - } - } - - // we're done - assert(q1.isEmpty, s"Some ClassDefs remained in the first queue: $q1") - assert(q2.isEmpty, s"Some classfiles remained in the second queue: $q2") - assert(q3.isEmpty, s"Some classfiles weren't written to disk: $q3") - - } - //} // end of class BCodePhase +object GenBCode { + val name: String = "genBCode" + val description: String = "generate JVM bytecode" } diff --git a/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala b/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala index e9e532933290..c16bc70fc3b0 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenericSignatureVisitor.scala @@ -185,13 +185,13 @@ abstract class GenericSignatureVisitor(nestedOnly: Boolean) { } // Backported from scala/scala, commit sha: 724be0e9425b9ad07c244d25efdad695d75abbcf -// https://github.com/scala/scala/blob/724be0e9425b9ad07c244d25efdad695d75abbcf/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala#L790 +// https://github.com/scala/scala/blob/724be0e9425b9ad07c244d25efdad695d75abbcf/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala#L790 abstract class NestedClassesCollector[T](nestedOnly: Boolean) extends GenericSignatureVisitor(nestedOnly) { - type InternalName = String + type InternalName = String def declaredNestedClasses(internalName: InternalName): List[T] def getClassIfNested(internalName: InternalName): Option[T] - + val declaredInnerClasses = mutable.Set.empty[T] val referredInnerClasses = mutable.Set.empty[T] diff --git a/compiler/src/dotty/tools/backend/jvm/PostProcessor.scala b/compiler/src/dotty/tools/backend/jvm/PostProcessor.scala new file mode 100644 index 000000000000..606b5645aa24 --- /dev/null +++ b/compiler/src/dotty/tools/backend/jvm/PostProcessor.scala @@ -0,0 +1,117 @@ +package dotty.tools.backend.jvm + +import scala.collection.mutable.ListBuffer +import dotty.tools.dotc.util.{SourcePosition, NoSourcePosition} +import dotty.tools.io.AbstractFile +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Decorators.em +import scala.tools.asm.ClassWriter +import scala.tools.asm.tree.ClassNode + +/** + * Implements late stages of the backend that don't depend on a Global instance, i.e., + * optimizations, post-processing and classfile serialization and writing. + */ +class PostProcessor(val frontendAccess: PostProcessorFrontendAccess, val bTypes: BTypes) { + self => + import bTypes.* + import frontendAccess.{backendReporting, compilerSettings} + import int.given + + val backendUtils = new BackendUtils(this) + val classfileWriter = ClassfileWriter(frontendAccess) + + def postProcessAndSendToDisk(generatedDefs: GeneratedDefs): Unit = { + val GeneratedDefs(classes, tasty) = generatedDefs + for (GeneratedClass(classNode, sourceFile, isArtifact, onFileCreated) <- classes) { + val bytes = + try + if !isArtifact then setSerializableLambdas(classNode) + setInnerClasses(classNode) + serializeClass(classNode) + catch + case e: java.lang.RuntimeException if e.getMessage != null && e.getMessage.nn.contains("too large!") => + backendReporting.error(em"Could not write class ${classNode.name} because it exceeds JVM code size limits. ${e.getMessage}") + null + case ex: Throwable => + ex.printStackTrace() + backendReporting.error(em"Error while emitting ${classNode.name}\n${ex.getMessage}") + null + + if (bytes != null) { + if (AsmUtils.traceSerializedClassEnabled && classNode.name.nn.contains(AsmUtils.traceSerializedClassPattern)) + AsmUtils.traceClass(bytes) + + val clsFile = classfileWriter.writeClass(classNode.name.nn, bytes, sourceFile) + if clsFile != null then onFileCreated(clsFile) + } + } + + for (GeneratedTasty(classNode, binaryGen) <- tasty){ + classfileWriter.writeTasty(classNode.name.nn, binaryGen()) + } + } + + private def setSerializableLambdas(classNode: ClassNode): Unit = { + import backendUtils.{collectSerializableLambdas, addLambdaDeserialize} + val serializableLambdas = collectSerializableLambdas(classNode) + if serializableLambdas.nonEmpty then + addLambdaDeserialize(classNode, serializableLambdas) + } + + private def setInnerClasses(classNode: ClassNode): Unit = { + import backendUtils.{collectNestedClasses, addInnerClasses} + classNode.innerClasses.nn.clear() + val (declared, referred) = collectNestedClasses(classNode) + addInnerClasses(classNode, declared, referred) + } + + def serializeClass(classNode: ClassNode): Array[Byte] = { + val cw = new ClassWriterWithBTypeLub(backendUtils.extraProc) + classNode.accept(cw) + cw.toByteArray.nn + } + + // ----------------------------------------------------------------------------------------- + // finding the least upper bound in agreement with the bytecode verifier (given two internal names handed by ASM) + // Background: + // http://gallium.inria.fr/~xleroy/publi/bytecode-verification-JAR.pdf + // http://comments.gmane.org/gmane.comp.java.vm.languages/2293 + // https://github.com/scala/bug/issues/3872 + // ----------------------------------------------------------------------------------------- + + /* An `asm.ClassWriter` that uses `jvmWiseLUB()` + * The internal name of the least common ancestor of the types given by inameA and inameB. + * It's what ASM needs to know in order to compute stack map frames, http://asm.ow2.org/doc/developer-guide.html#controlflow + */ + final class ClassWriterWithBTypeLub(flags: Int) extends ClassWriter(flags) { + + /** + * This method is used by asm when computing stack map frames. It is thread-safe: it depends + * only on the BTypes component, which does not depend on global. + * TODO @lry move to a different place where no global is in scope, on bTypes. + */ + override def getCommonSuperClass(inameA: String, inameB: String): String = { + // All types that appear in a class node need to have their ClassBType cached, see [[cachedClassBType]]. + val a = classBTypeFromInternalName(inameA) + val b = classBTypeFromInternalName(inameB) + val lub = a.jvmWiseLUB(b) + val lubName = lub.internalName + assert(lubName != "scala/Any") + lubName // ASM caches the answer during the lifetime of a ClassWriter. We outlive that. Not sure whether caching on our side would improve things. + } + } +} + +/** + * The result of code generation. [[isArtifact]] is `true` for mirror. + */ +case class GeneratedClass(classNode: ClassNode, sourceFile: AbstractFile, isArtifact: Boolean, onFileCreated: AbstractFile => Unit) +case class GeneratedTasty(classNode: ClassNode, tastyGen: () => Array[Byte]) +case class GeneratedDefs(classes: List[GeneratedClass], tasty: List[GeneratedTasty]) + +// Temporary class, will be refactored in a future commit +trait ClassWriterForPostProcessor { + type InternalName = String + def write(bytes: Array[Byte], className: InternalName, sourceFile: AbstractFile): Unit +} diff --git a/compiler/src/dotty/tools/backend/jvm/PostProcessorFrontendAccess.scala b/compiler/src/dotty/tools/backend/jvm/PostProcessorFrontendAccess.scala new file mode 100644 index 000000000000..80ee68bc94c3 --- /dev/null +++ b/compiler/src/dotty/tools/backend/jvm/PostProcessorFrontendAccess.scala @@ -0,0 +1,79 @@ +package dotty.tools.backend.jvm + +import scala.collection.mutable.{Clearable, HashSet} +import dotty.tools.dotc.util.* +import dotty.tools.dotc.reporting.Message +import dotty.tools.io.AbstractFile +import java.util.{Collection => JCollection, Map => JMap} +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.report +import dotty.tools.dotc.core.Phases + +/** + * Functionality needed in the post-processor whose implementation depends on the compiler + * frontend. All methods are synchronized. + */ +sealed abstract class PostProcessorFrontendAccess { + import PostProcessorFrontendAccess._ + + def compilerSettings: CompilerSettings + def backendReporting: BackendReporting + def getEntryPoints: List[String] + + private val frontendLock: AnyRef = new Object() + inline final def frontendSynch[T](inline x: => T): T = frontendLock.synchronized(x) +} + +object PostProcessorFrontendAccess { + sealed trait CompilerSettings { + def debug: Boolean + def target: String // javaOutputVersion + + def dumpClassesDirectory: Option[String] + def outputDirectory: AbstractFile + + def mainClass: Option[String] + } + + sealed trait BackendReporting { + def error(message: Context ?=> Message): Unit + def warning(message: Context ?=> Message): Unit + def log(message: Context ?=> String): Unit + } + + class Impl[I <: DottyBackendInterface](val int: I, entryPoints: HashSet[String]) extends PostProcessorFrontendAccess { + import int.given + lazy val compilerSettings: CompilerSettings = buildCompilerSettings() + + private def buildCompilerSettings(): CompilerSettings = new CompilerSettings { + extension [T](s: dotty.tools.dotc.config.Settings.Setting[T]) + def valueSetByUser: Option[T] = + Option(s.value).filter(_ != s.default) + def s = ctx.settings + + lazy val target = + val releaseValue = Option(s.javaOutputVersion.value).filter(_.nonEmpty) + val targetValue = Option(s.XuncheckedJavaOutputVersion.value).filter(_.nonEmpty) + (releaseValue, targetValue) match + case (Some(release), None) => release + case (None, Some(target)) => target + case (Some(release), Some(_)) => + report.warning(s"The value of ${s.XuncheckedJavaOutputVersion.name} was overridden by ${ctx.settings.javaOutputVersion.name}") + release + case (None, None) => "8" // least supported version by default + + lazy val debug: Boolean = ctx.debug + lazy val dumpClassesDirectory: Option[String] = s.Ydumpclasses.valueSetByUser + lazy val outputDirectory: AbstractFile = s.outputDir.value + lazy val mainClass: Option[String] = s.XmainClass.valueSetByUser + } + + object backendReporting extends BackendReporting { + def error(message: Context ?=> Message): Unit = frontendSynch(report.error(message)) + def warning(message: Context ?=> Message): Unit = frontendSynch(report.warning(message)) + def log(message: Context ?=> String): Unit = frontendSynch(report.log(message)) + } + + def getEntryPoints: List[String] = frontendSynch(entryPoints.toList) + } +} \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 8606d72546ef..32cb030a3296 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -15,7 +15,7 @@ import scala.util.chaining._ class ScalaSettings extends SettingGroup with AllScalaSettings object ScalaSettings: - // Keep synchronized with `classfileVersion` in `BCodeIdiomatic` + // Keep synchronized with `classfileVersion` in `BackendUtils` private val minTargetVersion = 8 private val maxTargetVersion = 20 diff --git a/docs/_docs/internals/backend.md b/docs/_docs/internals/backend.md index e3215c3993ae..660f6e1f41e5 100644 --- a/docs/_docs/internals/backend.md +++ b/docs/_docs/internals/backend.md @@ -6,8 +6,13 @@ title: "Backend Internals" The code for the JVM backend is split up by functionality and assembled in `GenBCode.scala`. This file defines class `GenBCode`, the compiler phase. +The workflow is split into `CodeGen.scala` Scala compilation context aware responsible for emitting bytecode, +and `PostProcessor.scala` which can be used for parallelized, context agnostic processing. In Scala 2 `PostProcessor`, +was responsible for performing bytecode optimization, e.g. inlining method calls. In Scala 3 it is only used for writing +Class files and Tasty to disk. + ``` -class GenBCodePipeline -[defines]--> PlainClassBuilder +class CodeGen.Impl -[defines]--> PlainClassBuilder | | [extends] [extends] | | @@ -18,14 +23,14 @@ BCodeBodyBuilder ----------------> PlainBodyBuilder BCodeSkelBuilder ----------------> PlainSkelBuilder | / | \ BCodeHelpers ----------------> BCClassGen BCAnnotGen ... (more components) - | | \ - | | \-------------> helper methods - | | \------------> JMirrorBuilder, JBeanInfoBuilder (uses some components, e.g. BCInnerClassGen) - | | - | BytecodeWriters ---------> methods and classes to write byte code files + | \ + | \-------------> helper methods + | \------------> JMirrorBuilder, JAndroidBuilder (uses some components, e.g. BCInnerClassGen) + | \-----------> `backendUtils`: utility for bytecode related ops, contains mapping for supported classfile version | BCodeIdiomatic ----------------> utilities for code generation, e.g. genPrimitiveArithmetic \--------------> `bTypes`: maps and fields for common BTypes + \-------------> `int`: synchronized interface between PostProcessor and compiltion ctx ``` The `BTypes.scala` class contains the `BType` class and predefined BTypes @@ -34,28 +39,33 @@ The `BTypes.scala` class contains the `BType` class and predefined BTypes Compiler creates a `GenBCode` `Phase`, calls `runOn(compilationUnits)`, which calls `run(context)`. This: -* initializes `myPrimitives` defined in `DottyPrimitives` (maps primitive - members, like `int.+`, to bytecode instructions) -* creates a `GenBCodePipeline` and calls `run(tree)` - -`GenBCodePipeline` now: - -* initializes the `bTypes` field of `GenBCodePipeline` defined in `BCodeIdiomatic` - (BType maps, common BTypes like `StringRef`) -* creates `BytecodeWriter` and `JMirrorBuilder` instances (on each compiler run) -* `buildAndSendToDisk(units)`: uses work queues, see below. - - `GenBCodePipeline.feedPipeline1` adds ClassDefs to `q1` - - `Worker1.run` creates ASM `ClassNodes`, adds to `q2`. It creates one - `PlainClassBuilder` for each compilation unit. - - `Worker2.run` adds byte arrays (one for each class) to `q3` - - `GenBCodePipeline.drainQ3` writes byte arrays to disk +* initializes lazily components reused by all `compilationUnits` using same instance of Context: + - `bTypes`, used by `CodeGen` and `PostProcessro`, defined in `BCodeIdiomatic` (BType maps, common BTypes like `StringRef`) + - `backendInterface:` - proxy to Context specific operations + - `codeGen: CodeGen` - uses `backendInterface`, `bTypes`, initializes instance of `DottyPrimitives` and defines `JMirrorBuilder` instance and implements bytecode generation flow (maps primitive members, like `int.+`, to bytecode instructions) + - `fontendAccess` - synchronized `PostProcessor` interface to compiler settings, reporting and GenBCode context (e.g. list of entrypoints) + - `postProcessor` - compilation context agnostic module dedicated to parallel processing of produced bytecode. Currently used only for writing Tasty and Class files. Defines `backendUtils` and `classfileWriter` +* sets context of current compilation unit to the shared context instance +* calls `codeGen.genUnit(ctx.compilation)` which returns structure with generated definitions (both Class files and Tasty) +* calls postProcessing of generated definition in `postProcessor` +* calls registered callbacks if needed for every generated class + +Upon calling `codeGen.genUnit` it: +* creates `PlainClassBuilder` instance for each generated `TypeDef` and creates ASM `ClassNode` +* creates optional mirror class if needed +* generates Tasty file content and store its attributes in either mirror or plain class node + +`PostProcessor` is later: +* enriching `ClassNode` with collected serializable lambdas +* sets its inner classes +* serializes class and writes it to file, optionally it can execute register callbacks for each generated file +* writes generated Tasty to file ## Architecture ## The architecture of `GenBCode` is the same as in Scalac. It can be partitioned into weakly coupled components (called "subsystems" below): - ### (a) The queue subsystem ### Queues mediate between processors, queues don't know what each processor does. @@ -126,4 +136,4 @@ emitting: ### (f) Building an ASM ClassNode given an AST TypeDef ### -It's done by `PlainClassBuilder`(see `GenBCode.scala`). +It's done by `PlainClassBuilder`(see `CodeGen.scala`). From d1255f33c0aea7a5dd9ed4303606de653c347ab3 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 28 Mar 2023 12:12:02 +0200 Subject: [PATCH 319/657] Fix needsOuterIfReferenced #17135 shows that traits also need an outer accessor if they are in a toplevel method (or lazy val). Fixes #17135 --- .../tools/dotc/transform/ExplicitOuter.scala | 10 ++-- tests/pos/i17135.scala | 53 +++++++++++++++++++ 2 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 tests/pos/i17135.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index cddfe51275c8..636dcc2772d0 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -199,9 +199,9 @@ object ExplicitOuter { /** Class needs an outer pointer, provided there is a reference to an outer this in it. */ def needsOuterIfReferenced(cls: ClassSymbol)(using Context): Boolean = - !(cls.isStatic || - cls.owner.enclosingClass.isStaticOwner || - cls.is(PureInterface) + !(cls.isStatic + || cls.effectiveOwner.isStaticOwner + || cls.is(PureInterface) ) /** Class unconditionally needs an outer pointer. This is the case if @@ -226,7 +226,9 @@ object ExplicitOuter { /** The outer parameter accessor of cass `cls` */ private def outerParamAccessor(cls: ClassSymbol)(using Context): TermSymbol = - cls.info.decl(nme.OUTER).symbol.asTerm + val outer = cls.info.decl(nme.OUTER).symbol + assert(outer.isTerm, i"missing outer accessor in $cls") + outer.asTerm /** The outer accessor of class `cls`. To find it is a bit tricky. The * class might have been moved with new owners between ExplicitOuter and Erasure, diff --git a/tests/pos/i17135.scala b/tests/pos/i17135.scala new file mode 100644 index 000000000000..7e3f91cf5e83 --- /dev/null +++ b/tests/pos/i17135.scala @@ -0,0 +1,53 @@ +package doobie + +// original example +def someFunction(param: Int): Int = { + sealed trait Foo { + def asString: String = this match { + case Foo.CaseC => "C" + } + } + object Foo { + // Having an object here crashes the compiler. + object CaseC extends Foo + } + + ??? +} + +// minimization +def foo = + class Bar { + // Having an object here crashes the compiler. + lazy val CaseC = + class Baz extends Foo + new Baz() + } + val Bar: Bar = new Bar() + trait Foo { + def asString = Bar.CaseC + } + +// variant: outer is lazy val +lazy val lazyfoo = + class Bar { + // Having an object here crashes the compiler. + lazy val CaseC = + class Baz extends Foo + new Baz() + } + val Bar: Bar = new Bar() + trait Foo { + def asString = Bar.CaseC + } + +// other example +def bar = + sealed trait GADT2[A] extends Product with Serializable + + object GADT2 { + case class IsDir(path: String) extends GADT2[_root_.scala.Boolean] + case class Exists(path: String) extends GADT2[_root_.scala.Boolean] + case class ReadBytes(path: String) extends GADT2[_root_.scala.Array[_root_.scala.Byte]] + case class CopyOver(src: Seq[_root_.scala.Byte], path: String) extends GADT2[Int] + } \ No newline at end of file From 7296e72779b34d5cd17eb91f132339c391bb169f Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 28 Mar 2023 13:31:00 +0200 Subject: [PATCH 320/657] Skip superArg methods when determining whether an outer accessor is needed --- .../src/dotty/tools/dotc/transform/ExplicitOuter.scala | 9 ++++++++- tests/pos/t0049.scala | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 636dcc2772d0..deb1f665c022 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -13,6 +13,7 @@ import core.Decorators._ import core.StdNames.nme import core.Names._ import core.NameOps._ +import core.NameKinds.SuperArgName import SymUtils._ import dotty.tools.dotc.ast.tpd @@ -197,10 +198,16 @@ object ExplicitOuter { private def outerAccName(cls: ClassSymbol)(using Context): TermName = nme.OUTER.expandedName(cls) + private def outerOwner(sym: Symbol)(using Context): Symbol = + val owner = sym.effectiveOwner + if owner.name.is(SuperArgName) || owner.isLocalDummy + then owner.enclosingClass + else owner + /** Class needs an outer pointer, provided there is a reference to an outer this in it. */ def needsOuterIfReferenced(cls: ClassSymbol)(using Context): Boolean = !(cls.isStatic - || cls.effectiveOwner.isStaticOwner + || outerOwner(cls).isStaticOwner || cls.is(PureInterface) ) diff --git a/tests/pos/t0049.scala b/tests/pos/t0049.scala index dd866422637f..a453cec9568e 100644 --- a/tests/pos/t0049.scala +++ b/tests/pos/t0049.scala @@ -1,3 +1,6 @@ class C1(x: AnyRef) {}; class C2 extends C1({ class A extends AnyRef {}; (new A) : AnyRef }) {}; + +class Outer: + class C2 extends C1({ class A extends AnyRef {}; (new A) : AnyRef }) {}; From 1d497f586a313aeb92039bd14d90db132c72d91e Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 28 Mar 2023 14:24:34 +0200 Subject: [PATCH 321/657] Recognize named arguments in isFunctionWithUnknownParamType --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 2 ++ compiler/src/dotty/tools/dotc/typer/Applications.scala | 2 +- tests/pos/i17155.scala | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i17155.scala diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 73f45bd7369b..c2147b6af2d3 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -399,6 +399,8 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] Some(tree) case Block(Nil, expr) => functionWithUnknownParamType(expr) + case NamedArg(_, expr) => + functionWithUnknownParamType(expr) case _ => None } diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 345a8693063b..79d6501ccb2d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -448,7 +448,7 @@ trait Applications extends Compatibility { def rec(t: Type): Type = { t.widen match{ case funType: MethodType => funType - case funType: PolyType => + case funType: PolyType => rec(instantiateWithTypeVars(funType)) case tp => tp } diff --git a/tests/pos/i17155.scala b/tests/pos/i17155.scala new file mode 100644 index 000000000000..7b58dbe4a13a --- /dev/null +++ b/tests/pos/i17155.scala @@ -0,0 +1,7 @@ +def foo[A, B](arr: Array[A], pf: PartialFunction[A, B]): Seq[B] = arr.toSeq.collect(pf) +def foo[A, B](list: List[A], pf: PartialFunction[A, B]): Seq[B] = list.collect(pf) // no errors if this is commented out + +val arr = Array(1, 2, 3) +val resOkay = foo(arr = arr, { case n if n % 2 != 0 => n.toString }) // compiles +val resNope = foo(arr = arr, pf = { case n if n % 2 != 0 => n.toString }) // Error 1 +val resNope2 = foo[Int, String](arr = arr, pf = { case n if n % 2 != 0 => n.toString }) // Error 2 \ No newline at end of file From 2417f194b23ad4dacf27e6e38d83eb5faf09349c Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 28 Mar 2023 18:06:27 +0200 Subject: [PATCH 322/657] Add suspend strawman Add a test that combines various experiments using our hypothetical Suspension abstractions. --- tests/pos/suspend-strawman/choices.scala | 28 ++++++++ tests/pos/suspend-strawman/generators.scala | 67 +++++++++++++++++++ .../suspend-strawman/monadic-reflect.scala | 56 ++++++++++++++++ tests/pos/suspend-strawman/runtime.scala | 11 +++ .../pos/suspend-strawman/simple-futures.scala | 53 +++++++++++++++ 5 files changed, 215 insertions(+) create mode 100644 tests/pos/suspend-strawman/choices.scala create mode 100644 tests/pos/suspend-strawman/generators.scala create mode 100644 tests/pos/suspend-strawman/monadic-reflect.scala create mode 100644 tests/pos/suspend-strawman/runtime.scala create mode 100644 tests/pos/suspend-strawman/simple-futures.scala diff --git a/tests/pos/suspend-strawman/choices.scala b/tests/pos/suspend-strawman/choices.scala new file mode 100644 index 000000000000..968c223d9c0b --- /dev/null +++ b/tests/pos/suspend-strawman/choices.scala @@ -0,0 +1,28 @@ +import scala.util.boundary, boundary.Label +import runtime.suspend + +trait Choice: + def choose[A](choices: A*): A + +// the handler +def choices[T](body: Choice ?=> T): Seq[T] = + boundary[Seq[T]]: + given Choice with + def choose[A](choices: A*): A = + suspend[A, Seq[T]](s => choices.flatMap(s.resume)) + Seq(body) + +def choose[A](choices: A*)(using c: Choice): A = c.choose(choices*) + +def TestChoices: Seq[Int] = + choices: + def x = choose(1, -2, -3) + def y = choose("ab", "cde") + val xx = x; + xx + ( + if xx > 0 then + val z = choose(xx / 2, xx * 2) + y.length * z + else y.length + ) + diff --git a/tests/pos/suspend-strawman/generators.scala b/tests/pos/suspend-strawman/generators.scala new file mode 100644 index 000000000000..a890196e6215 --- /dev/null +++ b/tests/pos/suspend-strawman/generators.scala @@ -0,0 +1,67 @@ +import scala.util.boundary +import scala.compiletime.uninitialized +import runtime.suspend + +trait CanProduce[-T]: + def produce(x: T): Unit + +object generate: + + def produce[T](x: T)(using cp: CanProduce[T]): Unit = cp.produce(x) + + def apply[T](body: CanProduce[T] ?=> Unit): Iterator[T] = new: + var nextKnown: Boolean = false + var nextElem: Option[T] = uninitialized + + var step: () => Unit = () => + boundary[Unit]: + given CanProduce[T] with + def produce(x: T): Unit = + nextElem = Some(x) + suspend[Unit, Unit]: k => + step = () => k.resume(()) + body + nextElem = None + + def hasNext: Boolean = + if !nextKnown then { step(); nextKnown = true } + nextElem.isDefined + + def next: T = + require(hasNext) + nextKnown = false + nextElem.get +end generate + +enum Tree[T]: + case Leaf(x: T) + case Inner(xs: List[Tree[T]]) + +def leafs[T](t: Tree[T]): Iterator[T] = + generate: + def recur(t: Tree[T]): Unit = t match + case Tree.Leaf(x) => generate.produce(x) + case Tree.Inner(xs) => xs.foreach(recur) + recur(t) + +object Variant2: + trait Generator[T]: + def nextOption: Option[T] + + def generate[T](body: CanProduce[T] ?=> Unit): Generator[T] = new: + + def nextOption: Option[T] = + step() + + var step: () => Option[T] = () => + boundary: + given CanProduce[T] with + def produce(x: T): Unit = + suspend[Unit, Option[T]]: k => + step = () => k.resume(()) + Some(x) + body + None + end generate + + diff --git a/tests/pos/suspend-strawman/monadic-reflect.scala b/tests/pos/suspend-strawman/monadic-reflect.scala new file mode 100644 index 000000000000..84c5255c2a96 --- /dev/null +++ b/tests/pos/suspend-strawman/monadic-reflect.scala @@ -0,0 +1,56 @@ +import scala.util.boundary +import runtime.suspend + +trait Monad[F[_]]: + + /** The unit value for a monad */ + def pure[A](x: A): F[A] + + extension [A](x: F[A]) + /** The fundamental composition operation */ + def flatMap[B](f: A => F[B]): F[B] + + /** The `map` operation can now be defined in terms of `flatMap` */ + def map[B](f: A => B) = x.flatMap(f.andThen(pure)) + +end Monad + +trait CanReflect[M[_]]: + def reflect[R](mr: M[R]): R + +trait Monadic[M[_]: Monad]: + + /** + * Embedding of pure values into the monad M + */ + def pure[A](a: A): M[A] + + /** + * Sequencing of monadic values + * + * Implementations are required to implement sequencing in a stack-safe + * way, that is they either need to implement trampolining on their own + * or implement `sequence` as a tail recursive function. + * + * Actually the type X can be different for every call to f... + * It is a type aligned sequence, but for simplicity we do not enforce this + * here. + */ + def sequence[X, R](init: M[X])(f: X => Either[M[X], M[R]]): M[R] + + /** + * Helper to summon and use an instance of CanReflect[M] + */ + def reflect[R](mr: M[R])(using r: CanReflect[M]): R = r.reflect(mr) + + /** + * Reify a computation into a monadic value + */ + def reify[R](prog: CanReflect[M] ?=> R): M[R] = + boundary [M[R]]: + given CanReflect[M] with + def reflect[R2](mr: M[R2]): R2 = + suspend [R2, M[R]] (k => mr.flatMap(k.resume)) + pure(prog) + +end Monadic \ No newline at end of file diff --git a/tests/pos/suspend-strawman/runtime.scala b/tests/pos/suspend-strawman/runtime.scala new file mode 100644 index 000000000000..406da4f7dd5e --- /dev/null +++ b/tests/pos/suspend-strawman/runtime.scala @@ -0,0 +1,11 @@ +package runtime +import scala.util.boundary, boundary.Label + +/** A hypothetical API for suspensions. Not yet implemented. + * Suspension contain a delimited contination, which can be + * invoked with `resume` + */ +class Suspension[-T, +R]: + def resume(arg: T): R = ??? + +def suspend[T, R](body: Suspension[T, R] => R)(using Label[R]): T = ??? diff --git a/tests/pos/suspend-strawman/simple-futures.scala b/tests/pos/suspend-strawman/simple-futures.scala new file mode 100644 index 000000000000..0a80a74d49dc --- /dev/null +++ b/tests/pos/suspend-strawman/simple-futures.scala @@ -0,0 +1,53 @@ +package simpleFutures + +import scala.collection.mutable.ListBuffer +import scala.util.boundary, boundary.Label +import runtime.suspend + +object Scheduler: + def schedule(task: Runnable): Unit = ??? + +trait Async: + def await[T](f: Future[T]): T + +class Future[+T](body: Async ?=> T): + private var result: Option[T] = None + private var waiting: ListBuffer[T => Unit] = ListBuffer() + private def addWaiting(k: T => Unit): Unit = waiting += k + + def await(using a: Async): T = a.await(this) + + private def complete(): Unit = + Future.async: + val value = body + val result = Some(value) + for k <- waiting do + Scheduler.schedule(() => k(value)) + waiting.clear() + + Scheduler.schedule(() => complete()) + +object Future: + + // a handler for Async + def async(body: Async ?=> Unit): Unit = + boundary [Unit]: + given Async with + def await[T](f: Future[T]): T = f.result match + case Some(x) => x + case None => suspend[T, Unit](s => f.addWaiting(s.resume)) + body + +end Future + +def Test(x: Future[Int], xs: List[Future[Int]]) = + Future: + x.await + xs.map(_.await).sum + + + + + + + + From 4219db2e0306b739fc9671bca9c1fac82f4c2013 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 7 Mar 2023 15:18:52 +0100 Subject: [PATCH 323/657] WUnused: Fix for symbols with synthetic names and unused transparent inlines --- .../tools/dotc/transform/CheckUnused.scala | 27 +++++++++++++++++- .../fatal-warnings/i15503i.scala | 28 +++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5e4ed6f6e0df..cc51d9bfbe64 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.core.Decorators.{em, i} import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames -import dotty.tools.dotc.report +import dotty.tools.dotc.{ast, report} import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo import dotty.tools.dotc.util.{Property, SrcPos} @@ -432,6 +432,20 @@ object CheckUnused: else exists } + + // not report unused transparent inline imports + for { + imp <- imports + sel <- imp.selectors + } { + if unusedImport.contains(sel) then + val tpd.Import(qual, _) = imp + val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) + val isTransparentAndInline = importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) + if isTransparentAndInline then + unusedImport -= sel + } + // if there's an outer scope if usedInScope.nonEmpty then // we keep the symbols not referencing an import in this scope @@ -450,6 +464,7 @@ object CheckUnused: */ def getUnused(using Context): UnusedResult = popScope() + val sortedImp = if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then unusedImport.map(d => d.srcPos -> WarnTypes.Imports).toList @@ -460,6 +475,7 @@ object CheckUnused: localDefInScope .filterNot(d => d.symbol.usedDefContains) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.LocalDefs).toList else Nil @@ -467,6 +483,7 @@ object CheckUnused: if ctx.settings.WunusedHas.explicits then explicitParamInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ExplicitParams).toList else Nil @@ -474,6 +491,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil @@ -481,6 +499,7 @@ object CheckUnused: if ctx.settings.WunusedHas.privates then privateDefInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.PrivateMembers).toList else Nil @@ -488,6 +507,7 @@ object CheckUnused: if ctx.settings.WunusedHas.patvars then patVarsInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .map(d => d.namePos -> WarnTypes.PatVars).toList else @@ -500,6 +520,11 @@ object CheckUnused: end getUnused //============================ HELPERS ==================================== + /** + * Heuristic to detect synthetic suffixes in names of symbols + */ + private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean = + symbol.name.mangledString.contains("$") /** * Is the the constructor of synthetic package object * Should be ignored as it is always imported/used in package diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 3dd4d1fc61e7..9ac2ec5ef622 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -209,6 +209,34 @@ package foo.test.i16925: _ = println(i) // OK } yield () +package foo.test.i16863a: + import scala.quoted.* + def fn(using Quotes) = + val x = Expr(1) + '{ $x + 2 } // OK + +package foo.test.i16863b: + import scala.quoted.* + def fn[A](using Quotes, Type[A]) = // OK + val numeric = Expr.summon[Numeric[A]].getOrElse(???) + '{ $numeric.fromInt(3) } // OK + +package foo.test.i16863c: + import scala.quoted.* + def fn[A](expr: Expr[Any])(using Quotes) = + val imp = expr match + case '{ ${ _ }: a } => Expr.summon[Numeric[a]] // OK + println(imp) + +package foo.test.i16863d: + import scala.quoted.* + import scala.compiletime.asMatchable // OK + def fn[A](using Quotes, Type[A]) = + import quotes.reflect.* + val imp = TypeRepr.of[A].widen.asMatchable match + case Refinement(_,_,_) => () + println(imp) + package foo.test.i16679a: object myPackage: trait CaseClassName[A]: From c36965ce94ff81e18e88294d932a3d9014668f70 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 8 Mar 2023 13:24:54 +0100 Subject: [PATCH 324/657] Adjust assertions in test --- .../fatal-warnings/i15503-scala2/scala2-t11681.scala | 4 ++-- tests/neg-custom-args/fatal-warnings/i15503b.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala index f04129a19e48..18aa6879eeba 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala @@ -100,9 +100,9 @@ trait Anonymous { trait Context[A] trait Implicits { def f[A](implicit ctx: Context[A]) = answer // error - def g[A: Context] = answer // error + def g[A: Context] = answer // OK } -class Bound[A: Context] // error +class Bound[A: Context] // OK object Answers { def answer: Int = 42 } diff --git a/tests/neg-custom-args/fatal-warnings/i15503b.scala b/tests/neg-custom-args/fatal-warnings/i15503b.scala index 19bcd01a8dde..8a4a055150f9 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503b.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503b.scala @@ -75,7 +75,7 @@ package foo.scala2.tests: object Types { def l1() = { - object HiObject { def f = this } // error + object HiObject { def f = this } // OK class Hi { // error def f1: Hi = new Hi def f2(x: Hi) = x From 8ff475452f74111be884cea7f18b1b818e6c4c0a Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 10 Mar 2023 17:36:15 +0100 Subject: [PATCH 325/657] Check if import contains transparent inline in registerImport --- .../tools/dotc/transform/CheckUnused.scala | 29 +++++++++---------- .../fatal-warnings/i15503f.scala | 2 +- .../fatal-warnings/i15503g.scala | 4 +-- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index cc51d9bfbe64..9b2fd122f68a 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.core.Decorators.{em, i} import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames -import dotty.tools.dotc.{ast, report} +import dotty.tools.dotc.report import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo import dotty.tools.dotc.util.{Property, SrcPos} @@ -368,7 +368,7 @@ object CheckUnused: /** Register an import */ def registerImport(imp: tpd.Import)(using Context): Unit = - if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum then + if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum && !isTransparentAndInline(imp) then impInScope.top += imp unusedImport ++= imp.selectors.filter { s => !shouldSelectorBeReported(imp, s) && !isImportExclusion(s) @@ -433,19 +433,6 @@ object CheckUnused: exists } - // not report unused transparent inline imports - for { - imp <- imports - sel <- imp.selectors - } { - if unusedImport.contains(sel) then - val tpd.Import(qual, _) = imp - val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) - val isTransparentAndInline = importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) - if isTransparentAndInline then - unusedImport -= sel - } - // if there's an outer scope if usedInScope.nonEmpty then // we keep the symbols not referencing an import in this scope @@ -520,6 +507,18 @@ object CheckUnused: end getUnused //============================ HELPERS ==================================== + + /** + * Checks if import selects a def that is transparent and inline + */ + private def isTransparentAndInline(imp: tpd.Import)(using Context): Boolean = + (for { + sel <- imp.selectors + } yield { + val qual = imp.expr + val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) + importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) + }).exists(identity) /** * Heuristic to detect synthetic suffixes in names of symbols */ diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index db695da3490b..d36cd01be74e 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -5,7 +5,7 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // error +def f3(a: Int)(using Int) = a // OK def f4(a: Int)(using Int) = default_int // error def f6(a: Int)(using Int) = summon[Int] // OK def f7(a: Int)(using Int) = summon[Int] + a // OK diff --git a/tests/neg-custom-args/fatal-warnings/i15503g.scala b/tests/neg-custom-args/fatal-warnings/i15503g.scala index d4daea944184..a0822e7e1611 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503g.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503g.scala @@ -5,8 +5,8 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = default_int // error -def f3(a: Int)(using Int) = a // error -def f4(a: Int)(using Int) = default_int // error // error +def f3(a: Int)(using Int) = a // OK +def f4(a: Int)(using Int) = default_int // error def f6(a: Int)(using Int) = summon[Int] // error def f7(a: Int)(using Int) = summon[Int] + a // OK From 56b484dcb62e0d98c6bd0178b4469ed405e212ef Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Mar 2023 17:32:00 +0200 Subject: [PATCH 326/657] Warn for synthetic using/givens with wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503f.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 9b2fd122f68a..bf1ec37ebab4 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -478,7 +478,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol)) + .filterNot(d => containsSyntheticSuffix(d.symbol) && !d.rawMods.is(Given)) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index d36cd01be74e..db695da3490b 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -5,7 +5,7 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // OK +def f3(a: Int)(using Int) = a // error def f4(a: Int)(using Int) = default_int // error def f6(a: Int)(using Int) = summon[Int] // OK def f7(a: Int)(using Int) = summon[Int] + a // OK From 00b3d251a97817b253b4bef0c45efee4c03d9867 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Mar 2023 19:29:09 +0200 Subject: [PATCH 327/657] Wunused: only filter out non-zero span-length givens --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index bf1ec37ebab4..665c0b4284ca 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -478,7 +478,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol) && !d.rawMods.is(Given)) + .filterNot(d => containsSyntheticSuffix(d.symbol) && (!d.rawMods.is(Given) || hasZeroLengthSpan(d.symbol))) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil @@ -519,11 +519,18 @@ object CheckUnused: val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) }).exists(identity) + /** * Heuristic to detect synthetic suffixes in names of symbols */ private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean = symbol.name.mangledString.contains("$") + + /** + * Heuristic to detect generated symbols by checking if symbol has zero length span in source + */ + private def hasZeroLengthSpan(symbol: Symbol)(using Context): Boolean = + symbol.span.end - symbol.span.start == 0 /** * Is the the constructor of synthetic package object * Should be ignored as it is always imported/used in package From 8f044bf014b7ab180e3ecffd4f3fab6fe95d981b Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Tue, 28 Mar 2023 22:50:57 +0200 Subject: [PATCH 328/657] Detail UnapplyInvalidReturnType error message --- .../dotty/tools/dotc/reporting/messages.scala | 6 +++++- tests/neg/17077.scala | 14 +++++++++++++ tests/pos/17077.scala | 20 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 tests/neg/17077.scala create mode 100644 tests/pos/17077.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 9875c508c9fd..a9cbf9037995 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1962,7 +1962,11 @@ class UnapplyInvalidReturnType(unapplyResult: Type, unapplyName: Name)(using Con |To be used as an extractor, an unapply method has to return a type that either: | - has members ${Magenta("isEmpty: Boolean")} and ${Magenta("get: S")} (usually an ${Green("Option[S]")}) | - is a ${Green("Boolean")} - | - is a ${Green("Product")} (like a ${Magenta("Tuple2[T1, T2]")}) + | - is a ${Green("Product")} (like a ${Magenta("Tuple2[T1, T2]")}) of arity i with i >= 1, and has members _1 to _i + | + |See: https://docs.scala-lang.org/scala3/reference/changed-features/pattern-matching.html#fixed-arity-extractors + | + |Examples: | |class A(val i: Int) | diff --git a/tests/neg/17077.scala b/tests/neg/17077.scala new file mode 100644 index 000000000000..26a052a7bf97 --- /dev/null +++ b/tests/neg/17077.scala @@ -0,0 +1,14 @@ +case class IsIntResult() + +object IsInt: + def unapply(x: Int): IsIntResult = IsIntResult() + +@main def test = + val v: String | Int = "Blop" + val res = + v match + case IsInt() => 43 // error: cannot use a product of arity zero as a return type for unapply + // see UnapplyInvalidReturnType in messages.scala + // and https://docs.scala-lang.org/scala3/reference/changed-features/pattern-matching.html#fixed-arity-extractors + case _ => 42 + println(res) diff --git a/tests/pos/17077.scala b/tests/pos/17077.scala new file mode 100644 index 000000000000..434bba9b4e8a --- /dev/null +++ b/tests/pos/17077.scala @@ -0,0 +1,20 @@ +class MyProduct extends Product: + def foo = ??? + override def productArity: Int = 1 + override def productElement(n: Int): Any = 42 + override def canEqual(that: Any): Boolean = that.isInstanceOf[MyProduct] + def _1 = 42 + +object MyProductUnapply: + def unapply(x: Int): MyProduct = MyProduct() + +@main def test = + val v: String | Int = "Blop" + val res = + v match + case MyProductUnapply(y) => y // works: a product of arity 1 is accepted as the return type of unapply + // see UnapplyInvalidReturnType in messages.scala + // and https://docs.scala-lang.org/scala3/reference/changed-features/pattern-matching.html#fixed-arity-extractors + case _ => 42 + println(res) + From e9077a9ff4385aa79b0d34aab58b87b55749ca96 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 14:52:46 +0200 Subject: [PATCH 329/657] Skip all symbols with $ in name in Wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 665c0b4284ca..f960f7b9e60c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -478,7 +478,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol) && (!d.rawMods.is(Given) || hasZeroLengthSpan(d.symbol))) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil @@ -526,11 +526,6 @@ object CheckUnused: private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean = symbol.name.mangledString.contains("$") - /** - * Heuristic to detect generated symbols by checking if symbol has zero length span in source - */ - private def hasZeroLengthSpan(symbol: Symbol)(using Context): Boolean = - symbol.span.end - symbol.span.start == 0 /** * Is the the constructor of synthetic package object * Should be ignored as it is always imported/used in package From a83d0c954a619076aec91e1ed6d141a600960913 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 15:51:50 +0200 Subject: [PATCH 330/657] Add a failing case with named using to test Wunused:implicits --- tests/neg-custom-args/fatal-warnings/i15503f.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index db695da3490b..67c595d74f40 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -5,8 +5,9 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // error -def f4(a: Int)(using Int) = default_int // error +def f3(a: Int)(using Int) = a // OK +def f4(a: Int)(using Int) = default_int // OK def f6(a: Int)(using Int) = summon[Int] // OK def f7(a: Int)(using Int) = summon[Int] + a // OK +def f8(a: Int)(using foo: Int) = a // error From 1f9e8693d5b782d11774b1d239fc6c82aa734ebe Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 16:10:21 +0200 Subject: [PATCH 331/657] Replace for with exists in isTransparentInline in WUNused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index f960f7b9e60c..d7c88a1fca40 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -512,13 +512,11 @@ object CheckUnused: * Checks if import selects a def that is transparent and inline */ private def isTransparentAndInline(imp: tpd.Import)(using Context): Boolean = - (for { - sel <- imp.selectors - } yield { + imp.selectors.exists { sel => val qual = imp.expr val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) - }).exists(identity) + } /** * Heuristic to detect synthetic suffixes in names of symbols From 599fc9df8c3cc46ce3e28abb5f6832944127d3ec Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 19:19:13 +0200 Subject: [PATCH 332/657] Skip extension method params in WUnused --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503g.scala | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5e4ed6f6e0df..b9a726455bb7 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -627,7 +627,7 @@ object CheckUnused: extension (memDef: tpd.MemberDef) private def isValidMemberDef(using Context): Boolean = - !memDef.symbol.isUnusedAnnot && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) && !memDef.name.isWildcard + !memDef.symbol.isUnusedAnnot && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) && !memDef.name.isWildcard && !memDef.symbol.owner.is(Extension) private def isValidParam(using Context): Boolean = val sym = memDef.symbol diff --git a/tests/neg-custom-args/fatal-warnings/i15503g.scala b/tests/neg-custom-args/fatal-warnings/i15503g.scala index d4daea944184..ee8f6075d4f6 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503g.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503g.scala @@ -12,4 +12,11 @@ def f7(a: Int)(using Int) = summon[Int] + a // OK /* --- Trivial method check --- */ def g1(x: Int) = 1 // OK -def g2(x: Int) = ??? // OK \ No newline at end of file +def g2(x: Int) = ??? // OK + +package foo.test.i17101: + type Test[A] = A + extension[A] (x: Test[A]) { // OK + def value: A = x + def causesIssue: Unit = println("oh no") + } From 1d6ccf84d0df260a874b2f55e7276aedd6919922 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 27 Mar 2023 16:49:09 +0200 Subject: [PATCH 333/657] Fix wunused false positive when deriving alias type --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 12 ++++++++++-- libste | 0 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 libste diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index d7c88a1fca40..6fc11d3ab007 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -589,14 +589,22 @@ object CheckUnused: /** Given an import and accessibility, return an option of selector that match import<->symbol */ private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp - val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).contains(sym) + val dealiasedSym = dealias(sym) + val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).map(dealias).contains(dealiasedSym) def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) + def dealiasedSelector = sels.flatMap(sel => qual.tpe.member(sym.name).alternatives.map(m => (sel, m.symbol))).collect { + case (sel, sym) if dealias(sym) == dealiasedSym => sel + }.headOption def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) if qualHasSymbol && !isAccessible && sym.exists then - selector.orElse(wildcard) // selector with name or wildcard (or given) + selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None + private def dealias(symbol: Symbol)(using Context): Symbol = + if(symbol.isType && symbol.asType.denot.isAliasType) then + symbol.asType.typeRef.dealias.typeSymbol + else symbol /** Annotated with @unused */ private def isUnusedAnnot(using Context): Boolean = sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot) diff --git a/libste b/libste new file mode 100644 index 000000000000..e69de29bb2d1 From 2e3021109941163b9161c5085eefc4ef5e31495a Mon Sep 17 00:00:00 2001 From: Arman Bilge Date: Thu, 30 Mar 2023 06:42:40 -0700 Subject: [PATCH 334/657] Strip `index.html` from external API url (#17112) Ports this fix from Scaladoc 2. https://github.com/scala/scala/blob/15538c7365bbb03f37d06b59f050241683972c71/src/scaladoc/scala/tools/nsc/doc/Settings.scala#L281-L287 Without it, links to external docs are broken for several libraries. --- scaladoc/src/dotty/tools/scaladoc/ExternalDocLink.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/ExternalDocLink.scala b/scaladoc/src/dotty/tools/scaladoc/ExternalDocLink.scala index 97e0d309d6b8..fd4963bcce4a 100644 --- a/scaladoc/src/dotty/tools/scaladoc/ExternalDocLink.scala +++ b/scaladoc/src/dotty/tools/scaladoc/ExternalDocLink.scala @@ -25,10 +25,12 @@ object ExternalDocLink: case Failure(e) => fail(mapping, s"Unable to parse $descr. Exception $e occured") } + private def stripIndex(url: String): String = url.stripSuffix("index.html").stripSuffix("/") + "/" + def parseLegacy(mapping: String): Either[String, ExternalDocLink] = mapping.split("#").toList match case path :: apiUrl :: Nil => for { - url <- tryParse(mapping, "url")(URL(apiUrl)) + url <- tryParse(mapping, "url")(URL(stripIndex(apiUrl))) } yield ExternalDocLink( List(s"${Regex.quote(path)}.*".r), url, @@ -55,7 +57,7 @@ object ExternalDocLink: case regexStr :: docToolStr :: urlStr :: rest => for { regex <- tryParse(mapping, "regex")(regexStr.r) - url <- tryParse(mapping, "url")(URL(urlStr)) + url <- tryParse(mapping, "url")(URL(stripIndex(urlStr))) doctool <- doctoolByName(docToolStr) packageList <- parsePackageList(rest) } yield ExternalDocLink( From cabfa68927445f5a1bf09167b2daeafb4e0bc701 Mon Sep 17 00:00:00 2001 From: Lucas Leblanc <44496264+Dedelweiss@users.noreply.github.com> Date: Thu, 30 Mar 2023 16:03:16 +0200 Subject: [PATCH 335/657] Fix: Remove the duplicate logo (#17170) - I added an orElse to the snippet so that the dark logo works and there is no duplicate logo. ### Before Screenshot 2023-03-29 at 14 51 09 ### After Screenshot 2023-03-29 at 14 48 22 Fixes: #16693 --- .../src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala index dbb3b6bc6014..cc0a0e197d26 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala @@ -207,11 +207,11 @@ class HtmlRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx: Do } val darkProjectLogoElem = - darkProjectLogo.flatMap { + darkProjectLogo.orElse(projectLogo).flatMap { case Resource.File(path, _) => Some(span(id := "dark-project-logo", cls := "project-logo")(img(src := resolveRoot(link.dri, path)))) case _ => None - }.orElse(projectLogoElem) + } val parentsHtml = val innerTags = parents.flatMap[TagArg](b => Seq( From e940421e7cb2062c0cda5b79b347379f5005a313 Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 20 Mar 2023 16:03:34 +0100 Subject: [PATCH 336/657] Added some corrections - Correct typo in SignatureTest file - Correct from the feedback --- project/Build.scala | 2 +- .../src/tests/extensionParams.scala | 4 ---- .../scaladoc/signatures/SignatureTest.scala | 22 ++++++------------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 1ec68edbdc1b..82b7fee64879 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -361,7 +361,7 @@ object Build { // Settings used when compiling dotty with a non-bootstrapped dotty lazy val commonBootstrappedSettings = commonDottySettings ++ NoBloopExport.settings ++ Seq( // To enable support of scaladoc and language-server projects you need to change this to true and use sbt as your build server - // bspEnabled := false, + bspEnabled := false, (Compile / unmanagedSourceDirectories) += baseDirectory.value / "src-bootstrapped", version := dottyVersion, diff --git a/scaladoc-testcases/src/tests/extensionParams.scala b/scaladoc-testcases/src/tests/extensionParams.scala index 950dc57dbe93..0e2225d8aa3c 100644 --- a/scaladoc-testcases/src/tests/extensionParams.scala +++ b/scaladoc-testcases/src/tests/extensionParams.scala @@ -61,10 +61,6 @@ extension (using Unit)(a: Int) def f14(): Any = ??? -extension (a: Int)(using Unit) - def f15(): Any - = ??? - import scala.language.experimental.clauseInterleaving extension (using String)(using Int)(a: Animal)(using Unit)(using Number) diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala index 9c831d6a7349..d5b7a0b9b6f8 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/SignatureTest.scala @@ -43,7 +43,7 @@ abstract class SignatureTest( val unexpected = unexpectedFromSources.flatMap(actualSignatures.get).flatten val expectedButNotFound = expectedFromSources.flatMap { - case (k, v) => findMissingSingatures(v, actualSignatures.getOrElse(k, Nil)) + case (k, v) => findMissingSignatures(v, actualSignatures.getOrElse(k, Nil)) } val missingReport = Option.when(!ignoreMissingSignatures && !expectedButNotFound.isEmpty) @@ -75,26 +75,21 @@ abstract class SignatureTest( private val unexpectedRegex = raw"(.+)//unexpected".r private val identifierRegex = raw"^\s*(`.*`|(?:\w+)(?:_[^\[\(\s]+)|\w+|[^\[\(\s]+)".r - private def findMissingSingatures(expected: Seq[String], actual: Seq[String]): Set[String] = + private def findMissingSignatures(expected: Seq[String], actual: Seq[String]): Set[String] = expected.toSet &~ actual.toSet extension (s: String) private def startWithAnyOfThese(c: String*) = c.exists(s.startsWith) private def compactWhitespaces = whitespaceRegex.replaceAllIn(s, " ") - private var counter = 0 private def findName(signature: String, kinds: Seq[String]): Option[String] = for kindMatch <- kinds.flatMap(k =>s"\\b$k\\b".r.findFirstMatchIn(signature)).headOption + kind <- Option(kindMatch.group(0)) // to filter out nulls afterKind <- Option(kindMatch.after(0)) // to filter out nulls - nameMatch <- identifierRegex.findFirstMatchIn( - if kindMatch.group(0).contains("extension") - then - signature - else - afterKind - ) - yield nameMatch.group(1) + name <- if kind.contains("extension") then Some(signature) // The name of an extension will always be the signature itself + else identifierRegex.findFirstMatchIn(afterKind).map(_.group(1)) + yield name private def signaturesFromSources(source: Source, kinds: Seq[String]): Seq[SignatureRes] = source.getLines.map(_.trim) @@ -118,9 +113,7 @@ abstract class SignatureTest( def processFile(path: Path): Unit = if filterFunc(path) then val document = Jsoup.parse(IO.read(path)) val documentable = document.select(".groupHeader").forEach { element => - val signature = element.select(".groupHeader").eachText.asScala.mkString("") - val all = s"$signature" - signatures += all + signatures += element.text } val content = document.select(".documentableElement").forEach { elem => val annotations = elem.select(".annotations").eachText.asScala.mkString("") @@ -134,7 +127,6 @@ abstract class SignatureTest( val all = s"$annotations$other $sigPrefix$signature".trim() signatures += all } - counter = 0 IO.foreachFileIn(output, processFile) signatures.result From f035f102069bbbb6b438b0d18f871a68cfa29987 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 10 Mar 2023 15:39:04 +0100 Subject: [PATCH 337/657] Fix quotes with references to path dependent types --- .../dotty/tools/dotc/staging/HealType.scala | 43 ++++++++---- .../tools/dotc/transform/PickleQuotes.scala | 7 +- .../tools/dotc/transform/TreeChecker.scala | 4 +- .../path-dependent-type-capture/Macro_1.scala | 70 +++++++++++++++++++ .../path-dependent-type-capture/Test_2.scala | 1 + 5 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 tests/pos-macros/path-dependent-type-capture/Macro_1.scala create mode 100644 tests/pos-macros/path-dependent-type-capture/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 7907c2e47542..4f59e92241fb 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -31,15 +31,12 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { */ def apply(tp: Type): Type = tp match - case tp: TypeRef => - tp.underlying match - case TypeAlias(alias) - if !tp.symbol.isTypeSplice && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => - this.apply(alias) - case _ => - healTypeRef(tp) - case tp @ TermRef(NoPrefix, _) if !tp.symbol.isStatic && level > levelOf(tp.symbol) => - levelError(tp.symbol, tp, pos) + case NonSpliceAlias(aliased) => this.apply(aliased) + case tp: TypeRef => healTypeRef(tp) + case tp: TermRef => + val inconsistentRoot = levelInconsistentRootOfPath(tp) + if inconsistentRoot.exists then levelError(inconsistentRoot, tp, pos) + else tp case tp: AnnotatedType => derivedAnnotatedType(tp, apply(tp.parent), tp.annot) case _ => @@ -47,23 +44,39 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { private def healTypeRef(tp: TypeRef): Type = tp.prefix match + case NoPrefix if tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => + tp case prefix: TermRef if tp.symbol.isTypeSplice => checkNotWildcardSplice(tp) if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix) - case prefix: TermRef if !prefix.symbol.isStatic && level > levelOf(prefix.symbol) => - tryHeal(prefix.symbol, tp, pos) - case NoPrefix if level > levelOf(tp.symbol) && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => - tryHeal(tp.symbol, tp, pos) - case prefix: ThisType if level > levelOf(prefix.cls) && !tp.symbol.isStatic => - tryHeal(tp.symbol, tp, pos) + case _: NamedType | _: ThisType | NoPrefix => + if levelInconsistentRootOfPath(tp).exists then + tryHeal(tp.symbol, tp, pos) + else + tp case _ => mapOver(tp) + private object NonSpliceAlias: + def unapply(tp: TypeRef)(using Context): Option[Type] = tp.underlying match + case TypeAlias(alias) if !tp.symbol.isTypeSplice && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => Some(alias) + case _ => None + private def checkNotWildcardSplice(splice: TypeRef): Unit = splice.prefix.termSymbol.info.argInfos match case (tb: TypeBounds) :: _ => report.error(em"Cannot splice $splice because it is a wildcard type", pos) case _ => + /** Return the root of this path if it is a variable defined in a previous level. + * If the path is consistent, return NoSymbol. + */ + private def levelInconsistentRootOfPath(tp: Type)(using Context): Symbol = + tp match + case tp @ NamedType(NoPrefix, _) if level > levelOf(tp.symbol) => tp.symbol + case tp: NamedType if !tp.symbol.isStatic => levelInconsistentRootOfPath(tp.prefix) + case tp: ThisType if level > levelOf(tp.cls) => tp.cls + case _ => NoSymbol + /** Try to heal reference to type `T` used in a higher level than its definition. * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 728ee9552c81..62174c806f09 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -157,13 +157,18 @@ class PickleQuotes extends MacroTransform { override def apply(tp: Type): Type = tp match case tp: TypeRef if tp.typeSymbol.isTypeSplice => apply(tp.dealias) - case tp @ TypeRef(pre, _) if pre == NoPrefix || pre.termSymbol.isLocal => + case tp @ TypeRef(pre, _) if isLocalPath(pre) => val hiBound = tp.typeSymbol.info match case info: ClassInfo => info.parents.reduce(_ & _) case info => info.hiBound apply(hiBound) case tp => mapOver(tp) + + private def isLocalPath(tp: Type): Boolean = tp match + case NoPrefix => true + case tp: TermRef if !tp.symbol.is(Package) => isLocalPath(tp.prefix) + case tp => false } /** Remove references to local types that will not be defined in this quote */ diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index f9966eac7fad..f9240d6091c4 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -679,7 +679,7 @@ object TreeChecker { defn.AnyType case tpe => tpe defn.QuotedExprClass.typeRef.appliedTo(tpe) - else defn.QuotedTypeClass.typeRef.appliedTo(arg.typeOpt) + else defn.QuotedTypeClass.typeRef.appliedTo(arg.typeOpt.widenTermRefExpr) } val expectedResultType = if isTermHole then defn.QuotedExprClass.typeRef.appliedTo(tpt.typeOpt) @@ -688,7 +688,7 @@ object TreeChecker { defn.FunctionOf(List(defn.QuotesClass.typeRef), expectedResultType, isContextual = true) val expectedContentType = defn.FunctionOf(argQuotedTypes, contextualResult) - assert(content.typeOpt =:= expectedContentType, i"expected content of the hole to be ${expectedContentType} but got ${content.typeOpt}") + assert(content.typeOpt =:= expectedContentType, i"unexpected content of hole\nexpected: ${expectedContentType}\nwas: ${content.typeOpt}") tree1 } diff --git a/tests/pos-macros/path-dependent-type-capture/Macro_1.scala b/tests/pos-macros/path-dependent-type-capture/Macro_1.scala new file mode 100644 index 000000000000..588e50846eff --- /dev/null +++ b/tests/pos-macros/path-dependent-type-capture/Macro_1.scala @@ -0,0 +1,70 @@ +import scala.quoted.* + +trait A: + type T + val b: B + +trait B: + type T + def f: Unit + +trait C0: + type U + val d: D0 +trait D0: + type U + def h: Unit +object Macro: + inline def generateCode: Unit = ${ generateCodeExpr } + + def generateCodeExpr(using Quotes): Expr[Unit] = + '{ + $testLocalPathsGlobalClasses + $testLocalPathsLocalClasses + } + + def testLocalPathsGlobalClasses(using Quotes): Expr[Unit] = + '{ + type T + val a: A = ??? + ${ + val expr = '{ + val t: T = ??? + val aT: a.T = ??? + val abT: a.b.T = ??? + val aRef: a.type = ??? + aRef.b + aRef.b.f + val abRef: a.b.type = ??? + abRef.f + () + } + expr + } + } + + def testLocalPathsLocalClasses(using Quotes): Expr[Unit] = + '{ + type U + trait C extends C0: + type U + val d: D + trait D extends D0: + type U + def h: Unit + val c: C = ??? + ${ + val expr = '{ + val u: U = ??? + val cU: c.U = ??? + val cdU: c.d.U = ??? + val cRef: c.type = ??? + cRef.d + cRef.d.h + val cdRef: c.d.type = ??? + cdRef.h + () + } + expr + } + } diff --git a/tests/pos-macros/path-dependent-type-capture/Test_2.scala b/tests/pos-macros/path-dependent-type-capture/Test_2.scala new file mode 100644 index 000000000000..c12cd8d2436a --- /dev/null +++ b/tests/pos-macros/path-dependent-type-capture/Test_2.scala @@ -0,0 +1 @@ +@main def test = Macro.generateCode From 1297e13a7e6a5b4f83fb2d6b47f3e0921826c2dd Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Mon, 3 Apr 2023 10:55:26 +0200 Subject: [PATCH 338/657] Drop network tests in requests community-build --- community-build/community-projects/requests-scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/community-build/community-projects/requests-scala b/community-build/community-projects/requests-scala index 2d7316a8f444..8e4a40588491 160000 --- a/community-build/community-projects/requests-scala +++ b/community-build/community-projects/requests-scala @@ -1 +1 @@ -Subproject commit 2d7316a8f444c2f38795e5905f90837e651c79c3 +Subproject commit 8e4a40588491608aa40099f79c881d54a5094e75 From ca3f7c6ea9b9c7f37cb2d07158652868dc3d084e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Mar 2023 14:33:53 +0100 Subject: [PATCH 339/657] Improve missing argument list error Fixes #17123 --- .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 10 +++ .../tools/dotc/typer/ErrorReporting.scala | 22 +++-- tests/neg/i16820.check | 24 ++++-- tests/neg/i17123.check | 86 +++++++++++++++++++ tests/neg/i17123.scala | 22 +++++ tests/neg/i7816.scala | 2 +- tests/neg/indent-colons.check | 28 ++++-- 8 files changed, 177 insertions(+), 18 deletions(-) create mode 100644 tests/neg/i17123.check create mode 100644 tests/neg/i17123.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 65ebeb02c667..1fe38ce5e801 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -191,6 +191,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case ValueDiscardingID // errorNumber 175 case UnusedNonUnitValueID // errorNumber 176 case ConstrProxyShadowsID // errorNumber 177 + case MissingArgumentListID // errorNumber: 178 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 9875c508c9fd..fe2df5ca6016 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1501,6 +1501,16 @@ class MissingArgument(pname: Name, methString: String)(using Context) else s"missing argument for parameter $pname of $methString" def explain(using Context) = "" +class MissingArgumentList(method: String, sym: Symbol)(using Context) + extends TypeMsg(MissingArgumentListID) { + def msg(using Context) = + val symDcl = if sym.exists then "\n\n " + sym.showDcl else "" + i"missing argument list for $method$symDcl" + def explain(using Context) = { + i"""Unapplied methods are only converted to functions when a function type is expected.""" + } +} + class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(using Context) extends TypeMismatchMsg( if which == "lower" then bound else tpe, diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 32b5fde689ec..126d109889e1 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -41,12 +41,24 @@ object ErrorReporting { errorType(WrongNumberOfTypeArgs(fntpe, expectedArgs, actual), pos) def missingArgs(tree: Tree, mt: Type)(using Context): Unit = + def isCallableWithoutArgumentsLists(mt: Type): Boolean = mt match + case pt: PolyType => isCallableWithoutArgumentsLists(pt.resType) + case mt: MethodType if mt.isImplicitMethod => isCallableWithoutArgumentsLists(mt.resType) + case mt: MethodType => false + case _ => true + def isCallableWithSingleEmptyArgumentList(mt: Type): Boolean = + mt match + case mt: MethodType if mt.paramNames.isEmpty => isCallableWithoutArgumentsLists(mt.resType) + case mt: MethodType if mt.isImplicitMethod => isCallableWithSingleEmptyArgumentList(mt.resType) + case pt: PolyType => isCallableWithSingleEmptyArgumentList(pt.resType) + case _ => false val meth = err.exprStr(methPart(tree)) - mt match - case mt: MethodType if mt.paramNames.isEmpty => - report.error(MissingEmptyArgumentList(meth), tree.srcPos) - case _ => - report.error(em"missing arguments for $meth", tree.srcPos) + val info = if tree.symbol.exists then tree.symbol.info else mt + if isCallableWithSingleEmptyArgumentList(info) then + report.error(MissingEmptyArgumentList(meth), tree.srcPos) + else + report.error(MissingArgumentList(meth, tree.symbol), tree.srcPos) + def matchReductionAddendum(tps: Type*)(using Context): String = val collectMatchTrace = new TypeAccumulator[String]: diff --git a/tests/neg/i16820.check b/tests/neg/i16820.check index f51bfafef899..48824d683244 100644 --- a/tests/neg/i16820.check +++ b/tests/neg/i16820.check @@ -1,18 +1,30 @@ --- Error: tests/neg/i16820.scala:5:11 ---------------------------------------------------------------------------------- +-- [E178] Type Error: tests/neg/i16820.scala:5:11 ---------------------------------------------------------------------- 5 | val x1 = f // error | ^ - | missing arguments for method f in object Test + | missing argument list for method f in object Test + | + | def f(xs: Int*): Int + | + | longer explanation available when compiling with `-explain` -- [E100] Syntax Error: tests/neg/i16820.scala:6:11 -------------------------------------------------------------------- 6 | val x2 = g // error | ^ | method g in object Test must be called with () argument | | longer explanation available when compiling with `-explain` --- Error: tests/neg/i16820.scala:7:40 ---------------------------------------------------------------------------------- +-- [E178] Type Error: tests/neg/i16820.scala:7:40 ---------------------------------------------------------------------- 7 | val x3 = java.nio.file.Paths.get(".").toRealPath // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | missing arguments for method toRealPath in trait Path --- Error: tests/neg/i16820.scala:11:14 --------------------------------------------------------------------------------- + | missing argument list for method toRealPath in trait Path + | + | def toRealPath(x$0: java.nio.file.LinkOption*): java.nio.file.Path + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i16820.scala:11:14 --------------------------------------------------------------------- 11 |def test = Foo(3) // error | ^^^^^^ - | missing arguments for method apply in object Foo + | missing argument list for method apply in object Foo + | + | def apply(x: Int)(xs: String*): Foo + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i17123.check b/tests/neg/i17123.check new file mode 100644 index 000000000000..e858de67b73a --- /dev/null +++ b/tests/neg/i17123.check @@ -0,0 +1,86 @@ +-- [E100] Syntax Error: tests/neg/i17123.scala:7:2 --------------------------------------------------------------------- +7 | m1 // error + | ^^ + | method m1 in object ConfusingErrorMessage must be called with () argument + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i17123.scala:9:2 ----------------------------------------------------------------------- +9 | m2 // error + | ^^ + | missing argument list for method m2 in object ConfusingErrorMessage + | + | def m2()(): Unit + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i17123.scala:10:4 ---------------------------------------------------------------------- +10 | m2() // error + | ^^^^ + | missing argument list for method m2 in object ConfusingErrorMessage + | + | def m2()(): Unit + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i17123.scala:11:2 ---------------------------------------------------------------------- +11 | m3 // error + | ^^ + | missing argument list for method m3 in object ConfusingErrorMessage + | + | def m3()()(): Unit + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i17123.scala:12:4 ---------------------------------------------------------------------- +12 | m3() // error + | ^^^^ + | missing argument list for method m3 in object ConfusingErrorMessage + | + | def m3()()(): Unit + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i17123.scala:13:6 ---------------------------------------------------------------------- +13 | m3()() // error + | ^^^^^^ + | missing argument list for method m3 in object ConfusingErrorMessage + | + | def m3()()(): Unit + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i17123.scala:15:2 ---------------------------------------------------------------------- +15 | f3 // error + | ^^ + | missing argument list for method f3 in object ConfusingErrorMessage + | + | def f3()(i: Int)(): Unit + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i17123.scala:16:2 ---------------------------------------------------------------------- +16 | f3() // error + | ^^^^ + | missing argument list for method f3 in object ConfusingErrorMessage + | + | def f3()(i: Int)(): Unit + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i17123.scala:17:6 ---------------------------------------------------------------------- +17 | f3()(2) // error + | ^^^^^^^ + | missing argument list for method f3 in object ConfusingErrorMessage + | + | def f3()(i: Int)(): Unit + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i17123.scala:19:2 ---------------------------------------------------------------------- +19 | i3 // error + | ^^ + | missing argument list for method i3 in object ConfusingErrorMessage + | + | def i3()(using d: DummyImplicit)(): Unit + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/i17123.scala:20:2 ---------------------------------------------------------------------- +20 | i3() // error + | ^^^^ + | missing argument list for method i3 in object ConfusingErrorMessage + | + | def i3()(using d: DummyImplicit)(): Unit + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i17123.scala b/tests/neg/i17123.scala new file mode 100644 index 000000000000..6547a375fec3 --- /dev/null +++ b/tests/neg/i17123.scala @@ -0,0 +1,22 @@ +object ConfusingErrorMessage { + def m1() = () + def m2()() = () + def m3()()() = () + def f3()(i: Int)() = () + def i3()(using d: DummyImplicit)() = () + m1 // error + m1() + m2 // error + m2() // error + m3 // error + m3() // error + m3()() // error + m3()()() + f3 // error + f3() // error + f3()(2) // error + f3()(2)() + i3 // error + i3() // error + i3()() +} diff --git a/tests/neg/i7816.scala b/tests/neg/i7816.scala index f1eed694a085..41dd6c2ea98e 100644 --- a/tests/neg/i7816.scala +++ b/tests/neg/i7816.scala @@ -1,4 +1,4 @@ object A { def f()(>) = ??? // error - import f.NonExistent // error + import f.NonExistent } \ No newline at end of file diff --git a/tests/neg/indent-colons.check b/tests/neg/indent-colons.check index 102d41592014..f77d491f8b8f 100644 --- a/tests/neg/indent-colons.check +++ b/tests/neg/indent-colons.check @@ -47,15 +47,31 @@ | Not found: file | | longer explanation available when compiling with `-explain` --- Error: tests/neg/indent-colons.scala:5:2 ---------------------------------------------------------------------------- +-- [E178] Type Error: tests/neg/indent-colons.scala:5:2 ---------------------------------------------------------------- 5 | tryEither: // error | ^^^^^^^^^ - | missing arguments for method tryEither --- Error: tests/neg/indent-colons.scala:11:2 --------------------------------------------------------------------------- + | missing argument list for method tryEither + | + | def tryEither[T](x: T)(y: Int => T): T + | + | where: T is a type variable + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/indent-colons.scala:11:2 --------------------------------------------------------------- 11 | tryEither: // error | ^^^^^^^^^ - | missing arguments for method tryEither --- Error: tests/neg/indent-colons.scala:18:2 --------------------------------------------------------------------------- + | missing argument list for method tryEither + | + | def tryEither[T](x: T)(y: Int => T): T + | + | where: T is a type variable + | + | longer explanation available when compiling with `-explain` +-- [E178] Type Error: tests/neg/indent-colons.scala:18:2 --------------------------------------------------------------- 18 | Some(3).fold: // error | ^^^^^^^^^^^^ - | missing arguments for method fold in class Option + | missing argument list for method fold in class Option + | + | final def fold[B](ifEmpty: => B)(f: A => B): B + | + | longer explanation available when compiling with `-explain` From 3420f1b400af1b1ba8f3e51bc384c718156f041d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Mar 2023 16:53:20 +0100 Subject: [PATCH 340/657] Improve highlight of decl --- compiler/src/dotty/tools/dotc/core/Contexts.scala | 6 ++++++ compiler/src/dotty/tools/dotc/inlines/Inlines.scala | 3 +-- compiler/src/dotty/tools/dotc/reporting/messages.scala | 2 +- compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala | 2 +- .../dotty/tools/dotc/printing/SyntaxHighlightingTests.scala | 3 +-- .../test/dotty/tools/dotc/reporting/ErrorMessagesTest.scala | 2 +- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 2f28975dd066..e0e43169820a 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -444,6 +444,12 @@ object Contexts { def useColors: Boolean = base.settings.color.value == "always" + def withColors: FreshContext = + fresh.setSetting(ctx.settings.color, "always") + + def withoutColors: FreshContext = + fresh.setSetting(ctx.settings.color, "never") + /** Is the explicit nulls option set? */ def explicitNulls: Boolean = base.settings.YexplicitNulls.value diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index f504a4034631..36dc8a642afc 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -381,8 +381,7 @@ object Inlines: /** Expand call to scala.compiletime.codeOf */ def codeOf(arg: Tree, pos: SrcPos)(using Context): Tree = - val ctx1 = ctx.fresh.setSetting(ctx.settings.color, "never") - Literal(Constant(arg.show(using ctx1))).withSpan(pos.span) + Literal(Constant(arg.show(using ctx.withoutColors))).withSpan(pos.span) end Intrinsics /** Produces an inlined version of `call` via its `inlined` method. diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index fe2df5ca6016..fba08fd84d0c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1504,7 +1504,7 @@ class MissingArgument(pname: Name, methString: String)(using Context) class MissingArgumentList(method: String, sym: Symbol)(using Context) extends TypeMsg(MissingArgumentListID) { def msg(using Context) = - val symDcl = if sym.exists then "\n\n " + sym.showDcl else "" + val symDcl = if sym.exists then "\n\n " + hl(sym.showDcl(using ctx.withoutColors)) else "" i"missing argument list for $method$symDcl" def explain(using Context) = { i"""Unapplied methods are only converted to functions when a function type is expected.""" diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index a575238f7cd4..1949304ca287 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -3080,7 +3080,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler lazy val ConstantCode: Printer[Constant] = new Printer[Constant]: def show(const: Constant): String = - const.show(using ctx.fresh.setSetting(ctx.settings.color, "never")) + const.show(using ctx.withoutColors) lazy val ConstantStructure: Printer[Constant] = new Printer[Constant]: def show(const: Constant): String = diff --git a/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala b/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala index 2f35ccb35434..2e4b7bf1bb3f 100644 --- a/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala +++ b/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala @@ -12,8 +12,7 @@ class SyntaxHighlightingTests extends DottyTest { import SyntaxHighlighting._ private def test(source: String, expected: String): Unit = { - val testCtx = ctx.fresh.setSetting(ctx.settings.color, "always") - val highlighted = SyntaxHighlighting.highlight(source)(using testCtx) + val highlighted = SyntaxHighlighting.highlight(source)(using ctx.withColors) .replace(NoColor, ">") .replace(CommentColor, " Date: Mon, 3 Apr 2023 15:34:59 +0200 Subject: [PATCH 341/657] Add scrollbar to the content and sidebar --- .../resources/dotty_res/styles/theme/layout/container.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/container.css b/scaladoc/resources/dotty_res/styles/theme/layout/container.css index d71c75e8bda0..a5210210c420 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/container.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/container.css @@ -20,6 +20,10 @@ p { } ::-webkit-scrollbar { - width: 0; + width: 5; background: transparent; } + +::-webkit-scrollbar-thumb { + background: black; +} From 67e70fe3a9ae7d3525cef78e69dd66e7b9802a5b Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 3 Apr 2023 15:53:05 +0200 Subject: [PATCH 342/657] Fix wunused for deriving alias type that has a different name --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 7 +++++-- libste | 0 tests/neg-custom-args/fatal-warnings/i15503i.scala | 11 +++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) delete mode 100644 libste diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6fc11d3ab007..6943fb1b12e6 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -590,9 +590,12 @@ object CheckUnused: private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) - val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).map(dealias).contains(dealiasedSym) + val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) + val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) + val allSelections = typeSelections ::: termSelections :::qual.tpe.member(sym.name).alternatives + val qualHasSymbol = allSelections.map(_.symbol).map(dealias).contains(dealiasedSym) def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) - def dealiasedSelector = sels.flatMap(sel => qual.tpe.member(sym.name).alternatives.map(m => (sel, m.symbol))).collect { + def dealiasedSelector = sels.flatMap(sel => allSelections.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) diff --git a/libste b/libste deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 9ac2ec5ef622..a76f96b3c89b 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -273,3 +273,14 @@ package foo.test.i16679b: import Foo.x case class CoolClass(i: Int) println(summon[myPackage.CaseClassName[CoolClass]]) + +package foo.test.i17156: + package a: + trait Foo[A] + + package b: + type Xd = Foo + + package c: + import b.Xd + trait Z derives Xd From c7a5d0b0e6d7f541ac1133a7866116a381507ccf Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 3 Apr 2023 19:45:58 +0200 Subject: [PATCH 343/657] Fix test for wunused alias deriving --- tests/neg-custom-args/fatal-warnings/i15503i.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index a76f96b3c89b..737e5f0739ca 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -277,9 +277,12 @@ package foo.test.i16679b: package foo.test.i17156: package a: trait Foo[A] + object Foo: + inline def derived[T]: Foo[T] = new Foo{} package b: - type Xd = Foo + import a.Foo + type Xd[A] = Foo[A] package c: import b.Xd From e1ab97e66711f847890c6b662be212dad9dd21ba Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 4 Apr 2023 09:37:52 +0200 Subject: [PATCH 344/657] Remove trailing whitespace --- .github/PULL_REQUEST_TEMPLATE/fix-issue.md | 6 +++--- .github/PULL_REQUEST_TEMPLATE/other-pr.md | 6 +++--- .github/workflows/releases.yml | 4 ++-- .github/workflows/scripts/publish-sdkman.sh | 4 ++-- .../benchmarks/lazyvals/InitializedAccess.scala | 2 +- .../lazyvals/InitializedAccessAny.scala | 2 +- .../lazyvals/InitializedAccessGeneric.scala | 2 +- .../lazyvals/InitializedAccessString.scala | 2 +- .../tools/benchmarks/lazyvals/LazyVals.scala | 2 +- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- compiler/src/dotty/tools/dotc/cc/Setup.scala | 2 +- .../src/dotty/tools/dotc/core/Annotations.scala | 4 ++-- .../src/dotty/tools/dotc/parsing/Parsers.scala | 16 ++++++++-------- .../dotty/tools/dotc/transform/LazyVals.scala | 4 ++-- compiler/src/dotty/tools/dotc/util/lrutest.sc | 6 +++--- compiler/src/dotty/tools/io/JDK9Reflectors.java | 2 +- .../dotty/tools/runner/ScalaClassLoader.scala | 2 +- compiler/test/worksheets/baseTypetest.sc | 2 +- compiler/test/worksheets/denotTest.sc | 4 ++-- compiler/test/worksheets/nesting.sc | 8 ++++---- compiler/test/worksheets/periodtest.sc | 4 ++-- compiler/test/worksheets/positiontest.sc | 2 +- compiler/test/worksheets/testnames.sc | 8 ++++---- library/src/scala/runtime/LazyVals.scala | 2 +- .../sbt-dotty/tasty-inspector-jars/build.sbt | 2 +- .../implicit-search/changes/A1.scala | 2 +- sbt-test/source-dependencies/java-static/test | 2 +- .../restore-classes/changes/A2.scala | 2 +- .../src/example/typeAndObjects/binaryops.scala | 2 +- scaladoc-testcases/src/tests/contextBounds.scala | 6 +++--- .../src/tests/snippetComments.scala | 4 ++-- 31 files changed, 59 insertions(+), 59 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE/fix-issue.md b/.github/PULL_REQUEST_TEMPLATE/fix-issue.md index 506996510c7e..f7cf22eb59c7 100644 --- a/.github/PULL_REQUEST_TEMPLATE/fix-issue.md +++ b/.github/PULL_REQUEST_TEMPLATE/fix-issue.md @@ -6,14 +6,14 @@ assignees: '' --- - ## Fix #XYZ - + diff --git a/.github/PULL_REQUEST_TEMPLATE/other-pr.md b/.github/PULL_REQUEST_TEMPLATE/other-pr.md index 4b69a80460af..fad49836df92 100644 --- a/.github/PULL_REQUEST_TEMPLATE/other-pr.md +++ b/.github/PULL_REQUEST_TEMPLATE/other-pr.md @@ -6,14 +6,14 @@ assignees: '' --- - ## Description - + diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 7415759078ac..ba4bae0456d0 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -13,9 +13,9 @@ jobs: options: --cpu-shares 4096 env: - SDKMAN_KEY: ${{ secrets.SDKMAN_KEY }} + SDKMAN_KEY: ${{ secrets.SDKMAN_KEY }} SDKMAN_TOKEN: ${{ secrets.SDKMAN_TOKEN }} - + steps: - name: Reset existing repo run: git -c "http.https://github.com/.extraheader=" fetch --recurse-submodules=no "https://github.com/lampepfl/dotty" && git reset --hard FETCH_HEAD || true diff --git a/.github/workflows/scripts/publish-sdkman.sh b/.github/workflows/scripts/publish-sdkman.sh index 07d35a72a65e..70987bff175b 100755 --- a/.github/workflows/scripts/publish-sdkman.sh +++ b/.github/workflows/scripts/publish-sdkman.sh @@ -9,11 +9,11 @@ set -u -# latest stable dotty version +# latest stable dotty version DOTTY_VERSION=$(curl -s https://api.github.com/repos/lampepfl/dotty/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') DOTTY_URL="https://github.com/lampepfl/dotty/releases/download/$DOTTY_VERSION/scala3-$DOTTY_VERSION.zip" -# checking if dotty version is available +# checking if dotty version is available if ! curl --output /dev/null --silent --head --fail "$DOTTY_URL"; then echo "URL doesn't exist: $DOTTY_URL" exit 1 diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccess.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccess.scala index a2aaf3e88570..d413458d0049 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccess.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccess.scala @@ -18,7 +18,7 @@ class InitializedAccess { @Setup def prepare: Unit = { - holder = new LazyHolder + holder = new LazyHolder holder.value } diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessAny.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessAny.scala index 5a6b4ae1686d..8c75f6bb11a2 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessAny.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessAny.scala @@ -18,7 +18,7 @@ class InitializedAccessAny { @Setup def prepare: Unit = { - holder = new LazyAnyHolder + holder = new LazyAnyHolder holder.value } diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessGeneric.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessGeneric.scala index a95cb1de2980..a9fecae6281e 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessGeneric.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessGeneric.scala @@ -18,7 +18,7 @@ class InitializedAccessGeneric { @Setup def prepare: Unit = { - holder = new LazyGenericHolder[String]("foo") + holder = new LazyGenericHolder[String]("foo") holder.value } diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessString.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessString.scala index 25cc0f9b288d..e6c6cd5eb2e3 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessString.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/InitializedAccessString.scala @@ -18,7 +18,7 @@ class InitializedAccessString { @Setup def prepare: Unit = { - holder = new LazyStringHolder + holder = new LazyStringHolder holder.value } diff --git a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala index 68379f9e142c..26ebb7b9d356 100644 --- a/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala +++ b/bench-micro/src/main/scala/dotty/tools/benchmarks/lazyvals/LazyVals.scala @@ -21,7 +21,7 @@ object LazyVals { } } } - + class LazyHolder { lazy val value: List[Int] = { diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index b03953afb37c..db3d42d32190 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -89,7 +89,7 @@ class Compiler { new ExplicitOuter, // Add accessors to outer classes from nested ones. new ExplicitSelf, // Make references to non-trivial self types explicit as casts new StringInterpolatorOpt, // Optimizes raw and s and f string interpolators by rewriting them to string concatenations or formats - new DropBreaks) :: // Optimize local Break throws by rewriting them + new DropBreaks) :: // Optimize local Break throws by rewriting them List(new PruneErasedDefs, // Drop erased definitions from scopes and simplify erased expressions new UninitializedDefs, // Replaces `compiletime.uninitialized` by `_` new InlinePatterns, // Remove placeholders of inlined patterns diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 5642ea99de1a..fc16422e1373 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -262,7 +262,7 @@ extends tpd.TreeTraverser: // hard-coded expansion since $throws aliases in stdlib are defined with `?=>` rather than `?->` defn.FunctionOf( AnnotatedType( - defn.CanThrowClass.typeRef.appliedTo(exc), + defn.CanThrowClass.typeRef.appliedTo(exc), Annotation(defn.ErasedParamAnnot, defn.CanThrowClass.span)) :: Nil, res, isContextual = true diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 3b00f2915f1c..202f3eb26e41 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -194,7 +194,7 @@ object Annotations { object Annotation { def apply(tree: Tree): ConcreteAnnotation = ConcreteAnnotation(tree) - + def apply(cls: ClassSymbol, span: Span)(using Context): Annotation = apply(cls, Nil, span) @@ -206,7 +206,7 @@ object Annotations { def apply(atp: Type, arg: Tree, span: Span)(using Context): Annotation = apply(atp, arg :: Nil, span) - + def apply(atp: Type, args: List[Tree], span: Span)(using Context): Annotation = apply(New(atp, args).withSpan(span)) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index d1b0c6cba097..15a639743c15 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3129,8 +3129,8 @@ object Parsers { /* -------- PARAMETERS ------------------------------------------- */ /** DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent - * DefParamClause ::= DefTypeParamClause - * | DefTermParamClause + * DefParamClause ::= DefTypeParamClause + * | DefTermParamClause * | UsingParamClause */ def typeOrTermParamClauses( @@ -3228,7 +3228,7 @@ object Parsers { * UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ * ClsParams ::= ClsParam {‘,’ ClsParam} * ClsParam ::= {Annotation} - * + * * TypelessClause ::= DefTermParamClause * | UsingParamClause * @@ -3616,13 +3616,13 @@ object Parsers { } } - + /** DefDef ::= DefSig [‘:’ Type] ‘=’ Expr * | this TypelessClauses [DefImplicitClause] `=' ConstrExpr * DefDcl ::= DefSig `:' Type * DefSig ::= id [DefTypeParamClause] DefTermParamClauses - * + * * if clauseInterleaving is enabled: * DefSig ::= id [DefParamClauses] [DefImplicitClause] */ @@ -3661,8 +3661,8 @@ object Parsers { val mods1 = addFlag(mods, Method) val ident = termIdent() var name = ident.name.asTermName - val paramss = - if in.featureEnabled(Feature.clauseInterleaving) then + val paramss = + if in.featureEnabled(Feature.clauseInterleaving) then // If you are making interleaving stable manually, please refer to the PR introducing it instead, section "How to make non-experimental" typeOrTermParamClauses(ParamOwner.Def, numLeadParams = numLeadParams) else @@ -3672,7 +3672,7 @@ object Parsers { joinParams(tparams, vparamss) var tpt = fromWithinReturnType { typedOpt() } - + if (migrateTo3) newLineOptWhenFollowedBy(LBRACE) val rhs = if in.token == EQUALS then diff --git a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala index e4cb21a279d6..b433e37e39c0 100644 --- a/compiler/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/compiler/src/dotty/tools/dotc/transform/LazyVals.scala @@ -283,7 +283,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { * * ``` * private @volatile var _x: AnyRef = null - * + * * def x: A = * val result = _x * if result.isInstanceOf[A] then @@ -292,7 +292,7 @@ class LazyVals extends MiniPhase with IdentityDenotTransformer { * null // possible unboxing applied here * else * x_compute() // possible unboxing applied here - * + * * private def x_compute(): AnyRef = * while do * val current: AnyRef = _x diff --git a/compiler/src/dotty/tools/dotc/util/lrutest.sc b/compiler/src/dotty/tools/dotc/util/lrutest.sc index 6e6328b248e3..9c811a65a70a 100644 --- a/compiler/src/dotty/tools/dotc/util/lrutest.sc +++ b/compiler/src/dotty/tools/dotc/util/lrutest.sc @@ -15,12 +15,12 @@ object lrutest { cache.last //> res4: Int = 6 cache lookup "hi" //> res5: String = x cache.indices.take(10).toList //> res6: List[Int] = List(7, 0, 1, 2, 3, 4, 5, 6, 7, 0) - + for (i <- 1 to 10) { if (cache.lookup(i.toString) == null) cache.enter(i.toString, i.toString) } - + cache.indices.take(10).toList //> res7: List[Int] = List(5, 6, 7, 0, 1, 2, 3, 4, 5, 6) cache //> res8: dotty.tools.dotc.util.LRUCache[String,String] = LRUCache(10 -> 10, 9 - //| > 9, 8 -> 8, 7 -> 7, 6 -> 6, 5 -> 5, 4 -> 4, 3 -> 3) @@ -35,6 +35,6 @@ object lrutest { //| > 10, 7 -> 7, 9 -> 9, 8 -> 8, 6 -> 6, 4 -> 4, 3 -> 3) cache.lookup("11") //> res16: String = null cache.enter("11", "!!") - cache //> res17: dotty.tools.dotc.util.LRUCache[String,String] = LRUCache(11 -> !!, 5 + cache //> res17: dotty.tools.dotc.util.LRUCache[String,String] = LRUCache(11 -> !!, 5 //| -> 5, 10 -> 10, 7 -> 7, 9 -> 9, 8 -> 8, 6 -> 6, 4 -> 4) } \ No newline at end of file diff --git a/compiler/src/dotty/tools/io/JDK9Reflectors.java b/compiler/src/dotty/tools/io/JDK9Reflectors.java index 1b0ce5deabab..9816cc03f92a 100644 --- a/compiler/src/dotty/tools/io/JDK9Reflectors.java +++ b/compiler/src/dotty/tools/io/JDK9Reflectors.java @@ -32,7 +32,7 @@ public final class JDK9Reflectors { } // Classes from java.lang.Runtime are not available in JDK 8 so using them explicitly would prevent this file from compiling with JDK 8 - // but these methods are not called in runtime when using this version of JDK + // but these methods are not called in runtime when using this version of JDK public static /*java.lang.Runtime.Version*/ Object runtimeVersionParse(String string) { try { diff --git a/compiler/src/dotty/tools/runner/ScalaClassLoader.scala b/compiler/src/dotty/tools/runner/ScalaClassLoader.scala index 3c8c51d8d6b2..9ec0199abcbb 100644 --- a/compiler/src/dotty/tools/runner/ScalaClassLoader.scala +++ b/compiler/src/dotty/tools/runner/ScalaClassLoader.scala @@ -67,7 +67,7 @@ object ScalaClassLoader { @sharable private[this] val bootClassLoader: ClassLoader = if scala.util.Properties.isJavaAtLeast("9") then try - ClassLoader.getSystemClassLoader.getParent + ClassLoader.getSystemClassLoader.getParent catch case _: Throwable => null else null diff --git a/compiler/test/worksheets/baseTypetest.sc b/compiler/test/worksheets/baseTypetest.sc index 001f1e3b3eaa..4dbd68a6fdc7 100644 --- a/compiler/test/worksheets/baseTypetest.sc +++ b/compiler/test/worksheets/baseTypetest.sc @@ -22,5 +22,5 @@ object baseTypetest extends DottyTest { defn.StringClass isSubClass defn.NullClass //> res4: Boolean = false defn.StringClass.typeRef.baseType(defn.NullClass) //> res5: dotty.tools.dotc.core.Types.Type = NoType - + } \ No newline at end of file diff --git a/compiler/test/worksheets/denotTest.sc b/compiler/test/worksheets/denotTest.sc index 222a347b6947..aa3fb383bd6f 100644 --- a/compiler/test/worksheets/denotTest.sc +++ b/compiler/test/worksheets/denotTest.sc @@ -7,7 +7,7 @@ import Types._, Symbols._ object denotTest extends DottyTest { println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet - + val str = defn.StringClass.typeRef //> str : dotty.tools.dotc.core.Types.TypeRef = TypeRef(ThisType(module class l //| ang#57),String) val d= str.member("getBytes".toTermName) //> d : dotty.tools.dotc.core.Denotations.Denotation = val getBytes val g @@ -47,7 +47,7 @@ object denotTest extends DottyTest { //| a#35),Array), scala$Array$$T, TypeAlias(TypeRef(ThisType(module class scala# //| 35),Char))), TypeRef(ThisType(module class scala#35),Int), TypeRef(ThisType( //| module class scala#35),Int)), TypeRef(ThisType(module class lang#57),String) - //| ), JavaMethodType(List(x$0), List(RefinedType(TypeRef(ThisType(module class + //| ), JavaMethodType(List(x$0), List(RefinedType(TypeRef(ThisType(module class //| scala#35),Array), scala$Array$$T, TypeAlias(TypeRef(ThisType(module class sc //| ala#35),Char)))), TypeRef(ThisType(module class lang#57),String)), JavaMetho //| dType(List(x$0), List(TypeRef(ThisType(module class scala#35),Any)), TypeRef diff --git a/compiler/test/worksheets/nesting.sc b/compiler/test/worksheets/nesting.sc index a6fc924320a0..bb3e9a71146e 100644 --- a/compiler/test/worksheets/nesting.sc +++ b/compiler/test/worksheets/nesting.sc @@ -2,7 +2,7 @@ package dotty.tools.dotc.core object nesting { class C { - + class D { private def x = "D" def show = x @@ -10,7 +10,7 @@ object nesting { println(x) } } - + val foo: D = { class D extends C.this.D { private def x = "foo.D" @@ -21,11 +21,11 @@ object nesting { new D } } - + val c = new C //> c : dotty.tools.dotc.core.nesting.C = dotty.tools.dotc.core.nesting$C@1a84d //| a23 val d = c.foo //> d : dotty.tools.dotc.core.nesting.c.D = dotty.tools.dotc.core.nesting$C$D$1 //| @2705d88a d.show //> res0: String = foo.D - + } \ No newline at end of file diff --git a/compiler/test/worksheets/periodtest.sc b/compiler/test/worksheets/periodtest.sc index 09c02da19a10..68a7cc43b20e 100644 --- a/compiler/test/worksheets/periodtest.sc +++ b/compiler/test/worksheets/periodtest.sc @@ -2,9 +2,9 @@ package dotty.tools.dotc.core object periodtest { println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet - + import Periods._ - + val p1 = Period(1, 2, 7) //> p1 : dotty.tools.dotc.core.Periods.Period = Period(2..7, run = 1) val p2 = Period(1, 3, 7) //> p2 : dotty.tools.dotc.core.Periods.Period = Period(3..7, run = 1) p1 contains p2 //> res0: Boolean = true diff --git a/compiler/test/worksheets/positiontest.sc b/compiler/test/worksheets/positiontest.sc index 11cc54dbeab9..b152368145f1 100644 --- a/compiler/test/worksheets/positiontest.sc +++ b/compiler/test/worksheets/positiontest.sc @@ -5,7 +5,7 @@ import Positions._ object positiontest { println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet - + val p = Position(0, 1, 0) //> p : dotty.tools.dotc.util.Positions.Position = [0..1] val p2 = Position(0, 2) //> p2 : dotty.tools.dotc.util.Positions.Position = [0..2] val p3 = Position(1, 0) //> p3 : dotty.tools.dotc.util.Positions.Position = [no position] diff --git a/compiler/test/worksheets/testnames.sc b/compiler/test/worksheets/testnames.sc index 282b07d4edb7..8f042b7036fd 100644 --- a/compiler/test/worksheets/testnames.sc +++ b/compiler/test/worksheets/testnames.sc @@ -2,7 +2,7 @@ package dotty.tools.dotc.core object testnames { println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet - + import Names._ val n = termName("hello") //> n : dotty.tools.dotc.core.Names.TermName = hello val tn = n.toTypeName //> tn : dotty.tools.dotc.core.Names.TypeName = hello @@ -10,7 +10,7 @@ object testnames { assert(tn.toTermName eq n) assert(tn.toLocalName eq ln) assert(n.toLocalName eq ln) - + n == tn //> res0: Boolean = false n == ln //> res1: Boolean = false n eq tn //> res2: Boolean = false @@ -19,7 +19,7 @@ object testnames { val foo = encodedTermName("++") //> foo : dotty.tools.dotc.core.Names.TermName = $plus$plus foo.hashCode //> res5: Int = 5 foo.toTypeName.hashCode //> res6: Int = -5 - + val nfoo = n ++ foo //> nfoo : dotty.tools.dotc.core.testnames.n.ThisName = hello$plus$plus nfoo contains '$' //> res7: Boolean = true nfoo.replace('$', '.') //> res8: dotty.tools.dotc.core.testnames.nfoo.ThisName = hello.plus.plus @@ -36,7 +36,7 @@ object testnames { termName("abc") //> res18: dotty.tools.dotc.core.Names.TermName = abc nfoo.filter(_ >= 'l') //> res19: dotty.tools.dotc.core.Names.Name = lloplusplus nfoo map (_.toUpper) //> res20: dotty.tools.dotc.core.Names.Name = HELLO$PLUS$PLUS - + import Decorators._ val local = "local".toTermName.toLocalName //> local : dotty.tools.dotc.core.Names.LocalName = local diff --git a/library/src/scala/runtime/LazyVals.scala b/library/src/scala/runtime/LazyVals.scala index d8c89c7abf28..0edbe0e748f4 100644 --- a/library/src/scala/runtime/LazyVals.scala +++ b/library/src/scala/runtime/LazyVals.scala @@ -10,7 +10,7 @@ import scala.annotation.* object LazyVals { @nowarn private[this] val unsafe: sun.misc.Unsafe = { - def throwInitializationException() = + def throwInitializationException() = throw new ExceptionInInitializerError( new IllegalStateException("Can't find instance of sun.misc.Unsafe") ) diff --git a/sbt-test/sbt-dotty/tasty-inspector-jars/build.sbt b/sbt-test/sbt-dotty/tasty-inspector-jars/build.sbt index 59dd85290bf0..3bece1b43fa7 100644 --- a/sbt-test/sbt-dotty/tasty-inspector-jars/build.sbt +++ b/sbt-test/sbt-dotty/tasty-inspector-jars/build.sbt @@ -15,7 +15,7 @@ lazy val inspector = project .settings( scalaVersion := dottyVersion, libraryDependencies += "org.scala-lang" %% "scala3-tasty-inspector" % scalaVersion.value, - runTest := + runTest := Def.sequential( Def.task(IO.copyFile((lib/Compile/packageBin).value, jarDest)), (Compile/run).toTask(" " + jarDest.getAbsolutePath) diff --git a/sbt-test/source-dependencies/implicit-search/changes/A1.scala b/sbt-test/source-dependencies/implicit-search/changes/A1.scala index 7aa91d096277..69c493db2131 100644 --- a/sbt-test/source-dependencies/implicit-search/changes/A1.scala +++ b/sbt-test/source-dependencies/implicit-search/changes/A1.scala @@ -1 +1 @@ -object A +object A diff --git a/sbt-test/source-dependencies/java-static/test b/sbt-test/source-dependencies/java-static/test index 42890ca74f4d..0bb6f50169e4 100644 --- a/sbt-test/source-dependencies/java-static/test +++ b/sbt-test/source-dependencies/java-static/test @@ -2,7 +2,7 @@ # the statics as an object without a file and so the Analyzer must know to look for the # object's linked class. # This test verifies this happens. -# The test compiles a Java class with a static field. +# The test compiles a Java class with a static field. # It then adds a Scala object that references the static field. Because the object only depends on a # static member and because the Java source is not included in the compilation (since it didn't change), # this triggers the special case above. diff --git a/sbt-test/source-dependencies/restore-classes/changes/A2.scala b/sbt-test/source-dependencies/restore-classes/changes/A2.scala index 10d738255cca..778f16ab95cc 100644 --- a/sbt-test/source-dependencies/restore-classes/changes/A2.scala +++ b/sbt-test/source-dependencies/restore-classes/changes/A2.scala @@ -2,4 +2,4 @@ object A { val x = "a" } -class C +class C diff --git a/scaladoc-testcases/src/example/typeAndObjects/binaryops.scala b/scaladoc-testcases/src/example/typeAndObjects/binaryops.scala index 890d39732ca8..6474d04f91ef 100644 --- a/scaladoc-testcases/src/example/typeAndObjects/binaryops.scala +++ b/scaladoc-testcases/src/example/typeAndObjects/binaryops.scala @@ -5,7 +5,7 @@ sealed trait Expr object Expr{ case class BinaryOp(offset: Int, lhs: Expr, op: BinaryOp.Op, rhs: Expr) extends Expr - + object BinaryOp{ sealed trait Op case object `<<` extends Op diff --git a/scaladoc-testcases/src/tests/contextBounds.scala b/scaladoc-testcases/src/tests/contextBounds.scala index 1925f7f40994..794af0b8b8f8 100644 --- a/scaladoc-testcases/src/tests/contextBounds.scala +++ b/scaladoc-testcases/src/tests/contextBounds.scala @@ -25,15 +25,15 @@ class A: def a[T <: String | Int : ([T] =>> T match { case String => A case Int => B })](t: T): T = t - def falsePositive[T](evidence$1: ClassTag[T]): Int + def falsePositive[T](evidence$1: ClassTag[T]): Int = 1 // Scala spec stats that behaviour of names with `$` is undefined. // Scaladoc documents definition below as `def falsePositive2[T: ClassTag]: Int` // that is equivalent of methods below - // def falsePositive2[T](implicit evidence$3: ClassTag[T]): Int + // def falsePositive2[T](implicit evidence$3: ClassTag[T]): Int // = 1 class Outer[A]: - def falsePositiveInner[T](implicit evidence$3: ClassTag[A]): Int + def falsePositiveInner[T](implicit evidence$3: ClassTag[A]): Int = 1 \ No newline at end of file diff --git a/scaladoc-testcases/src/tests/snippetComments.scala b/scaladoc-testcases/src/tests/snippetComments.scala index 39b15648103e..9f54b8a465f1 100644 --- a/scaladoc-testcases/src/tests/snippetComments.scala +++ b/scaladoc-testcases/src/tests/snippetComments.scala @@ -3,7 +3,7 @@ package tests.snippetComments /** * This is my codeblock - * + * * ``` * //{{ * import xd @@ -20,7 +20,7 @@ package tests.snippetComments * val y = 2 // comment in the same line * // comment in new line * val z = 3 - * + * * //{{ * val hideMe = 7 * //}} From 93cc31d54efc988d41321430d973d1c7191a3f12 Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 4 Apr 2023 10:32:37 +0200 Subject: [PATCH 345/657] Add scrollbar in the sidebar --- .../dotty_res/styles/theme/layout/container.css | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/container.css b/scaladoc/resources/dotty_res/styles/theme/layout/container.css index a5210210c420..849235e2fa82 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/container.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/container.css @@ -19,11 +19,18 @@ p { --header-height: calc(8 * var(--base-spacing)); } +/* Scrollbar */ + ::-webkit-scrollbar { - width: 5; + width: 0; background: transparent; } -::-webkit-scrollbar-thumb { - background: black; +#leftColumn ::-webkit-scrollbar{ + width: 5px; } + +#leftColumn ::-webkit-scrollbar-thumb { + background: var(--code-syntax-highlighting-scrollbar); + border-radius: 2px; +} \ No newline at end of file From e7f310bf3d2b909fcb180641f8cd50bac971fb88 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 4 Apr 2023 14:43:12 +0200 Subject: [PATCH 346/657] Fix selecting unaliased selector in wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6943fb1b12e6..0ebf1c090ee3 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -590,12 +590,13 @@ object CheckUnused: private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) + val simpleSelections = qual.tpe.member(sym.name).alternatives val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) - val allSelections = typeSelections ::: termSelections :::qual.tpe.member(sym.name).alternatives - val qualHasSymbol = allSelections.map(_.symbol).map(dealias).contains(dealiasedSym) + val selectionsToDealias = typeSelections ::: termSelections + val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) - def dealiasedSelector = sels.flatMap(sel => allSelections.map(m => (sel, m.symbol))).collect { + def dealiasedSelector = sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) @@ -604,6 +605,7 @@ object CheckUnused: else None + private def dealias(symbol: Symbol)(using Context): Symbol = if(symbol.isType && symbol.asType.denot.isAliasType) then symbol.asType.typeRef.dealias.typeSymbol From cbbae64eace76b8910170fd3022d1231a7de7ea5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 4 Apr 2023 15:36:03 +0200 Subject: [PATCH 347/657] Use TermRef to distinguish distinct Type[T] instances We used the symbol before, but this ignored the specific path to the instance of Type[T]. Fixes #16959 --- .../tools/dotc/staging/QuoteTypeTags.scala | 6 +++--- .../dotty/tools/dotc/transform/Splicing.scala | 8 ++++---- tests/pos-macros/i16959/Macro_1.scala | 17 +++++++++++++++++ tests/pos-macros/i16959/Test_2.scala | 1 + 4 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 tests/pos-macros/i16959/Macro_1.scala create mode 100644 tests/pos-macros/i16959/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala b/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala index c4c9c611be3a..d6f0a6576085 100644 --- a/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala +++ b/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala @@ -27,10 +27,10 @@ object QuoteTypeTags { class QuoteTypeTags(span: Span)(using Context) { import tpd.* - private val tags = collection.mutable.LinkedHashMap.empty[Symbol, TypeDef] + private val tags = collection.mutable.LinkedHashMap.empty[TermRef, TypeDef] def getTagRef(spliced: TermRef): TypeRef = { - val typeDef = tags.getOrElseUpdate(spliced.symbol, mkTagSymbolAndAssignType(spliced)) + val typeDef = tags.getOrElseUpdate(spliced, mkTagSymbolAndAssignType(spliced)) typeDef.symbol.typeRef } @@ -42,7 +42,7 @@ class QuoteTypeTags(span: Span)(using Context) { val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs, EmptyTree) val local = newSymbol( owner = ctx.owner, - name = UniqueName.fresh((splicedTree.symbol.name.toString + "$_").toTermName).toTypeName, + name = UniqueName.fresh(rhs.tpe.dealias.typeSymbol.name.toTypeName), flags = Synthetic, info = TypeAlias(splicedTree.tpe.select(tpnme.Underlying)), coord = span).asType diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index bb82fba32a7c..e959f5d1451d 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -108,8 +108,8 @@ class Splicing extends MacroTransform: /** Number of holes created in this quote. Used for indexing holes. */ private var numHoles = 0 - /** Mapping from the term symbol of a `Type[T]` to it's hole. Used to deduplicate type holes. */ - private val typeHoles = mutable.Map.empty[Symbol, Hole] + /** Mapping from the term of a `Type[T]` to it's hole. Used to deduplicate type holes. */ + private val typeHoles = mutable.Map.empty[TermRef, Hole] override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match @@ -127,13 +127,13 @@ class Splicing extends MacroTransform: case tree: TypeDef if tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => val tp @ TypeRef(qual: TermRef, _) = tree.rhs.tpe.hiBound: @unchecked quotedDefs += tree.symbol - val hole = typeHoles.get(qual.symbol) match + val hole = typeHoles.get(qual) match case Some (hole) => cpy.Hole(hole)(content = EmptyTree) case None => val holeIdx = numHoles numHoles += 1 val hole = tpd.Hole(false, holeIdx, Nil, ref(qual), TypeTree(tp)) - typeHoles.put(qual.symbol, hole) + typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) case Apply(Select(Apply(TypeApply(fn,_), List(code)),nme.apply),List(quotes)) diff --git a/tests/pos-macros/i16959/Macro_1.scala b/tests/pos-macros/i16959/Macro_1.scala new file mode 100644 index 000000000000..61483bff7ff1 --- /dev/null +++ b/tests/pos-macros/i16959/Macro_1.scala @@ -0,0 +1,17 @@ +import scala.quoted.* + +inline def test = ${ testImpl } + +def testImpl(using Quotes) = + import quotes.reflect.* + + val int = PackedType[Int] + val string = PackedType[String] + + assert(Type.show[(int.U, string.U, string.U)] == "scala.Tuple3[scala.Int, java.lang.String, java.lang.String]") + + '{ () } + +final class PackedType[T](using t: Type[T]): + opaque type U = T + given tpe: Type[U] = t diff --git a/tests/pos-macros/i16959/Test_2.scala b/tests/pos-macros/i16959/Test_2.scala new file mode 100644 index 000000000000..e9772d026451 --- /dev/null +++ b/tests/pos-macros/i16959/Test_2.scala @@ -0,0 +1 @@ +def app = test From 57a6867935840447a25c63b33b7612ab7422542d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 4 Apr 2023 15:39:21 +0200 Subject: [PATCH 348/657] Simplify tryHeal signature --- .../src/dotty/tools/dotc/staging/CrossStageSafety.scala | 4 ++-- compiler/src/dotty/tools/dotc/staging/HealType.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/Staging.scala | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 98e060488f43..ce0f323fe31a 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -206,8 +206,8 @@ class CrossStageSafety extends TreeMapWithStages { * The tag is generated by an instance of `QuoteTypeTags` directly if the splice is explicit * or indirectly by `tryHeal`. */ - protected def healType(pos: SrcPos)(using Context) = - new HealType(pos) + protected def healType(pos: SrcPos)(tpe: Type)(using Context) = + new HealType(pos).apply(tpe) /** Check level consistency of terms references */ private def checkLevelConsistency(tree: Ident | This)(using Context): Unit = diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 4f59e92241fb..50c2584b2fd0 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -51,7 +51,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix) case _: NamedType | _: ThisType | NoPrefix => if levelInconsistentRootOfPath(tp).exists then - tryHeal(tp.symbol, tp, pos) + tryHeal(tp) else tp case _ => @@ -82,7 +82,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. * Emits an error if `T` cannot be healed and returns `T`. */ - protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): Type = { + protected def tryHeal(tp: TypeRef): Type = { val reqType = defn.QuotedTypeClass.typeRef.appliedTo(tp) val tag = ctx.typer.inferImplicitArg(reqType, pos.span) tag.tpe match diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 83b2bcdbcaa6..4fb5f5910440 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -36,8 +36,9 @@ class Staging extends MacroTransform { tree match { case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass => val checker = new CrossStageSafety { - override protected def healType(pos: SrcPos)(using Context) = new HealType(pos) { - override protected def tryHeal(sym: Symbol, tp: TypeRef, pos: SrcPos): TypeRef = { + override protected def healType(pos: SrcPos)(tpe: Type)(using Context) = new HealType(pos) { + override protected def tryHeal(tp: TypeRef): TypeRef = { + val sym = tp.symbol def symStr = if (sym.is(ModuleClass)) sym.sourceModule.show else i"${sym.name}.this" @@ -51,7 +52,7 @@ class Staging extends MacroTransform { tp } - } + }.apply(tpe) } checker.transform(tree) case _ => From b08729b43722ca6aa64b0ffb38489fcc09b451f2 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 5 Apr 2023 09:56:46 +0200 Subject: [PATCH 349/657] Check level consistency of SingletonTypeTree as a type Fixes #8887 --- compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala | 2 +- tests/neg-macros/i8887.scala | 5 +++++ tests/neg-macros/quote-this-a.scala | 4 +--- tests/pos-macros/i8887.scala | 3 +++ 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 tests/neg-macros/i8887.scala create mode 100644 tests/pos-macros/i8887.scala diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 98e060488f43..e3928538a183 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -60,7 +60,7 @@ class CrossStageSafety extends TreeMapWithStages { val healedType = healType(tree.srcPos)(tp1) if healedType == tree.tpe then tree else TypeTree(healedType).withSpan(tree.span) - case _: RefTree if tree.isType => + case _: RefTree | _: SingletonTypeTree if tree.isType => val healedType = healType(tree.srcPos)(tree.tpe) if healedType == tree.tpe then tree else TypeTree(healedType).withSpan(tree.span) diff --git a/tests/neg-macros/i8887.scala b/tests/neg-macros/i8887.scala new file mode 100644 index 000000000000..944544ba33dc --- /dev/null +++ b/tests/neg-macros/i8887.scala @@ -0,0 +1,5 @@ +import scala.quoted._ + +def expr[X](x: Any)(using Quotes): Expr[Any] = + '{ foo[x.type] } // error +def foo[X]: Any = ??? diff --git a/tests/neg-macros/quote-this-a.scala b/tests/neg-macros/quote-this-a.scala index 11621176526b..9f71aca0a7fb 100644 --- a/tests/neg-macros/quote-this-a.scala +++ b/tests/neg-macros/quote-this-a.scala @@ -4,9 +4,7 @@ class Foo { def f(using Quotes): Unit = '{ def bar[T](x: T): T = x - bar[ - this.type // error - ] { + bar[this.type] { this // error } } diff --git a/tests/pos-macros/i8887.scala b/tests/pos-macros/i8887.scala new file mode 100644 index 000000000000..5bfd5501063b --- /dev/null +++ b/tests/pos-macros/i8887.scala @@ -0,0 +1,3 @@ +import scala.quoted._ +inline def foo(x: Any): Any = ${ expr[x.type] } +def expr[X](using Quotes): Expr[Any] = ??? From f2fc7444ba61fb23849771f66d8ddaacf16b78c1 Mon Sep 17 00:00:00 2001 From: Decel <8268812+Decel@users.noreply.github.com> Date: Thu, 6 Apr 2023 16:53:46 +0200 Subject: [PATCH 350/657] add regression test --- tests/pos/i14351.scala | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/pos/i14351.scala diff --git a/tests/pos/i14351.scala b/tests/pos/i14351.scala new file mode 100644 index 000000000000..556e6b1ed64e --- /dev/null +++ b/tests/pos/i14351.scala @@ -0,0 +1 @@ +val p: (Option[Int], Option[String]) = (1,"foo").map([T] => (x: T) => Option.apply[T](x)) From 4c9bdf00aedd7aac948aadf0cf7d5bd6734e97d6 Mon Sep 17 00:00:00 2001 From: brandonspark Date: Fri, 7 Apr 2023 15:15:59 -0700 Subject: [PATCH 351/657] documentation: fix tokens that can start indentation region --- compiler/src/dotty/tools/dotc/parsing/Scanners.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index b3d824a2efd2..933661b7c5c9 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -523,7 +523,8 @@ object Scanners { * * The following tokens can start an indentation region: * - * : = => <- if then else while do try catch finally for yield match + * : = => <- if then else while do try catch + * finally for yield match throw return with * * Inserting an INDENT starts a new indentation region with the indentation of the current * token as indentation width. From cd5affc7dfa5f5a3bdd8b4e5aef7d346c84e5b81 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 10 Apr 2023 19:41:15 +0200 Subject: [PATCH 352/657] Dealias only conditionally when symbol is derived val type in wunused --- .../tools/dotc/transform/CheckUnused.scala | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 0ebf1c090ee3..ff1b54ec2d76 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -24,7 +24,7 @@ import dotty.tools.dotc.core.Annotations import dotty.tools.dotc.core.Definitions import dotty.tools.dotc.core.NameKinds.WildcardParamName import dotty.tools.dotc.core.Symbols.Symbol - +import dotty.tools.dotc.core.StdNames.nme /** @@ -109,6 +109,9 @@ class CheckUnused extends MiniPhase: traverseAnnotations(tree.symbol) if !tree.symbol.is(Module) then ud.registerDef(tree) + if tree.name.mangledString.startsWith(nme.derived.mangledString + "$") + && tree.typeOpt != NoType then + ud.registerUsed(tree.typeOpt.typeSymbol, None, true) ud.addIgnoredUsage(tree.symbol) } @@ -304,7 +307,7 @@ object CheckUnused: * * See the `isAccessibleAsIdent` extension method below in the file */ - private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name])]()) + private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name], Boolean)]()) private val usedInPosition = MutSet[(SrcPos, Name)]() /* unused import collected during traversal */ private val unusedImport = MutSet[ImportSelector]() @@ -347,14 +350,14 @@ object CheckUnused: * The optional name will be used to target the right import * as the same element can be imported with different renaming */ - def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit = + def registerUsed(sym: Symbol, name: Option[Name], isDerived: Boolean = false)(using Context): Unit = if !isConstructorOfSynth(sym) && !doNotRegister(sym) then if sym.isConstructor && sym.exists then registerUsed(sym.owner, None) // constructor are "implicitly" imported with the class else - usedInScope.top += ((sym, sym.isAccessibleAsIdent, name)) - usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name)) - usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name)) + usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived)) + usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived)) + usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived)) name.map(n => usedInPosition += ((sym.sourcePos, n))) /** Register a symbol that should be ignored */ @@ -408,15 +411,15 @@ object CheckUnused: // used symbol in this scope val used = usedInScope.pop().toSet // used imports in this scope - val imports = impInScope.pop().toSet + val imports = impInScope.pop() val kept = used.filterNot { t => - val (sym, isAccessible, optName) = t + val (sym, isAccessible, optName, isDerived) = t // keep the symbol for outer scope, if it matches **no** import // This is the first matching wildcard selector var selWildCard: Option[ImportSelector] = None val exists = imports.exists { imp => - sym.isInImport(imp, isAccessible, optName) match + sym.isInImport(imp, isAccessible, optName, isDerived) match case None => false case optSel@Some(sel) if sel.isWildcard => if selWildCard.isEmpty then selWildCard = optSel @@ -587,7 +590,7 @@ object CheckUnused: } /** Given an import and accessibility, return an option of selector that match import<->symbol */ - private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = + private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name], isDerived: Boolean)(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) val simpleSelections = qual.tpe.member(sym.name).alternatives @@ -596,9 +599,9 @@ object CheckUnused: val selectionsToDealias = typeSelections ::: termSelections val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) - def dealiasedSelector = sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { + def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel - }.headOption + }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) if qualHasSymbol && !isAccessible && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) From 6195663f444cd715eea49e8012a59cfaf9d49152 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 10 Apr 2023 20:19:48 +0200 Subject: [PATCH 353/657] WIP: Disable WUnused for params of non-private defs --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 12 +++++++++++- .../fatal-warnings/i15503-scala2/scala2-t11681.scala | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index d7c88a1fca40..d49e69c46206 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -114,6 +114,10 @@ class CheckUnused extends MiniPhase: override def prepareForDefDef(tree: tpd.DefDef)(using Context): Context = unusedDataApply{ ud => + if !tree.rawMods.is(Private) then + tree.termParamss.flatten.foreach { p => + ud.addIgnoredParam(p.symbol) + } import ud.registerTrivial tree.registerTrivial traverseAnnotations(tree.symbol) @@ -331,6 +335,8 @@ object CheckUnused: /** Trivial definitions, avoid registering params */ private val trivialDefs = MutSet[Symbol]() + private val paramsToSkip = MutSet[Symbol]() + /** * Push a new Scope of the given type, executes the given Unit and * pop it back to the original type. @@ -365,6 +371,10 @@ object CheckUnused: def removeIgnoredUsage(sym: Symbol)(using Context): Unit = doNotRegister --= sym.everySymbol + def addIgnoredParam(sym: Symbol)(using Context): Unit = + paramsToSkip += sym + + /** Register an import */ def registerImport(imp: tpd.Import)(using Context): Unit = @@ -380,7 +390,7 @@ object CheckUnused: if memDef.isValidParam then if memDef.symbol.isOneOf(GivenOrImplicit) then implicitParamInScope += memDef - else + else if !paramsToSkip.contains(memDef.symbol) then explicitParamInScope += memDef else if currScopeType.top == ScopeType.Local then localDefInScope += memDef diff --git a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala index 18aa6879eeba..162bf9128a98 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala @@ -9,7 +9,7 @@ trait InterFace { } trait BadAPI extends InterFace { - def f(a: Int, + private def f(a: Int, b: String, // error c: Double): Int = { println(c) From 498abec4fe07279231f816c6747c914aadd38ef0 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 19:09:47 +0200 Subject: [PATCH 354/657] Fix WUnused for accessible symbols that are renamed --- .../dotty/tools/dotc/transform/CheckUnused.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503i.scala | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5a178ff2ec1f..fcbb61ab90e2 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,7 +603,7 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && !isAccessible && sym.exists then + if qualHasSymbol && (!isAccessible || symName.exists(_ != sym.name)) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 737e5f0739ca..fbdf47dae17a 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -287,3 +287,17 @@ package foo.test.i17156: package c: import b.Xd trait Z derives Xd + +package foo.test.i17117: + package example { + object test1 { + val test = "test" + } + + object test2 { + + import example.test1 as t1 + + val test = t1.test + } + } \ No newline at end of file From 25d1b63c3c9c7b0970113f6b17cfe5ff039077d7 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 29 Mar 2023 15:04:33 +0200 Subject: [PATCH 355/657] Change the CSS the logo for the generated documentation is adaptive - Add a fixed width of the span - Add a 100% for the img in the span --- .../resources/dotty_res/styles/theme/layout/header.css | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/header.css b/scaladoc/resources/dotty_res/styles/theme/layout/header.css index cf3b91db2698..85e6b0240899 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/header.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/header.css @@ -63,6 +63,14 @@ align-items: center; } +.logo-container .project-logo { + max-width: 40px; +} + +.logo-container .project-logo img { + max-width: 100%; +} + #mobile-menu-toggle { display: none; } From 781a398347963b1ac55bdf92f7f9fe916766fa73 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 11 Apr 2023 12:58:19 +0200 Subject: [PATCH 356/657] Remove NamedArg from inlined arguments Fixes #17227 --- .../dotty/tools/dotc/inlines/Inliner.scala | 17 +++++++++----- tests/pos-macros/i17227/Macro_1.scala | 22 +++++++++++++++++++ tests/pos-macros/i17227/Test_2.scala | 6 +++++ tests/pos/i17227.scala | 10 +++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 tests/pos-macros/i17227/Macro_1.scala create mode 100644 tests/pos-macros/i17227/Test_2.scala create mode 100644 tests/pos/i17227.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 872dc7793ff4..8b775a25fe0c 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -207,11 +207,18 @@ class Inliner(val call: tpd.Tree)(using Context): private[inlines] def paramBindingDef(name: Name, formal: Type, arg0: Tree, buf: DefBuffer)(using Context): ValOrDefDef = { val isByName = formal.dealias.isInstanceOf[ExprType] - val arg = arg0 match { - case Typed(arg1, tpt) if tpt.tpe.isRepeatedParam && arg1.tpe.derivesFrom(defn.ArrayClass) => - wrapArray(arg1, arg0.tpe.elemType) - case _ => arg0 - } + val arg = + def dropNameArg(arg: Tree): Tree = arg match + case NamedArg(_, arg1) => arg1 + case SeqLiteral(elems, tpt) => + cpy.SeqLiteral(arg)(elems.mapConserve(dropNameArg), tpt) + case _ => arg + arg0 match + case Typed(seq, tpt) if tpt.tpe.isRepeatedParam => + if seq.tpe.derivesFrom(defn.ArrayClass) then wrapArray(dropNameArg(seq), arg0.tpe.elemType) + else cpy.Typed(arg0)(dropNameArg(seq), tpt) + case arg0 => + dropNameArg(arg0) val argtpe = arg.tpe.dealiasKeepAnnots.translateFromRepeated(toArray = false) val argIsBottom = argtpe.isBottomTypeAfterErasure val bindingType = diff --git a/tests/pos-macros/i17227/Macro_1.scala b/tests/pos-macros/i17227/Macro_1.scala new file mode 100644 index 000000000000..b483336119cb --- /dev/null +++ b/tests/pos-macros/i17227/Macro_1.scala @@ -0,0 +1,22 @@ +import scala.quoted.* + +inline def foo(f: Int => Int): Int => Int = ${impl('f)} +inline def bar(inline f: Int => Int): Int => Int = ${impl('f)} +inline def baz(inline f: (Int => Int)*): Int => Int = ${impl2('f)} + +def impl(f: Expr[Int => Int])(using Quotes): Expr[Int => Int] = + assertNoNamedArgs(f) + '{identity} + +def impl2(f: Expr[Seq[Int => Int]])(using Quotes): Expr[Int => Int] = + assertNoNamedArgs(f) + '{identity} + +def assertNoNamedArgs(expr: Expr[Any])(using Quotes): Unit = + import quotes.reflect.* + new TreeTraverser { + override def traverseTree(tree: Tree)(owner: Symbol): Unit = tree match + case _: NamedArg => + report.throwError(s"Unexpected NamedArg after inlining: ${tree}", tree.pos) + case _ => traverseTreeChildren(tree)(owner) + }.traverseTree(expr.asTerm)(Symbol.spliceOwner) diff --git a/tests/pos-macros/i17227/Test_2.scala b/tests/pos-macros/i17227/Test_2.scala new file mode 100644 index 000000000000..4106113d94c0 --- /dev/null +++ b/tests/pos-macros/i17227/Test_2.scala @@ -0,0 +1,6 @@ +def g(i: Int): Int = i + +def test = + foo(f = g) + bar(f = g) + baz(f = g) diff --git a/tests/pos/i17227.scala b/tests/pos/i17227.scala new file mode 100644 index 000000000000..d537f99f6515 --- /dev/null +++ b/tests/pos/i17227.scala @@ -0,0 +1,10 @@ +inline def foo(f: Int => Int): Int => Int = f +inline def bar(inline f: Int => Int): Int => Int = f +inline def baz(f: (Int => Int)*): Int => Int = f.head + +def g(i: Int): Int = i + +def test = + foo(f = g) + bar(f = g) + baz(f = g) From a8fab276a337bc0dfe65f15b6a9602a89c3a25ad Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 12 Apr 2023 14:35:53 +0200 Subject: [PATCH 357/657] Compare simple name and handle NO_NAME case in WUnused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index fcbb61ab90e2..5c5f382de1b2 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,7 +603,7 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || symName.exists(_ != sym.name)) && sym.exists then + if qualHasSymbol && (!isAccessible || (sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName))) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None From e5d4be3fcd349896a491c9685a45f903d43ed19a Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 18:08:33 +0200 Subject: [PATCH 358/657] Fix WUnused false positive in for --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 1 + tests/neg-custom-args/fatal-warnings/i15503i.scala | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5a178ff2ec1f..6c6bfea476d8 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -473,6 +473,7 @@ object CheckUnused: if ctx.settings.WunusedHas.explicits then explicitParamInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ExplicitParams).toList else diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 737e5f0739ca..76a982df3d67 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -287,3 +287,13 @@ package foo.test.i17156: package c: import b.Xd trait Z derives Xd + +package foo.test.i17175: + val continue = true + def foo = + for { + i <- 1.until(10) // OK + if continue + } { + println(i) + } \ No newline at end of file From c63840221dc1f9606361c1449cdcbf961dd33ace Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 12 Apr 2023 17:00:18 +0200 Subject: [PATCH 359/657] Extracted isRenamedSymbol def --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5c5f382de1b2..cd1a21ece440 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,11 +603,13 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || (sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName))) && sym.exists then + if qualHasSymbol && (!isAccessible || isRenamedSymbol(sym, symName)) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None + private def isRenamedSymbol(sym: Symbol, symNameInScope: Option[Name]) = + sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName) private def dealias(symbol: Symbol)(using Context): Symbol = if(symbol.isType && symbol.asType.denot.isAliasType) then From e72c020873d05a53722000a07a8f1bd0a3338243 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 12 Apr 2023 17:16:39 +0200 Subject: [PATCH 360/657] Fix isRenamedSymbol method in WUnused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index cd1a21ece440..04f993e4c805 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,13 +603,13 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || isRenamedSymbol(sym, symName)) && sym.exists then + if qualHasSymbol && (!isAccessible || sym.isRenamedSymbol(symName)) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None - private def isRenamedSymbol(sym: Symbol, symNameInScope: Option[Name]) = - sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName) + private def isRenamedSymbol(symNameInScope: Option[Name])(using Context) = + sym.name != nme.NO_NAME && symNameInScope.exists(_.toSimpleName != sym.name.toSimpleName) private def dealias(symbol: Symbol)(using Context): Symbol = if(symbol.isType && symbol.asType.denot.isAliasType) then From fb0efe5a85529a42d38eff2c003cca09e3dd8a54 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 13:32:30 +0200 Subject: [PATCH 361/657] Do not register used symbol when position doesnt exist in wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 3 ++- .../fatal-warnings/i15503-scala2/scala2-t11681.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503i.scala | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6c6bfea476d8..b4db279a522c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -358,7 +358,8 @@ object CheckUnused: usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived)) usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived)) usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived)) - name.map(n => usedInPosition += ((sym.sourcePos, n))) + if sym.sourcePos.exists then + name.map(n => usedInPosition += ((sym.sourcePos, n))) /** Register a symbol that should be ignored */ def addIgnoredUsage(sym: Symbol)(using Context): Unit = diff --git a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala index 18aa6879eeba..912dbb456f3b 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala @@ -60,7 +60,7 @@ class Revaluing(u: Int) { def f = u } // OK case class CaseyKasem(k: Int) // OK -case class CaseyAtTheBat(k: Int)(s: String) // error +case class CaseyAtTheBat(k: Int)(s: String) // ok trait Ignorance { def f(readResolve: Int) = answer // error diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 76a982df3d67..f67e273ca6f5 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -90,7 +90,7 @@ package foo.test.possibleclasses: k: Int, // OK private val y: Int // OK /* Kept as it can be taken from pattern */ )( - s: Int, // error /* But not these */ + s: Int, val t: Int, // OK private val z: Int // error ) @@ -131,7 +131,7 @@ package foo.test.possibleclasses.withvar: k: Int, // OK private var y: Int // OK /* Kept as it can be taken from pattern */ )( - s: Int, // error /* But not these */ + s: Int, var t: Int, // OK private var z: Int // error ) From f6040ad31d55c2cc93f9dc2eacfc3e1766c0bdc9 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 4 Apr 2023 17:20:02 +0200 Subject: [PATCH 362/657] Make CheckUnused run both after Typer and Inlining --- compiler/src/dotty/tools/dotc/Compiler.scala | 3 +- .../tools/dotc/transform/CheckUnused.scala | 92 ++++++++++++------- 2 files changed, 63 insertions(+), 32 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index db3d42d32190..5148e2b17c71 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,7 +35,7 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer - List(new CheckUnused) :: // Check for unused elements + List(CheckUnused.PostTyper) :: // Check for unused elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files @@ -50,6 +50,7 @@ class Compiler { List(new Pickler) :: // Generate TASTY info List(new Inlining) :: // Inline and execute macros List(new PostInlining) :: // Add mirror support for inlined code + List(CheckUnused.PostInlining) :: // Check for unused elements List(new Staging) :: // Check staging levels and heal staged types List(new Splicing) :: // Replace level 1 splices with holes List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5a178ff2ec1f..ac28c2e36b98 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -33,22 +33,15 @@ import dotty.tools.dotc.core.StdNames.nme * Basically, it gathers definition/imports and their usage. If a * definition/imports does not have any usage, then it is reported. */ -class CheckUnused extends MiniPhase: - import CheckUnused.UnusedData - - /** - * The key used to retrieve the "unused entity" analysis metadata, - * from the compilation `Context` - */ - private val _key = Property.Key[UnusedData] +class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _key: Property.Key[CheckUnused.UnusedData]) extends MiniPhase: + import CheckUnused.* + import UnusedData.* private def unusedDataApply[U](f: UnusedData => U)(using Context): Context = ctx.property(_key).foreach(f) ctx - private def getUnusedData(using Context): Option[UnusedData] = - ctx.property(_key) - override def phaseName: String = CheckUnused.phaseName + override def phaseName: String = CheckUnused.phaseNamePrefix + suffix override def description: String = CheckUnused.description @@ -60,13 +53,21 @@ class CheckUnused extends MiniPhase: override def prepareForUnit(tree: tpd.Tree)(using Context): Context = val data = UnusedData() + tree.getAttachment(_key).foreach(oldData => + data.unusedAggregate = oldData.unusedAggregate + ) val fresh = ctx.fresh.setProperty(_key, data) + tree.putAttachment(_key, data) fresh // ========== END + REPORTING ========== override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = - unusedDataApply(ud => reportUnused(ud.getUnused)) + unusedDataApply { ud => + aggregateUnused(ud, ud.getUnused) + if(phaseMode == PhaseMode.Report) then + ud.unusedAggregate.foreach(reportUnused) + } tree // ========== MiniPhase Prepare ========== @@ -252,31 +253,45 @@ class CheckUnused extends MiniPhase: private def traverseAnnotations(sym: Symbol)(using Context): Unit = sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree)) + private def aggregateUnused(data: UnusedData, res: UnusedData.UnusedResult)(using Context): Unit = + data.unusedAggregate match { + case None => + data.unusedAggregate = Some(res) + case Some(prevUnused) => + val intersection = res.warnings.filter(sym => prevUnused.warnings.contains(sym)) + data.unusedAggregate = Some(UnusedResult(intersection)) + } + + + /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = - import CheckUnused.WarnTypes res.warnings.foreach { s => s match - case (t, WarnTypes.Imports) => + case UnusedSymbol(t, _, WarnTypes.Imports) => report.warning(s"unused import", t) - case (t, WarnTypes.LocalDefs) => + case UnusedSymbol(t, _, WarnTypes.LocalDefs) => report.warning(s"unused local definition", t) - case (t, WarnTypes.ExplicitParams) => + case UnusedSymbol(t, _, WarnTypes.ExplicitParams) => report.warning(s"unused explicit parameter", t) - case (t, WarnTypes.ImplicitParams) => + case UnusedSymbol(t, _, WarnTypes.ImplicitParams) => report.warning(s"unused implicit parameter", t) - case (t, WarnTypes.PrivateMembers) => + case UnusedSymbol(t, _, WarnTypes.PrivateMembers) => report.warning(s"unused private member", t) - case (t, WarnTypes.PatVars) => + case UnusedSymbol(t, _, WarnTypes.PatVars) => report.warning(s"unused pattern variable", t) } end CheckUnused object CheckUnused: - val phaseName: String = "checkUnused" + val phaseNamePrefix: String = "checkUnused" val description: String = "check for unused elements" + enum PhaseMode: + case Aggregate + case Report + private enum WarnTypes: case Imports case LocalDefs @@ -285,6 +300,15 @@ object CheckUnused: case PrivateMembers case PatVars + /** + * The key used to retrieve the "unused entity" analysis metadata, + * from the compilation `Context` + */ + private val _key = Property.StickyKey[UnusedData] + + val PostTyper = new CheckUnused(PhaseMode.Aggregate, "PostTyper", _key) + val PostInlining = new CheckUnused(PhaseMode.Report, "PostInlining", _key) + /** * A stateful class gathering the infos on : * - imports @@ -292,13 +316,14 @@ object CheckUnused: * - usage */ private class UnusedData: - import dotty.tools.dotc.transform.CheckUnused.UnusedData.UnusedResult import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack} - import UnusedData.ScopeType + import UnusedData.* /** The current scope during the tree traversal */ var currScopeType: MutStack[ScopeType] = MutStack(ScopeType.Other) + var unusedAggregate: Option[UnusedResult] = None + /* IMPORTS */ private val impInScope = MutStack(MutSet[tpd.Import]()) /** @@ -452,12 +477,13 @@ object CheckUnused: * * The given `List` is sorted by line and then column of the position */ + def getUnused(using Context): UnusedResult = popScope() val sortedImp = if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then - unusedImport.map(d => d.srcPos -> WarnTypes.Imports).toList + unusedImport.map(d => UnusedSymbol(d.srcPos, d.name, WarnTypes.Imports)).toList else Nil val sortedLocalDefs = @@ -466,7 +492,7 @@ object CheckUnused: .filterNot(d => d.symbol.usedDefContains) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => d.namePos -> WarnTypes.LocalDefs).toList + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.LocalDefs)).toList else Nil val sortedExplicitParams = @@ -474,7 +500,7 @@ object CheckUnused: explicitParamInScope .filterNot(d => d.symbol.usedDefContains) .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => d.namePos -> WarnTypes.ExplicitParams).toList + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.ExplicitParams)).toList else Nil val sortedImplicitParams = @@ -482,7 +508,7 @@ object CheckUnused: implicitParamInScope .filterNot(d => d.symbol.usedDefContains) .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => d.namePos -> WarnTypes.ImplicitParams).toList + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.ImplicitParams)).toList else Nil val sortedPrivateDefs = @@ -490,7 +516,7 @@ object CheckUnused: privateDefInScope .filterNot(d => d.symbol.usedDefContains) .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => d.namePos -> WarnTypes.PrivateMembers).toList + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PrivateMembers)).toList else Nil val sortedPatVars = @@ -499,14 +525,14 @@ object CheckUnused: .filterNot(d => d.symbol.usedDefContains) .filterNot(d => containsSyntheticSuffix(d.symbol)) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) - .map(d => d.namePos -> WarnTypes.PatVars).toList + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PatVars)).toList else Nil val warnings = List(sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, sortedPrivateDefs, sortedPatVars).flatten.sortBy { s => - val pos = s._1.sourcePos + val pos = s.pos.sourcePos (pos.line, pos.column) } - UnusedResult(warnings, Nil) + UnusedResult(warnings) end getUnused //============================ HELPERS ==================================== @@ -703,7 +729,11 @@ object CheckUnused: case _:tpd.Block => Local case _ => Other + case class UnusedSymbol(pos: SrcPos, name: Name, warnType: WarnTypes) /** A container for the results of the used elements analysis */ - case class UnusedResult(warnings: List[(dotty.tools.dotc.util.SrcPos, WarnTypes)], usedImports: List[(tpd.Import, untpd.ImportSelector)]) + case class UnusedResult(warnings: List[UnusedSymbol]) + object UnusedResult: + val Empty = UnusedResult(Nil) + end CheckUnused From 6997686525c6a01e7fd78b2b19744a245be988da Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 14:18:44 +0200 Subject: [PATCH 363/657] Fix instantation of CheckUnused phase --- compiler/src/dotty/tools/dotc/Compiler.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 6 ++++-- tests/neg-custom-args/fatal-warnings/i15503a.scala | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 5148e2b17c71..b0f7b0a533d7 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -35,7 +35,7 @@ class Compiler { protected def frontendPhases: List[List[Phase]] = List(new Parser) :: // Compiler frontend: scanner, parser List(new TyperPhase) :: // Compiler frontend: namer, typer - List(CheckUnused.PostTyper) :: // Check for unused elements + List(new CheckUnused.PostTyper) :: // Check for unused elements List(new YCheckPositions) :: // YCheck positions List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files @@ -50,7 +50,7 @@ class Compiler { List(new Pickler) :: // Generate TASTY info List(new Inlining) :: // Inline and execute macros List(new PostInlining) :: // Add mirror support for inlined code - List(CheckUnused.PostInlining) :: // Check for unused elements + List(new CheckUnused.PostInlining) :: // Check for unused elements List(new Staging) :: // Check staging levels and heal staged types List(new Splicing) :: // Replace level 1 splices with holes List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index ac28c2e36b98..2619136d26ab 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -52,6 +52,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke // ========== SETUP ============ override def prepareForUnit(tree: tpd.Tree)(using Context): Context = + println(this) val data = UnusedData() tree.getAttachment(_key).foreach(oldData => data.unusedAggregate = oldData.unusedAggregate @@ -306,8 +307,9 @@ object CheckUnused: */ private val _key = Property.StickyKey[UnusedData] - val PostTyper = new CheckUnused(PhaseMode.Aggregate, "PostTyper", _key) - val PostInlining = new CheckUnused(PhaseMode.Report, "PostInlining", _key) + class PostTyper extends CheckUnused(PhaseMode.Aggregate, "PostTyper", _key) + + class PostInlining extends CheckUnused(PhaseMode.Report, "PostInlining", _key) /** * A stateful class gathering the infos on : diff --git a/tests/neg-custom-args/fatal-warnings/i15503a.scala b/tests/neg-custom-args/fatal-warnings/i15503a.scala index 868c488ddb84..cd7282490fc9 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503a.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503a.scala @@ -63,12 +63,12 @@ object FooTypeName: object InlineChecks: object InlineFoo: - import collection.mutable.Set // OK + import collection.mutable.Set // ok import collection.mutable.Map // error inline def getSet = Set(1) object InlinedBar: - import collection.mutable.Set // error + import collection.mutable.Set // ok import collection.mutable.Map // error val a = InlineFoo.getSet From 3b5491e318552cdef9acd8ed0dcc2a35cd1ba211 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 14:37:21 +0200 Subject: [PATCH 364/657] Remove unnecessary logging in CheckUnused phase --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 2619136d26ab..44d130f16e16 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -52,7 +52,6 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke // ========== SETUP ============ override def prepareForUnit(tree: tpd.Tree)(using Context): Context = - println(this) val data = UnusedData() tree.getAttachment(_key).foreach(oldData => data.unusedAggregate = oldData.unusedAggregate From fa07b41507aa369488983099ba68f80fc5ac496c Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 14:48:20 +0200 Subject: [PATCH 365/657] Add test cases for macro wunused --- .../fatal-warnings/i16876/Macro.scala | 23 +++++++++++++++++++ .../fatal-warnings/i16876/Test.scala | 11 +++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/neg-custom-args/fatal-warnings/i16876/Macro.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i16876/Test.scala diff --git a/tests/neg-custom-args/fatal-warnings/i16876/Macro.scala b/tests/neg-custom-args/fatal-warnings/i16876/Macro.scala new file mode 100644 index 000000000000..2823de1f72c5 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16876/Macro.scala @@ -0,0 +1,23 @@ +import scala.quoted.* + +def findMethodSymbol(using q: Quotes)(s: quotes.reflect.Symbol): quotes.reflect.Symbol = + if s.isDefDef then + s + else + findMethodSymbol(using q)(s.owner) +end findMethodSymbol + + +inline def adder: Int = ${ + adderImpl +} + +def adderImpl(using q: Quotes): Expr[Int] = + import quotes.reflect.* + + val inputs = findMethodSymbol(using q)(q.reflect.Symbol.spliceOwner).tree match + case DefDef(_, params, _, _) => + params.last match + case TermParamClause(valDefs) => + valDefs.map(vd => Ref(vd.symbol).asExprOf[Int]) + inputs.reduce((exp1, exp2) => '{ $exp1 + $exp2 }) \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i16876/Test.scala b/tests/neg-custom-args/fatal-warnings/i16876/Test.scala new file mode 100644 index 000000000000..d9229d31cd6d --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16876/Test.scala @@ -0,0 +1,11 @@ +// scalac: -Wunused:all + +object Foo { + private def myMethod(a: Int, b: Int, c: Int) = adder // ok + myMethod(1, 2, 3) + + private def myMethodFailing(a: Int, b: Int, c: Int) = a + 0 // error // error + myMethodFailing(1, 2, 3) +} + + From 14dd04b1f11ec4c0aa5ed57fd4b744f134caf09b Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 15:32:56 +0200 Subject: [PATCH 366/657] Handle implicit params and adjust tests in WUnused --- .../tools/dotc/transform/CheckUnused.scala | 5 ++-- .../i15503-scala2/scala2-t11681.scala | 22 +++++++++--------- .../fatal-warnings/i15503e.scala | 18 ++++++++------- .../fatal-warnings/i15503f.scala | 17 +++++++------- .../fatal-warnings/i15503g.scala | 23 ++++++++++--------- .../fatal-warnings/i15503h.scala | 2 +- .../fatal-warnings/i15503i.scala | 10 +++++--- 7 files changed, 52 insertions(+), 45 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index d49e69c46206..f083690db11f 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -374,8 +374,6 @@ object CheckUnused: def addIgnoredParam(sym: Symbol)(using Context): Unit = paramsToSkip += sym - - /** Register an import */ def registerImport(imp: tpd.Import)(using Context): Unit = if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum && !isTransparentAndInline(imp) then @@ -389,7 +387,8 @@ object CheckUnused: if memDef.isValidMemberDef then if memDef.isValidParam then if memDef.symbol.isOneOf(GivenOrImplicit) then - implicitParamInScope += memDef + if !paramsToSkip.contains(memDef.symbol) then + implicitParamInScope += memDef else if !paramsToSkip.contains(memDef.symbol) then explicitParamInScope += memDef else if currScopeType.top == ScopeType.Local then diff --git a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala index 162bf9128a98..c26ac6c57770 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala @@ -33,7 +33,7 @@ trait BadAPI extends InterFace { override def equals(other: Any): Boolean = true // OK - def i(implicit s: String) = answer // error + def i(implicit s: String) = answer // ok /* def future(x: Int): Int = { @@ -63,7 +63,7 @@ case class CaseyKasem(k: Int) // OK case class CaseyAtTheBat(k: Int)(s: String) // error trait Ignorance { - def f(readResolve: Int) = answer // error + def f(readResolve: Int) = answer // ok } class Reusing(u: Int) extends Unusing(u) // OK @@ -78,28 +78,28 @@ trait Unimplementation { } trait DumbStuff { - def f(implicit dummy: DummyImplicit) = answer // todo // error - def g(dummy: DummyImplicit) = answer // error + def f(implicit dummy: DummyImplicit) = answer // ok + def g(dummy: DummyImplicit) = answer // ok } trait Proofs { - def f[A, B](implicit ev: A =:= B) = answer // todo // error - def g[A, B](implicit ev: A <:< B) = answer // todo // error - def f2[A, B](ev: A =:= B) = answer // error - def g2[A, B](ev: A <:< B) = answer // error + def f[A, B](implicit ev: A =:= B) = answer // ok + def g[A, B](implicit ev: A <:< B) = answer // ok + def f2[A, B](ev: A =:= B) = answer // ok + def g2[A, B](ev: A <:< B) = answer // ok } trait Anonymous { - def f = (i: Int) => answer // error + def f = (i: Int) => answer // ok def f1 = (_: Int) => answer // OK def f2: Int => Int = _ + 1 // OK - def g = for (i <- List(1)) yield answer // error + def g = for (i <- List(1)) yield answer // ok } trait Context[A] trait Implicits { - def f[A](implicit ctx: Context[A]) = answer // error + def f[A](implicit ctx: Context[A]) = answer // ok def g[A: Context] = answer // OK } class Bound[A: Context] // OK diff --git a/tests/neg-custom-args/fatal-warnings/i15503e.scala b/tests/neg-custom-args/fatal-warnings/i15503e.scala index 56aec702a39e..6d166aff7347 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503e.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503e.scala @@ -1,14 +1,16 @@ // scalac: -Wunused:explicits -/* This goes around the "trivial method" detection */ -val default_val = 1 +object Foo { + /* This goes around the "trivial method" detection */ + val default_val = 1 -def f1(a: Int) = a // OK -def f2(a: Int) = default_val // error -def f3(a: Int)(using Int) = a // OK -def f4(a: Int)(using Int) = default_val // error -def f6(a: Int)(using Int) = summon[Int] // error -def f7(a: Int)(using Int) = summon[Int] + a // OK + private def f1(a: Int) = a // OK + private def f2(a: Int) = default_val // error + private def f3(a: Int)(using Int) = a // OK + private def f4(a: Int)(using Int) = default_val // error + private def f6(a: Int)(using Int) = summon[Int] // error + private def f7(a: Int)(using Int) = summon[Int] + a // OK +} package scala2main.unused.args: object happyBirthday { diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index 67c595d74f40..f909272af732 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -3,11 +3,12 @@ /* This goes around the "trivial method" detection */ val default_int = 1 -def f1(a: Int) = a // OK -def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // OK -def f4(a: Int)(using Int) = default_int // OK -def f6(a: Int)(using Int) = summon[Int] // OK -def f7(a: Int)(using Int) = summon[Int] + a // OK -def f8(a: Int)(using foo: Int) = a // error - +object Xd { + private def f1(a: Int) = a // OK + private def f2(a: Int) = 1 // OK + private def f3(a: Int)(using Int) = a // OK + private def f4(a: Int)(using Int) = default_int // OK + private def f6(a: Int)(using Int) = summon[Int] // OK + private def f7(a: Int)(using Int) = summon[Int] + a // OK + private def f8(a: Int)(using foo: Int) = a // error +} diff --git a/tests/neg-custom-args/fatal-warnings/i15503g.scala b/tests/neg-custom-args/fatal-warnings/i15503g.scala index a0822e7e1611..0d71d516adad 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503g.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503g.scala @@ -1,15 +1,16 @@ // scalac: -Wunused:params /* This goes around the "trivial method" detection */ -val default_int = 1 +object Foo { + val default_int = 1 -def f1(a: Int) = a // OK -def f2(a: Int) = default_int // error -def f3(a: Int)(using Int) = a // OK -def f4(a: Int)(using Int) = default_int // error -def f6(a: Int)(using Int) = summon[Int] // error -def f7(a: Int)(using Int) = summon[Int] + a // OK - -/* --- Trivial method check --- */ -def g1(x: Int) = 1 // OK -def g2(x: Int) = ??? // OK \ No newline at end of file + private def f1(a: Int) = a // OK + private def f2(a: Int) = default_int // error + private def f3(a: Int)(using Int) = a // OK + private def f4(a: Int)(using Int) = default_int // error + private def f6(a: Int)(using Int) = summon[Int] // error + private def f7(a: Int)(using Int) = summon[Int] + a // OK + /* --- Trivial method check --- */ + private def g1(x: Int) = 1 // OK + private def g2(x: Int) = ??? // OK +} \ No newline at end of file diff --git a/tests/neg-custom-args/fatal-warnings/i15503h.scala b/tests/neg-custom-args/fatal-warnings/i15503h.scala index f8d1d6f2202f..3bab6cdbd098 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503h.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503h.scala @@ -7,7 +7,7 @@ class A { val b = 2 // OK private def c = 2 // error - def d(using x:Int): Int = b // error + def d(using x:Int): Int = b // ok def e(x: Int) = 1 // OK def f = val x = 1 // error diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 9ac2ec5ef622..2b86e444c44f 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -17,10 +17,10 @@ class A { private def c2 = 2 // OK def c3 = c2 - def d1(using x:Int): Int = default_int // error + def d1(using x:Int): Int = default_int // ok def d2(using x:Int): Int = x // OK - def e1(x: Int) = default_int // error + def e1(x: Int) = default_int // ok def e2(x: Int) = x // OK def f = val x = 1 // error @@ -44,7 +44,11 @@ package foo.test.scala.annotation: val default_int = 12 def a1(a: Int) = a // OK - def a2(a: Int) = default_int // error + def a2(a: Int) = default_int // ok + + private def a2_p(a: Int) = default_int // error + def a2_p_used = a2_p(3) + def a3(@unused a: Int) = default_int //OK def b1 = From 38c35bd2b0e0881f46eda471f99a9cb28f9e973e Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 15:47:59 +0200 Subject: [PATCH 367/657] Apply review suggestions to WUnused PR --- .../tools/dotc/transform/CheckUnused.scala | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 44d130f16e16..4f9ec3eaa9e3 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -25,7 +25,7 @@ import dotty.tools.dotc.core.Definitions import dotty.tools.dotc.core.NameKinds.WildcardParamName import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.StdNames.nme - +import scala.math.Ordering /** * A compiler phase that checks for unused imports or definitions @@ -64,7 +64,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = unusedDataApply { ud => - aggregateUnused(ud, ud.getUnused) + finishAggregation(ud) if(phaseMode == PhaseMode.Report) then ud.unusedAggregate.foreach(reportUnused) } @@ -253,12 +253,13 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke private def traverseAnnotations(sym: Symbol)(using Context): Unit = sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree)) - private def aggregateUnused(data: UnusedData, res: UnusedData.UnusedResult)(using Context): Unit = + private def finishAggregation(data: UnusedData)(using Context): Unit = + val unusedInThisStage = data.getUnused data.unusedAggregate match { case None => - data.unusedAggregate = Some(res) + data.unusedAggregate = Some(unusedInThisStage) case Some(prevUnused) => - val intersection = res.warnings.filter(sym => prevUnused.warnings.contains(sym)) + val intersection = unusedInThisStage.warnings.intersect(prevUnused.warnings) data.unusedAggregate = Some(UnusedResult(intersection)) } @@ -266,7 +267,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = - res.warnings.foreach { s => + res.warnings.toList.sortBy(_.pos.line)(using Ordering[Int]).foreach { s => s match case UnusedSymbol(t, _, WarnTypes.Imports) => report.warning(s"unused import", t) @@ -321,7 +322,7 @@ object CheckUnused: import UnusedData.* /** The current scope during the tree traversal */ - var currScopeType: MutStack[ScopeType] = MutStack(ScopeType.Other) + val currScopeType: MutStack[ScopeType] = MutStack(ScopeType.Other) var unusedAggregate: Option[UnusedResult] = None @@ -732,7 +733,7 @@ object CheckUnused: case class UnusedSymbol(pos: SrcPos, name: Name, warnType: WarnTypes) /** A container for the results of the used elements analysis */ - case class UnusedResult(warnings: List[UnusedSymbol]) + case class UnusedResult(warnings: Set[UnusedSymbol]) object UnusedResult: val Empty = UnusedResult(Nil) From 3f67e270baab1ccb8fed33a2c814aefdeb691c2c Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 15:55:29 +0200 Subject: [PATCH 368/657] Move finishAggregation to UnusedData class in CheckUnused --- .../tools/dotc/transform/CheckUnused.scala | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 4f9ec3eaa9e3..c1705f97d494 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -64,7 +64,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = unusedDataApply { ud => - finishAggregation(ud) + ud.finishAggregation() if(phaseMode == PhaseMode.Report) then ud.unusedAggregate.foreach(reportUnused) } @@ -253,17 +253,6 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke private def traverseAnnotations(sym: Symbol)(using Context): Unit = sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree)) - private def finishAggregation(data: UnusedData)(using Context): Unit = - val unusedInThisStage = data.getUnused - data.unusedAggregate match { - case None => - data.unusedAggregate = Some(unusedInThisStage) - case Some(prevUnused) => - val intersection = unusedInThisStage.warnings.intersect(prevUnused.warnings) - data.unusedAggregate = Some(UnusedResult(intersection)) - } - - /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = @@ -371,6 +360,17 @@ object CheckUnused: execInNewScope popScope() + def finishAggregation(using Context)(): Unit = + val unusedInThisStage = this.getUnused + this.unusedAggregate match { + case None => + this.unusedAggregate = Some(unusedInThisStage) + case Some(prevUnused) => + val intersection = unusedInThisStage.warnings.intersect(prevUnused.warnings) + this.unusedAggregate = Some(UnusedResult(intersection)) + } + + /** * Register a found (used) symbol along with its name * @@ -534,7 +534,7 @@ object CheckUnused: val pos = s.pos.sourcePos (pos.line, pos.column) } - UnusedResult(warnings) + UnusedResult(warnings.toSet) end getUnused //============================ HELPERS ==================================== @@ -735,7 +735,7 @@ object CheckUnused: /** A container for the results of the used elements analysis */ case class UnusedResult(warnings: Set[UnusedSymbol]) object UnusedResult: - val Empty = UnusedResult(Nil) + val Empty = UnusedResult(Set.empty) end CheckUnused From 013a2842092a31461b2a1eb4a7d81aa3cd762d70 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Thu, 13 Apr 2023 17:28:33 +0200 Subject: [PATCH 369/657] Fix tests for WUnused/disable for public defs --- .../fatal-warnings/i15503e.scala | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503e.scala b/tests/neg-custom-args/fatal-warnings/i15503e.scala index 6d166aff7347..57664cd08dcd 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503e.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503e.scala @@ -14,7 +14,7 @@ object Foo { package scala2main.unused.args: object happyBirthday { - def main(args: Array[String]): Unit = println("Hello World") // error + def main(args: Array[String]): Unit = println("Hello World") // ok } package scala2main: @@ -31,7 +31,7 @@ package scala3main: package foo.test.lambda.param: val default_val = 1 val a = (i: Int) => i // OK - val b = (i: Int) => default_val // error + val b = (i: Int) => default_val // OK val c = (_: Int) => default_val // OK package foo.test.trivial: @@ -39,19 +39,19 @@ package foo.test.trivial: class C { def answer: 42 = 42 object X - def g0(x: Int) = ??? // OK - def f0(x: Int) = () // OK - def f1(x: Int) = throw new RuntimeException // OK - def f2(x: Int) = 42 // OK - def f3(x: Int): Option[Int] = None // OK - def f4(x: Int) = classOf[Int] // OK - def f5(x: Int) = answer + 27 // OK - def f6(x: Int) = X // OK - def f7(x: Int) = Y // OK - def f8(x: Int): List[C] = Nil // OK - def f9(x: Int): List[Int] = List(1,2,3,4) // error - def foo:Int = 32 // OK - def f77(x: Int) = foo // error + private def g0(x: Int) = ??? // OK + private def f0(x: Int) = () // OK + private def f1(x: Int) = throw new RuntimeException // OK + private def f2(x: Int) = 42 // OK + private def f3(x: Int): Option[Int] = None // OK + private def f4(x: Int) = classOf[Int] // OK + private def f5(x: Int) = answer + 27 // OK + private def f6(x: Int) = X // OK + private def f7(x: Int) = Y // OK + private def f8(x: Int): List[C] = Nil // OK + private def f9(x: Int): List[Int] = List(1,2,3,4) // error + private def foo:Int = 32 // OK + private def f77(x: Int) = foo // error } object Y From 49d9901bcccee72515a196832bcb471c289b8229 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 14 Apr 2023 11:56:54 +0200 Subject: [PATCH 370/657] correctly type Expr.ofTupleFromSeq for arity > 22 --- library/src/scala/quoted/Expr.scala | 15 +++++++++++- tests/run-macros/i17257/a.scala | 38 +++++++++++++++++++++++++++++ tests/run-macros/i17257/b.scala | 21 ++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/run-macros/i17257/a.scala create mode 100644 tests/run-macros/i17257/b.scala diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 996fe3ff8da2..72ceaa8ffadd 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -103,7 +103,7 @@ object Expr { case 20 => ofTupleFromSeq20(seq) case 21 => ofTupleFromSeq21(seq) case 22 => ofTupleFromSeq22(seq) - case _ => '{ Tuple.fromIArray(IArray(${Varargs(seq)}: _*)) } + case _ => ofTupleFromSeqXXL(seq) } } @@ -214,6 +214,19 @@ object Expr { case Seq('{ $x1: t1 }, '{ $x2: t2 }, '{ $x3: t3 }, '{ $x4: t4 }, '{ $x5: t5 }, '{ $x6: t6 }, '{ $x7: t7 }, '{ $x8: t8 }, '{ $x9: t9 }, '{ $x10: t10 }, '{ $x11: t11 }, '{ $x12: t12 }, '{ $x13: t13 }, '{ $x14: t14 }, '{ $x15: t15 }, '{ $x16: t16 }, '{ $x17: t17 }, '{ $x18: t18 }, '{ $x19: t19 }, '{ $x20: t20 }, '{ $x21: t21 }, '{ $x22: t22 }) => '{ Tuple22($x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x16, $x17, $x18, $x19, $x20, $x21, $x22) } + private def ofTupleFromSeqXXL(seq: Seq[Expr[Any]])(using Quotes): Expr[Tuple] = + val tupleTpe = tupleTypeFromSeq(seq) + tupleTpe.asType match + case '[tpe] => + '{ Tuple.fromIArray(IArray(${Varargs(seq)}: _*)).asInstanceOf[tpe & Tuple] } + + private def tupleTypeFromSeq(seq: Seq[Expr[Any]])(using Quotes): quotes.reflect.TypeRepr = + import quotes.reflect.* + val consRef = Symbol.classSymbol("scala.*:").typeRef + seq.foldLeft(TypeRepr.of[EmptyTuple]) { (ts, expr) => + expr match + case '{ $e: t } => AppliedType(consRef, TypeRepr.of[t] :: ts :: Nil) + } /** Given a tuple of the form `(Expr[A1], ..., Expr[An])`, outputs a tuple `Expr[(A1, ..., An)]`. */ def ofTuple[T <: Tuple: Tuple.IsMappedBy[Expr]: Type](tup: T)(using Quotes): Expr[Tuple.InverseMap[T, Expr]] = { diff --git a/tests/run-macros/i17257/a.scala b/tests/run-macros/i17257/a.scala new file mode 100644 index 000000000000..873e8a6e84a2 --- /dev/null +++ b/tests/run-macros/i17257/a.scala @@ -0,0 +1,38 @@ +package derivation +import scala.quoted.* + +import scala.annotation.tailrec + +object Helpers: + + // file a.scala + inline def summonAllOptimized[T <: Tuple]: T = + ${ summonAllOptimizedImpl[T] } + + private def summonAllOptimizedImpl[T <: Tuple: Type](using q: Quotes): Expr[T] = { + import q.reflect.* + + Expr + .ofTupleFromSeq(typesOfTuple(TypeRepr.of[T], Nil).map { tpe => + tpe.asType match { + case '[t] => + Expr.summon[t].getOrElse(report.errorAndAbort(s"Unable to to find implicit instance for ${tpe.show}")) + } + }) + .asExprOf[T] + } + + @tailrec + private[derivation] def typesOfTuple( + using q: Quotes + )(tpe: q.reflect.TypeRepr, acc: List[q.reflect.TypeRepr]): List[q.reflect.TypeRepr] = + import q.reflect.* + val cons = Symbol.classSymbol("scala.*:") + tpe.widenTermRefByName.dealias match + case AppliedType(fn, tpes) if defn.isTupleClass(fn.typeSymbol) => + tpes.reverse_:::(acc) + case AppliedType(tp, List(headType, tailType)) if tp.derivesFrom(cons) => + typesOfTuple(tailType, headType :: acc) + case tpe => + if tpe.derivesFrom(Symbol.classSymbol("scala.EmptyTuple")) then acc.reverse + else report.errorAndAbort(s"Unknown type encountered in tuple ${tpe.show}") diff --git a/tests/run-macros/i17257/b.scala b/tests/run-macros/i17257/b.scala new file mode 100644 index 000000000000..b9159973fbc4 --- /dev/null +++ b/tests/run-macros/i17257/b.scala @@ -0,0 +1,21 @@ +package derivation { + //file b.scala + val test = Helpers.summonAllOptimized[( + ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], + ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], + ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], + ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], + ValueOf["a"], ValueOf["a"], ValueOf["a"] //Commenting out the last one here fixes the compile error + )] +} +@main def Test = + def assertions(list: List[ValueOf["a"]]): Unit = + assert(list.size == 23) + assert(list.map(_.value) == List( + "a", "a", "a", "a", "a", + "a", "a", "a", "a", "a", + "a", "a", "a", "a", "a", + "a", "a", "a", "a", "a", + "a", "a", "a" + )) + assertions(derivation.test.toList) From 799f22592a88546780691207d16c07c243b6a6c9 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 14 Apr 2023 12:58:37 +0200 Subject: [PATCH 371/657] Fix failing main due to wunused --- tests/neg-custom-args/fatal-warnings/i15503e.scala | 4 ++-- tests/neg-custom-args/fatal-warnings/i15503g.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503e.scala b/tests/neg-custom-args/fatal-warnings/i15503e.scala index 57664cd08dcd..b1b2e73d51a8 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503e.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503e.scala @@ -8,7 +8,7 @@ object Foo { private def f2(a: Int) = default_val // error private def f3(a: Int)(using Int) = a // OK private def f4(a: Int)(using Int) = default_val // error - private def f6(a: Int)(using Int) = summon[Int] // error + private def f6(a: Int)(using Int) = summon[Int] // ok private def f7(a: Int)(using Int) = summon[Int] + a // OK } @@ -49,7 +49,7 @@ package foo.test.trivial: private def f6(x: Int) = X // OK private def f7(x: Int) = Y // OK private def f8(x: Int): List[C] = Nil // OK - private def f9(x: Int): List[Int] = List(1,2,3,4) // error + private def f9(x: Int): List[Int] = List(1,2,3,4) // ok private def foo:Int = 32 // OK private def f77(x: Int) = foo // error } diff --git a/tests/neg-custom-args/fatal-warnings/i15503g.scala b/tests/neg-custom-args/fatal-warnings/i15503g.scala index 2185bfed711d..da2921351519 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503g.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503g.scala @@ -8,7 +8,7 @@ object Foo { private def f2(a: Int) = default_int // error private def f3(a: Int)(using Int) = a // OK private def f4(a: Int)(using Int) = default_int // error - private def f6(a: Int)(using Int) = summon[Int] // error + private def f6(a: Int)(using Int) = summon[Int] // ok private def f7(a: Int)(using Int) = summon[Int] + a // OK /* --- Trivial method check --- */ private def g1(x: Int) = 1 // OK From abe1879401d109b19907224fb489b10ce860b788 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 14 Apr 2023 14:11:20 +0200 Subject: [PATCH 372/657] Fix condition in checking if method is private in CHeckUnused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503e.scala | 4 ++-- tests/neg-custom-args/fatal-warnings/i15503f.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503g.scala | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index df916fc76a3b..ce05e6c125de 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -118,7 +118,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def prepareForDefDef(tree: tpd.DefDef)(using Context): Context = unusedDataApply{ ud => - if !tree.rawMods.is(Private) then + if !tree.symbol.is(Private) then tree.termParamss.flatten.foreach { p => ud.addIgnoredParam(p.symbol) } diff --git a/tests/neg-custom-args/fatal-warnings/i15503e.scala b/tests/neg-custom-args/fatal-warnings/i15503e.scala index b1b2e73d51a8..57664cd08dcd 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503e.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503e.scala @@ -8,7 +8,7 @@ object Foo { private def f2(a: Int) = default_val // error private def f3(a: Int)(using Int) = a // OK private def f4(a: Int)(using Int) = default_val // error - private def f6(a: Int)(using Int) = summon[Int] // ok + private def f6(a: Int)(using Int) = summon[Int] // error private def f7(a: Int)(using Int) = summon[Int] + a // OK } @@ -49,7 +49,7 @@ package foo.test.trivial: private def f6(x: Int) = X // OK private def f7(x: Int) = Y // OK private def f8(x: Int): List[C] = Nil // OK - private def f9(x: Int): List[Int] = List(1,2,3,4) // ok + private def f9(x: Int): List[Int] = List(1,2,3,4) // error private def foo:Int = 32 // OK private def f77(x: Int) = foo // error } diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index f909272af732..1dc16283caa8 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -8,7 +8,7 @@ object Xd { private def f2(a: Int) = 1 // OK private def f3(a: Int)(using Int) = a // OK private def f4(a: Int)(using Int) = default_int // OK - private def f6(a: Int)(using Int) = summon[Int] // OK + private def f6(a: Int)(using Int) = summon[Int] // error private def f7(a: Int)(using Int) = summon[Int] + a // OK private def f8(a: Int)(using foo: Int) = a // error } diff --git a/tests/neg-custom-args/fatal-warnings/i15503g.scala b/tests/neg-custom-args/fatal-warnings/i15503g.scala index da2921351519..2185bfed711d 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503g.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503g.scala @@ -8,7 +8,7 @@ object Foo { private def f2(a: Int) = default_int // error private def f3(a: Int)(using Int) = a // OK private def f4(a: Int)(using Int) = default_int // error - private def f6(a: Int)(using Int) = summon[Int] // ok + private def f6(a: Int)(using Int) = summon[Int] // error private def f7(a: Int)(using Int) = summon[Int] + a // OK /* --- Trivial method check --- */ private def g1(x: Int) = 1 // OK From ffa268222a3c989229731cf33e901a2c9cc4edab Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 14 Apr 2023 18:14:51 +0200 Subject: [PATCH 373/657] Remove unnecessary test --- tests/neg-custom-args/fatal-warnings/i15503i.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 3b653bfadb0a..fefead7f01a3 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -46,9 +46,6 @@ package foo.test.scala.annotation: def a1(a: Int) = a // OK def a2(a: Int) = default_int // ok - private def a2_p(a: Int) = default_int // error - def a2_p_used = a2_p(3) - def a3(@unused a: Int) = default_int //OK def b1 = From 8fdd8d61e6ee9a22f1be7698813d55f37064950b Mon Sep 17 00:00:00 2001 From: Simon R Date: Sat, 15 Apr 2023 11:25:17 +0200 Subject: [PATCH 374/657] Adjust failing expectation in WUnused tests --- tests/neg-custom-args/fatal-warnings/i15503f.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index 1dc16283caa8..f909272af732 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -8,7 +8,7 @@ object Xd { private def f2(a: Int) = 1 // OK private def f3(a: Int)(using Int) = a // OK private def f4(a: Int)(using Int) = default_int // OK - private def f6(a: Int)(using Int) = summon[Int] // error + private def f6(a: Int)(using Int) = summon[Int] // OK private def f7(a: Int)(using Int) = summon[Int] + a // OK private def f8(a: Int)(using foo: Int) = a // error } From a599381adad45f93bdd0d081d3e3ca8036dc83e2 Mon Sep 17 00:00:00 2001 From: Philippus Date: Sat, 15 Apr 2023 15:39:14 +0200 Subject: [PATCH 375/657] Update asm to 9.5 --- compiler/src/dotty/tools/backend/jvm/BackendUtils.scala | 1 + compiler/src/dotty/tools/dotc/config/ScalaSettings.scala | 2 +- project/Build.scala | 2 +- tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala | 3 ++- tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala index d54364b1675f..2eaaccdd441d 100644 --- a/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala +++ b/compiler/src/dotty/tools/backend/jvm/BackendUtils.scala @@ -35,6 +35,7 @@ class BackendUtils(val postProcessor: PostProcessor) { case "18" => asm.Opcodes.V18 case "19" => asm.Opcodes.V19 case "20" => asm.Opcodes.V20 + case "21" => asm.Opcodes.V21 } lazy val extraProc: Int = { diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 32cb030a3296..c06aa304ef72 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -17,7 +17,7 @@ class ScalaSettings extends SettingGroup with AllScalaSettings object ScalaSettings: // Keep synchronized with `classfileVersion` in `BackendUtils` private val minTargetVersion = 8 - private val maxTargetVersion = 20 + private val maxTargetVersion = 21 def supportedTargetVersions: List[String] = (minTargetVersion to maxTargetVersion).toList.map(_.toString) diff --git a/project/Build.scala b/project/Build.scala index 82b7fee64879..2689aa65f972 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -547,7 +547,7 @@ object Build { // get libraries onboard libraryDependencies ++= Seq( - "org.scala-lang.modules" % "scala-asm" % "9.4.0-scala-1", // used by the backend + "org.scala-lang.modules" % "scala-asm" % "9.5.0-scala-1", // used by the backend Dependencies.oldCompilerInterface, // we stick to the old version to avoid deprecation warnings "org.jline" % "jline-reader" % "3.19.0", // used by the REPL "org.jline" % "jline-terminal" % "3.19.0", diff --git a/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala b/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala index 77e58440aa97..5eb8d7a52aa2 100644 --- a/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala +++ b/tests/pos-with-compiler-cc/backend/jvm/BCodeIdiomatic.scala @@ -54,7 +54,8 @@ trait BCodeIdiomatic extends caps.Pure { case "17" => asm.Opcodes.V17 case "18" => asm.Opcodes.V18 case "19" => asm.Opcodes.V19 - case "20" => asm.Opcodes.V20*/ + case "20" => asm.Opcodes.V20 + case "21" => asm.Opcodes.V21*/ } lazy val majorVersion: Int = (classfileVersion & 0xFF) diff --git a/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala b/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala index f7743dddda4e..20708b98cc95 100644 --- a/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala +++ b/tests/pos-with-compiler-cc/dotc/config/ScalaSettings.scala @@ -17,7 +17,7 @@ class ScalaSettings extends SettingGroup with AllScalaSettings object ScalaSettings: // Keep synchronized with `classfileVersion` in `BCodeIdiomatic` private val minTargetVersion = 8 - private val maxTargetVersion = 20 + private val maxTargetVersion = 21 def supportedTargetVersions: List[String] = (minTargetVersion to maxTargetVersion).toList.map(_.toString) From 0d2ffb59fb2b23b41ce8f6b7b4aa478e658fdde3 Mon Sep 17 00:00:00 2001 From: Florian3k Date: Fri, 14 Apr 2023 10:19:22 +0200 Subject: [PATCH 376/657] scaladoc: fix crash when processing extends call --- scaladoc-testcases/src/tests/extendsCall.scala | 6 ++++++ .../scaladoc/tasty/ClassLikeSupport.scala | 18 +++++++++++------- .../TranslatableSignaturesTestCases.scala | 2 ++ 3 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 scaladoc-testcases/src/tests/extendsCall.scala diff --git a/scaladoc-testcases/src/tests/extendsCall.scala b/scaladoc-testcases/src/tests/extendsCall.scala new file mode 100644 index 000000000000..b90af8162e15 --- /dev/null +++ b/scaladoc-testcases/src/tests/extendsCall.scala @@ -0,0 +1,6 @@ +package tests +package extendsCall + +class Impl() extends Base(Seq.empty, c = "-") //expected: class Impl() extends Base + +class Base(val a: Seq[String], val b: String = "", val c: String = "") //expected: class Base(val a: Seq[String], val b: String, val c: String) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 0c85dff0879f..40548ae97a89 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -64,14 +64,18 @@ trait ClassLikeSupport: signatureOnly: Boolean = false, modifiers: Seq[Modifier] = classDef.symbol.getExtraModifiers(), ): Member = - def unpackTreeToClassDef(tree: Tree): ClassDef = tree match - case tree: ClassDef => tree - case TypeDef(_, tbt: TypeBoundsTree) => unpackTreeToClassDef(tbt.tpe.typeSymbol.tree) - case TypeDef(_, tt: TypeTree) => unpackTreeToClassDef(tt.tpe.typeSymbol.tree) - case c: Apply => - c.symbol.owner.tree.symbol.tree match + def unpackTreeToClassDef(tree: Tree): ClassDef = + def unpackApply(a: Apply) = + a.symbol.owner.tree.symbol.tree match case tree: ClassDef => tree - case tt: TypeTree => unpackTreeToClassDef(tt.tpe.typeSymbol.tree) + + tree match + case tree: ClassDef => tree + case TypeDef(_, tbt: TypeBoundsTree) => unpackTreeToClassDef(tbt.tpe.typeSymbol.tree) + case TypeDef(_, tt: TypeTree) => unpackTreeToClassDef(tt.tpe.typeSymbol.tree) + case c: Apply => unpackApply(c) + case Block(_, c: Apply) => unpackApply(c) + case tt: TypeTree => unpackTreeToClassDef(tt.tpe.typeSymbol.tree) def signatureWithName(s: dotty.tools.scaladoc.Signature): dotty.tools.scaladoc.Signature = s match diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala index 302ee39d6351..a09234be5512 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala @@ -108,3 +108,5 @@ class ImplicitMembers extends SignatureTest( ) class NonScala3Parent extends SignatureTest("nonScala3Parent", SignatureTest.all) + +class ExtendsCall extends SignatureTest("extendsCall", SignatureTest.all) From faf70f8dea4d8cde2501530cc289432d5b6755e5 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 17 Apr 2023 13:12:20 +0200 Subject: [PATCH 377/657] test Expr.ofTuple --- tests/run-macros/i17257/a.scala | 15 +++++++++++++++ tests/run-macros/i17257/b.scala | 2 ++ 2 files changed, 17 insertions(+) diff --git a/tests/run-macros/i17257/a.scala b/tests/run-macros/i17257/a.scala index 873e8a6e84a2..4a5682327604 100644 --- a/tests/run-macros/i17257/a.scala +++ b/tests/run-macros/i17257/a.scala @@ -9,6 +9,9 @@ object Helpers: inline def summonAllOptimized[T <: Tuple]: T = ${ summonAllOptimizedImpl[T] } + inline def summon23[E]: (E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E) = + ${ summon23Impl[E] } + private def summonAllOptimizedImpl[T <: Tuple: Type](using q: Quotes): Expr[T] = { import q.reflect.* @@ -22,6 +25,18 @@ object Helpers: .asExprOf[T] } + private def summon23Impl[E: Type](using q: Quotes): Expr[(E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E, E)] = { + import q.reflect.* + + val e = Expr.summon[E].getOrElse(report.errorAndAbort(s"Unable to to find implicit instance for ${TypeRepr.of[E].show}")) + + val tuple = (e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e, e) + + assert(tuple.size == 23) + + Expr.ofTuple(tuple) + } + @tailrec private[derivation] def typesOfTuple( using q: Quotes diff --git a/tests/run-macros/i17257/b.scala b/tests/run-macros/i17257/b.scala index b9159973fbc4..65d66fb20bff 100644 --- a/tests/run-macros/i17257/b.scala +++ b/tests/run-macros/i17257/b.scala @@ -7,6 +7,7 @@ package derivation { ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"], ValueOf["a"] //Commenting out the last one here fixes the compile error )] + val test2 = Helpers.summon23[ValueOf["a"]] } @main def Test = def assertions(list: List[ValueOf["a"]]): Unit = @@ -19,3 +20,4 @@ package derivation { "a", "a", "a" )) assertions(derivation.test.toList) + assertions(derivation.test2.toList) From b3cfa95119f2041078e1e03f47f040cb0ed61a59 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 17 Apr 2023 13:50:37 +0200 Subject: [PATCH 378/657] small optimisation --- library/src/scala/quoted/Expr.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 72ceaa8ffadd..8243e7dc4a4b 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -218,14 +218,13 @@ object Expr { val tupleTpe = tupleTypeFromSeq(seq) tupleTpe.asType match case '[tpe] => - '{ Tuple.fromIArray(IArray(${Varargs(seq)}: _*)).asInstanceOf[tpe & Tuple] } + '{ Tuple.fromIArray(IArray(${Varargs(seq)}*)).asInstanceOf[tpe & Tuple] } private def tupleTypeFromSeq(seq: Seq[Expr[Any]])(using Quotes): quotes.reflect.TypeRepr = import quotes.reflect.* val consRef = Symbol.classSymbol("scala.*:").typeRef seq.foldLeft(TypeRepr.of[EmptyTuple]) { (ts, expr) => - expr match - case '{ $e: t } => AppliedType(consRef, TypeRepr.of[t] :: ts :: Nil) + AppliedType(consRef, expr.asTerm.tpe :: ts :: Nil) } /** Given a tuple of the form `(Expr[A1], ..., Expr[An])`, outputs a tuple `Expr[(A1, ..., An)]`. */ From 75e0460d875d70296c3ea30984b2d44bfc826b26 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Apr 2023 10:55:30 +0200 Subject: [PATCH 379/657] Rename `TreeUnpickler.{readTerm => readTree}` --- .../tools/dotc/core/tasty/TreeUnpickler.scala | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 9078a8959112..428047d96e0c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -114,7 +114,7 @@ class TreeUnpickler(reader: TastyReader, val rdr = new TreeReader(reader) mode match { case UnpickleMode.TopLevel => rdr.readTopLevel() - case UnpickleMode.Term => rdr.readTerm() :: Nil + case UnpickleMode.Term => rdr.readTree() :: Nil case UnpickleMode.TypeTree => rdr.readTpt() :: Nil } } @@ -389,7 +389,7 @@ class TreeUnpickler(reader: TastyReader, val hi = readVariances(readType()) createNullableTypeBounds(lo, hi) case ANNOTATEDtype => - AnnotatedType(readType(), Annotation(readTerm())) + AnnotatedType(readType(), Annotation(readTree())) case ANDtype => AndType(readType(), readType()) case ORtype => @@ -488,7 +488,7 @@ class TreeUnpickler(reader: TastyReader, def readTypeRef(): Type = typeAtAddr(readAddr()) - def readTermRef()(using Context): TermRef = + def readTreeRef()(using Context): TermRef = readType().asInstanceOf[TermRef] /** Under pureFunctions, map all function types to impure function types, @@ -656,7 +656,7 @@ class TreeUnpickler(reader: TastyReader, val ctx1 = localContext(sym)(using ctx0).addMode(Mode.ReadPositions) inContext(sourceChangeContext(Addr(0))(using ctx1)) { // avoids space leaks by not capturing the current context - forkAt(rhsStart).readTerm() + forkAt(rhsStart).readTree() } }) goto(start) @@ -739,7 +739,7 @@ class TreeUnpickler(reader: TastyReader, readByte() val end = readEnd() val tp = readType() - val lazyAnnotTree = readLaterWithOwner(end, _.readTerm()) + val lazyAnnotTree = readLaterWithOwner(end, _.readTree()) owner => new DeferredSymAndTree(tp.typeSymbol, lazyAnnotTree(owner).complete): // Only force computation of symbol if it has the right name. This added @@ -790,7 +790,7 @@ class TreeUnpickler(reader: TastyReader, if (sctx `ne` ctx) return processPackage(op)(using sctx) readByte() val end = readEnd() - val pid = ref(readTermRef()).asInstanceOf[RefTree] + val pid = ref(readTreeRef()).asInstanceOf[RefTree] op(pid, end)(using localContext(pid.symbol.moduleClass)) } @@ -858,7 +858,7 @@ class TreeUnpickler(reader: TastyReader, def complete(using Context) = inlines.Inlines.bodyToInline(sym) } else - readLater(end, _.readTerm()) + readLater(end, _.readTree()) def ValDef(tpt: Tree) = ta.assignType(untpd.ValDef(sym.name.asTermName, tpt, readRhs(using localCtx)), sym) @@ -984,7 +984,7 @@ class TreeUnpickler(reader: TastyReader, case SELECTin => val end = readEnd() readName() - readTerm() match + readTree() match case nu: New => try nu.tpe finally goto(end) @@ -1000,7 +1000,7 @@ class TreeUnpickler(reader: TastyReader, collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) { nextUnsharedTag match case APPLY | TYPEAPPLY | BLOCK => - if withArgs then readTerm() + if withArgs then readTree() else InferredTypeTree().withType(readParentType()) case _ => readTpt() } @@ -1095,7 +1095,7 @@ class TreeUnpickler(reader: TastyReader, setSpan(start, PackageDef(pid, readIndexedStats(exprOwner, end))) } case _ => - readTerm()(using ctx.withOwner(exprOwner)) + readTree()(using ctx.withOwner(exprOwner)) } inline def readImportOrExport(inline mkTree: @@ -1104,7 +1104,7 @@ class TreeUnpickler(reader: TastyReader, assert(sourcePathAt(start).isEmpty) readByte() readEnd() - val expr = readTerm() + val expr = readTree() setSpan(start, mkTree(expr, readSelectors())) } @@ -1162,14 +1162,14 @@ class TreeUnpickler(reader: TastyReader, // ------ Reading trees ----------------------------------------------------- - def readTerm()(using Context): Tree = { // TODO: rename to readTree + def readTree()(using Context): Tree = { val sctx = sourceChangeContext() - if (sctx `ne` ctx) return readTerm()(using sctx) + if (sctx `ne` ctx) return readTree()(using sctx) val start = currentAddr val tag = readByte() pickling.println(s"reading term ${astTagToString(tag)} at $start, ${ctx.source}") - def readPathTerm(): Tree = { + def readPathTree(): Tree = { goto(start) readType() match { case path: TypeRef => TypeTree(path) @@ -1189,12 +1189,12 @@ class TreeUnpickler(reader: TastyReader, ConstFold.Select(untpd.Select(qual, name).withType(tpe)) def completeSelect(name: Name, sig: Signature, target: Name): Select = - val qual = readTerm() + val qual = readTree() val denot = accessibleDenot(qual.tpe.widenIfUnstable, name, sig, target) makeSelect(qual, name, denot) def readQualId(): (untpd.Ident, TypeRef) = - val qual = readTerm().asInstanceOf[untpd.Ident] + val qual = readTree().asInstanceOf[untpd.Ident] (untpd.Ident(qual.name).withSpan(qual.span), qual.tpe.asInstanceOf[TypeRef]) def accessibleDenot(qualType: Type, name: Name, sig: Signature, target: Name) = { @@ -1204,9 +1204,9 @@ class TreeUnpickler(reader: TastyReader, else qualType.findMember(name, pre, excluded = Private).atSignature(sig, target) } - def readSimpleTerm(): Tree = tag match { + def readSimpleTree(): Tree = tag match { case SHAREDterm => - forkAt(readAddr()).readTerm() + forkAt(readAddr()).readTree() case IDENT => untpd.Ident(readName()).withType(readType()) case IDENTtpt => @@ -1225,16 +1225,16 @@ class TreeUnpickler(reader: TastyReader, case NEW => New(readTpt()) case THROW => - Throw(readTerm()) + Throw(readTree()) case SINGLETONtpt => - SingletonTypeTree(readTerm()) + SingletonTypeTree(readTree()) case BYNAMEtpt => val arg = readTpt() ByNameTypeTree(if knowsPureFuns then arg else arg.adaptByNameArgUnderPureFuns) case NAMEDARG => - NamedArg(readName(), readTerm()) + NamedArg(readName(), readTree()) case _ => - readPathTerm() + readPathTree() } /** Adapt constructor calls where class has only using clauses from old to new scheme. @@ -1271,58 +1271,58 @@ class TreeUnpickler(reader: TastyReader, tree.overwriteType(tree.tpe.simplified) tree - def readLengthTerm(): Tree = { + def readLengthTree(): Tree = { val end = readEnd() val result = (tag: @switch) match { case SUPER => - val qual = readTerm() + val qual = readTree() val (mixId, mixTpe) = ifBefore(end)(readQualId(), (untpd.EmptyTypeIdent, NoType)) tpd.Super(qual, mixId, mixTpe.typeSymbol) case APPLY => - val fn = readTerm() - val args = until(end)(readTerm()) + val fn = readTree() + val args = until(end)(readTree()) if fn.symbol.isConstructor then constructorApply(fn, args) else tpd.Apply(fn, args) case TYPEAPPLY => - tpd.TypeApply(readTerm(), until(end)(readTpt())) + tpd.TypeApply(readTree(), until(end)(readTpt())) case APPLYsigpoly => - val fn = readTerm() + val fn = readTree() val methType = readType() - val args = until(end)(readTerm()) + val args = until(end)(readTree()) val fun2 = typer.Applications.retypeSignaturePolymorphicFn(fn, methType) tpd.Apply(fun2, args) case TYPED => - val expr = readTerm() + val expr = readTree() val tpt = readTpt() Typed(expr, tpt) case ASSIGN => - Assign(readTerm(), readTerm()) + Assign(readTree(), readTree()) case BLOCK => val exprReader = fork skipTree() readStats(ctx.owner, end, - (stats, ctx) => Block(stats, exprReader.readTerm()(using ctx))) + (stats, ctx) => Block(stats, exprReader.readTree()(using ctx))) case INLINED => val exprReader = fork skipTree() def maybeCall = nextUnsharedTag match { case VALDEF | DEFDEF => EmptyTree - case _ => readTerm() + case _ => readTree() } val call = ifBefore(end)(maybeCall, EmptyTree) val bindings = readStats(ctx.owner, end).asInstanceOf[List[ValOrDefDef]] - val expansion = exprReader.readTerm() // need bindings in scope, so needs to be read before + val expansion = exprReader.readTree() // need bindings in scope, so needs to be read before Inlined(call, bindings, expansion) case IF => if (nextByte == INLINE) { readByte() - InlineIf(readTerm(), readTerm(), readTerm()) + InlineIf(readTree(), readTree(), readTree()) } else - If(readTerm(), readTerm(), readTerm()) + If(readTree(), readTree(), readTree()) case LAMBDA => - val meth = readTerm() + val meth = readTree() val tpt = ifBefore(end)(readTpt(), EmptyTree) Closure(Nil, meth, tpt) case MATCH => @@ -1333,24 +1333,24 @@ class TreeUnpickler(reader: TastyReader, } else if (nextByte == INLINE) { readByte() - InlineMatch(readTerm(), readCases(end)) + InlineMatch(readTree(), readCases(end)) } - else Match(readTerm(), readCases(end))) + else Match(readTree(), readCases(end))) case RETURN => val from = readSymRef() - val expr = ifBefore(end)(readTerm(), EmptyTree) + val expr = ifBefore(end)(readTree(), EmptyTree) Return(expr, Ident(from.termRef)) case WHILE => - WhileDo(readTerm(), readTerm()) + WhileDo(readTree(), readTree()) case TRY => simplifyLub( - Try(readTerm(), readCases(end), ifBefore(end)(readTerm(), EmptyTree))) + Try(readTree(), readCases(end), ifBefore(end)(readTree(), EmptyTree))) case SELECTouter => val levels = readNat() - readTerm().outerSelect(levels, SkolemType(readType())) + readTree().outerSelect(levels, SkolemType(readType())) case SELECTin => var sname = readName() - val qual = readTerm() + val qual = readTree() val ownerTpe = readType() val owner = ownerTpe.typeSymbol val SignedName(name, sig, target) = sname: @unchecked // only methods with params use SELECTin @@ -1381,26 +1381,26 @@ class TreeUnpickler(reader: TastyReader, makeSelect(qual, name, denot) case REPEATED => val elemtpt = readTpt() - SeqLiteral(until(end)(readTerm()), elemtpt) + SeqLiteral(until(end)(readTree()), elemtpt) case BIND => val sym = symAtAddr.getOrElse(start, forkAt(start).createSymbol()) readName() readType() - val body = readTerm() + val body = readTree() val (givenFlags, _, _) = readModifiers(end) sym.setFlag(givenFlags) Bind(sym, body) case ALTERNATIVE => - Alternative(until(end)(readTerm())) + Alternative(until(end)(readTree())) case UNAPPLY => - val fn = readTerm() + val fn = readTree() val implicitArgs = collectWhile(nextByte == IMPLICITarg) { readByte() - readTerm() + readTree() } val patType = readType() - val argPats = until(end)(readTerm()) + val argPats = until(end)(readTree()) UnApply(fn, implicitArgs, argPats, patType) case REFINEDtpt => val refineCls = symAtAddr.getOrElse(start, @@ -1420,7 +1420,7 @@ class TreeUnpickler(reader: TastyReader, val ownType = ctx.typeAssigner.processAppliedType(tree, tycon.tpe.safeAppliedTo(args.tpes)) tree.withType(postProcessFunction(ownType)) case ANNOTATEDtpt => - Annotated(readTpt(), readTerm()) + Annotated(readTpt(), readTree()) case LAMBDAtpt => val tparams = readParams[TypeDef](TYPEPARAM) val body = readTpt() @@ -1438,16 +1438,16 @@ class TreeUnpickler(reader: TastyReader, case HOLE => val idx = readNat() val tpe = readType() - val args = until(end)(readTerm()) + val args = until(end)(readTree()) Hole(true, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) case _ => - readPathTerm() + readPathTree() } assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}") result } - val tree = if (tag < firstLengthTreeTag) readSimpleTerm() else readLengthTerm() + val tree = if (tag < firstLengthTreeTag) readSimpleTree() else readLengthTree() setSpan(start, tree) } @@ -1472,10 +1472,10 @@ class TreeUnpickler(reader: TastyReader, val end = readEnd() val idx = readNat() val tpe = readType() - val args = until(end)(readTerm()) + val args = until(end)(readTree()) Hole(false, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) case _ => - if (isTypeTreeTag(nextByte)) readTerm() + if (isTypeTreeTag(nextByte)) readTree() else { val start = currentAddr val tp = readType() @@ -1500,9 +1500,9 @@ class TreeUnpickler(reader: TastyReader, val start = currentAddr assert(readByte() == CASEDEF) val end = readEnd() - val pat = readTerm() - val rhs = readTerm() - val guard = ifBefore(end)(readTerm(), EmptyTree) + val pat = readTree() + val rhs = readTree() + val guard = ifBefore(end)(readTree(), EmptyTree) setSpan(start, CaseDef(pat, guard, rhs)) } From 372d58d81f3ac69b4e2621c002164f93d5e295eb Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Tue, 18 Apr 2023 12:48:15 +0200 Subject: [PATCH 380/657] Add test for issue #17231 --- compiler/test-resources/repl/i17231 | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 compiler/test-resources/repl/i17231 diff --git a/compiler/test-resources/repl/i17231 b/compiler/test-resources/repl/i17231 new file mode 100644 index 000000000000..07a509fea917 --- /dev/null +++ b/compiler/test-resources/repl/i17231 @@ -0,0 +1,2 @@ +scala> summon[ValueOf["a"]] +val res0: ValueOf["a"] = scala.ValueOf@61 From fe4478be2ca86e5fe2e4e6860f666c6ff0ec6cfe Mon Sep 17 00:00:00 2001 From: Florian3k Date: Tue, 18 Apr 2023 16:14:30 +0200 Subject: [PATCH 381/657] remove unnecessary .symbol.tree --- scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 40548ae97a89..49cb74073295 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -66,7 +66,7 @@ trait ClassLikeSupport: ): Member = def unpackTreeToClassDef(tree: Tree): ClassDef = def unpackApply(a: Apply) = - a.symbol.owner.tree.symbol.tree match + a.symbol.owner.tree match case tree: ClassDef => tree tree match From 95d7e7941548c6e05361f8b9533dca478dfd426b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Apr 2023 17:04:59 +0200 Subject: [PATCH 382/657] Use `foreachSubTree` instead of new `TreeTraverser` --- .../tools/dotc/ast/TreeMapWithImplicits.scala | 14 ++----- .../dotty/tools/dotc/inlines/Inliner.scala | 39 ++++++++----------- .../tools/dotc/quoted/PickledQuotes.scala | 4 +- .../src/dotty/tools/dotc/typer/Checking.scala | 12 ++---- 4 files changed, 26 insertions(+), 43 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala b/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala index e52bf1064e4c..ae674c25dc3d 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeMapWithImplicits.scala @@ -30,16 +30,10 @@ class TreeMapWithImplicits extends tpd.TreeMapWithPreciseStatContexts { private def patternScopeCtx(pattern: Tree)(using Context): Context = { val nestedCtx = ctx.fresh.setNewScope - new TreeTraverser { - def traverse(tree: Tree)(using Context): Unit = { - tree match { - case d: DefTree if d.symbol.isOneOf(GivenOrImplicitVal) => - nestedCtx.enter(d.symbol) - case _ => - } - traverseChildren(tree) - } - }.traverse(pattern) + pattern.foreachSubTree { + case d: DefTree if d.symbol.isOneOf(GivenOrImplicitVal) => nestedCtx.enter(d.symbol) + case _ => + } nestedCtx } diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 872dc7793ff4..20124e04abd4 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -963,29 +963,24 @@ class Inliner(val call: tpd.Tree)(using Context): bindingOfSym(binding.symbol) = binding } - val countRefs = new TreeTraverser { - override def traverse(t: Tree)(using Context) = { - def updateRefCount(sym: Symbol, inc: Int) = - for (x <- refCount.get(sym)) refCount(sym) = x + inc - def updateTermRefCounts(t: Tree) = - t.typeOpt.foreachPart { - case ref: TermRef => updateRefCount(ref.symbol, 2) // can't be inlined, so make sure refCount is at least 2 - case _ => - } - - t match { - case t: RefTree => - updateRefCount(t.symbol, 1) - updateTermRefCounts(t) - case _: New | _: TypeTree => - updateTermRefCounts(t) - case _ => - } - traverseChildren(t) + def updateRefCount(sym: Symbol, inc: Int) = + for (x <- refCount.get(sym)) refCount(sym) = x + inc + def updateTermRefCounts(tree: Tree) = + tree.typeOpt.foreachPart { + case ref: TermRef => updateRefCount(ref.symbol, 2) // can't be inlined, so make sure refCount is at least 2 + case _ => } - } - countRefs.traverse(tree) - for (binding <- bindings) countRefs.traverse(binding) + def countRefs(tree: Tree) = + tree.foreachSubTree { + case t: RefTree => + updateRefCount(t.symbol, 1) + updateTermRefCounts(t) + case t @ (_: New | _: TypeTree) => + updateTermRefCounts(t) + case _ => + } + countRefs(tree) + for (binding <- bindings) countRefs(binding) def retain(boundSym: Symbol) = { refCount.get(boundSym) match { diff --git a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala index 20bcba417a5e..43e2aae4c58c 100644 --- a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala @@ -275,9 +275,7 @@ object PickledQuotes { QuotesCache(pickled) = tree // Make sure trees and positions are fully loaded - new TreeTraverser { - def traverse(tree: Tree)(using Context): Unit = traverseChildren(tree) - }.traverse(tree) + tree.foreachSubTree(identity) quotePickling.println(i"**** unpickled quote\n$tree") diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index bff9310dee88..436721a6774b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1193,15 +1193,11 @@ trait Checking { */ def checkNoForwardDependencies(vparams: List[ValDef])(using Context): Unit = vparams match { case vparam :: vparams1 => - val check = new TreeTraverser { - def traverse(tree: Tree)(using Context) = tree match { - case id: Ident if vparams.exists(_.symbol == id.symbol) => - report.error(em"illegal forward reference to method parameter", id.srcPos) - case _ => - traverseChildren(tree) - } + vparam.tpt.foreachSubTree { + case id: Ident if vparams.exists(_.symbol == id.symbol) => + report.error(em"illegal forward reference to method parameter", id.srcPos) + case _ => } - check.traverse(vparam.tpt) checkNoForwardDependencies(vparams1) case Nil => } From 35b1d754ef7be055a73a08766a44acdec25635df Mon Sep 17 00:00:00 2001 From: xuwei-k <6b656e6a69@gmail.com> Date: Tue, 18 Apr 2023 15:37:04 +0900 Subject: [PATCH 383/657] fix incorrect scaladoc `@param` --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 6 +++--- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- compiler/src/dotty/tools/dotc/core/Symbols.scala | 2 +- .../tools/dotc/core/unpickleScala2/Scala2Unpickler.scala | 5 ++--- compiler/src/dotty/tools/dotc/coverage/Location.scala | 2 +- compiler/src/dotty/tools/dotc/inlines/Inliner.scala | 2 +- .../src/dotty/tools/dotc/inlines/PrepareInlineable.scala | 2 +- compiler/src/dotty/tools/dotc/plugins/Plugin.scala | 6 +++--- compiler/src/dotty/tools/dotc/transform/init/Cache.scala | 4 ++-- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 1 - compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 2 +- compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- compiler/src/dotty/tools/dotc/util/Signatures.scala | 2 +- compiler/src/dotty/tools/dotc/util/StackTraceOps.scala | 1 - library/src/scala/quoted/Quotes.scala | 8 ++++---- 16 files changed, 23 insertions(+), 26 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index c0b5987c3875..1c1e80922c05 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -855,9 +855,9 @@ object Trees { } /** extends parents { self => body } - * @param parentsOrDerived A list of parents followed by a list of derived classes, - * if this is of class untpd.DerivingTemplate. - * Typed templates only have parents. + * @param preParentsOrDerived A list of parents followed by a list of derived classes, + * if this is of class untpd.DerivingTemplate. + * Typed templates only have parents. */ case class Template[+T <: Untyped] private[ast] (constr: DefDef[T], private var preParentsOrDerived: LazyTreeList[T], self: ValDef[T], private var preBody: LazyTreeList[T])(implicit @constructorOnly src: SourceFile) extends DefTree[T] with WithLazyFields { diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index d1b1cdf607b5..c6e0bc581b59 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -1548,7 +1548,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { * * @param trees the elements the list represented by * the resulting tree should contain. - * @param tpe the type of the elements of the resulting list. + * @param tpt the type of the elements of the resulting list. * */ def mkList(trees: List[Tree], tpt: Tree)(using Context): Tree = diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index beeaa2ee922e..bcd91336f627 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1409,7 +1409,7 @@ object SymDenotations { /** The symbol overriding this symbol in given subclass `ofclazz`. * - * @param ofclazz is a subclass of this symbol's owner + * @pre `inClass` is a subclass of this symbol's owner */ final def overridingSymbol(inClass: ClassSymbol)(using Context): Symbol = if (canMatchInheritedSymbols) matchingDecl(inClass, inClass.thisType) diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index aa3ae0c3c513..7397ed1fc705 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -40,7 +40,7 @@ object Symbols { val Ids: Property.Key[Array[String]] = new Property.Key /** A Symbol represents a Scala definition/declaration or a package. - * @param coord The coordinates of the symbol (a position or an index) + * @param myCoord The coordinates of the symbol (a position or an index) * @param id A unique identifier of the symbol (unique per ContextBase) */ class Symbol private[Symbols] (private var myCoord: Coord, val id: Int, val nestingLevel: Int) diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 50b0b875c1fc..deb022d3c261 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -133,9 +133,8 @@ object Scala2Unpickler { /** Unpickle symbol table information descending from a class and/or module root * from an array of bytes. * @param bytes bytearray from which we unpickle - * @param classroot the top-level class which is unpickled, or NoSymbol if inapplicable - * @param moduleroot the top-level module class which is unpickled, or NoSymbol if inapplicable - * @param filename filename associated with bytearray, only used for error messages + * @param classRoot the top-level class which is unpickled, or NoSymbol if inapplicable + * @param moduleClassRoot the top-level module class which is unpickled, or NoSymbol if inapplicable */ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: ClassDenotation)(ictx: Context) extends PickleBuffer(bytes, 0, -1) with ClassfileParser.Embedded { diff --git a/compiler/src/dotty/tools/dotc/coverage/Location.scala b/compiler/src/dotty/tools/dotc/coverage/Location.scala index c565c2bb1116..aa7a586d4b57 100644 --- a/compiler/src/dotty/tools/dotc/coverage/Location.scala +++ b/compiler/src/dotty/tools/dotc/coverage/Location.scala @@ -13,7 +13,7 @@ import dotty.tools.dotc.util.SourceFile * @param className name of the closest enclosing class * @param fullClassName fully qualified name of the closest enclosing class * @param classType "type" of the closest enclosing class: Class, Trait or Object - * @param method name of the closest enclosing method + * @param methodName name of the closest enclosing method * @param sourcePath absolute path of the source file */ final case class Location( diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 872dc7793ff4..54f3c8562f9f 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -201,7 +201,7 @@ class Inliner(val call: tpd.Tree)(using Context): * to `buf`. * @param name the name of the parameter * @param formal the type of the parameter - * @param arg the argument corresponding to the parameter + * @param arg0 the argument corresponding to the parameter * @param buf the buffer to which the definition should be appended */ private[inlines] def paramBindingDef(name: Name, formal: Type, arg0: Tree, diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 7a0d3f61cb33..33bc5a7ef10e 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -256,7 +256,7 @@ object PrepareInlineable { /** Register inline info for given inlineable method `sym`. * - * @param sym The symbol denotation of the inlineable method for which info is registered + * @param inlined The symbol denotation of the inlineable method for which info is registered * @param treeExpr A function that computes the tree to be inlined, given a context * This tree may still refer to non-public members. * @param ctx The context to use for evaluating `treeExpr`. It needs diff --git a/compiler/src/dotty/tools/dotc/plugins/Plugin.scala b/compiler/src/dotty/tools/dotc/plugins/Plugin.scala index 21bb0fa2be54..be6fecebb55a 100644 --- a/compiler/src/dotty/tools/dotc/plugins/Plugin.scala +++ b/compiler/src/dotty/tools/dotc/plugins/Plugin.scala @@ -44,7 +44,7 @@ sealed trait Plugin { trait StandardPlugin extends Plugin { /** Non-research plugins should override this method to return the phases * - * @param options: commandline options to the plugin, `-P:plugname:opt1,opt2` becomes List(opt1, opt2) + * @param options commandline options to the plugin, `-P:plugname:opt1,opt2` becomes List(opt1, opt2) * @return a list of phases to be added to the phase plan */ def init(options: List[String]): List[PluginPhase] @@ -57,8 +57,8 @@ trait StandardPlugin extends Plugin { trait ResearchPlugin extends Plugin { /** Research plugins should override this method to return the new phase plan * - * @param options: commandline options to the plugin, `-P:plugname:opt1,opt2` becomes List(opt1, opt2) - * @param plan: the given phase plan + * @param options commandline options to the plugin, `-P:plugname:opt1,opt2` becomes List(opt1, opt2) + * @param plan the given phase plan * @return the new phase plan */ def init(options: List[String], plan: List[List[Phase]])(using Context): List[List[Phase]] diff --git a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala index 054db090c3bc..c0391a05262d 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Cache.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Cache.scala @@ -50,8 +50,8 @@ import tpd.Tree * need to be decided by the specific analysis and justified by reasoning about * soundness. * - * @param Config The analysis state that matters for evaluating an expression. - * @param Res The result from the evaluation the given expression. + * @tparam Config The analysis state that matters for evaluating an expression. + * @tparam Res The result from the evaluation the given expression. */ class Cache[Config, Res]: import Cache.* diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index a9631ad45e28..f303bfbc5e19 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1591,7 +1591,6 @@ trait Implicits: * implicit search. * * @param cand The candidate implicit to be explored. - * @param pt The target type for the above candidate. * @result True if this candidate/pt are divergent, false otherwise. */ def checkDivergence(cand: Candidate): Boolean = diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 4ca00ce6366f..73331046b41f 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -295,7 +295,7 @@ object RefChecks { * TODO This still needs to be cleaned up; the current version is a straight port of what was there * before, but it looks too complicated and method bodies are far too large. * - * @param makeOverridePairsChecker A function for creating a OverridePairsChecker instance + * @param makeOverridingPairsChecker A function for creating a OverridePairsChecker instance * from the class symbol and the self type */ def checkAllOverrides(clazz: ClassSymbol, makeOverridingPairsChecker: ((ClassSymbol, Type) => Context ?=> OverridingPairsChecker) | Null = null)(using Context): Unit = { diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 98e9cb638c17..6ac45cbcf04d 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -18,7 +18,7 @@ trait TypeAssigner { import TypeAssigner.* /** The qualifying class of a this or super with prefix `qual` (which might be empty). - * @param packageOk The qualifier may refer to a package. + * @param packageOK The qualifier may refer to a package. */ def qualifyingClass(tree: untpd.Tree, qual: Name, packageOK: Boolean)(using Context): Symbol = { def qualifies(sym: Symbol) = diff --git a/compiler/src/dotty/tools/dotc/util/Signatures.scala b/compiler/src/dotty/tools/dotc/util/Signatures.scala index ddf89e7dd04d..5513a1f803c6 100644 --- a/compiler/src/dotty/tools/dotc/util/Signatures.scala +++ b/compiler/src/dotty/tools/dotc/util/Signatures.scala @@ -54,7 +54,7 @@ object Signatures { * Extract (current parameter index, function index, functions) method call for given position. * * @param path The path to the function application - * @param span The position of the cursor + * @param pos The position of the cursor * * @return A triple containing the index of the parameter being edited, the index of functeon * being called, the list of overloads of this function). diff --git a/compiler/src/dotty/tools/dotc/util/StackTraceOps.scala b/compiler/src/dotty/tools/dotc/util/StackTraceOps.scala index 7fa54606c572..f991005f0c43 100644 --- a/compiler/src/dotty/tools/dotc/util/StackTraceOps.scala +++ b/compiler/src/dotty/tools/dotc/util/StackTraceOps.scala @@ -29,7 +29,6 @@ object StackTraceOps: * If a stack trace is truncated, it will be followed by a line of the form * `... 3 elided`, by analogy to the lines `... 3 more` which indicate * shared stack trace segments. - * @param e the exception * @param p the predicate to select the prefix */ def formatStackTracePrefix(p: StackTraceElement => Boolean): String = diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index edf8aa61b559..2ead4a8607a3 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -1429,9 +1429,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * ) * ``` * - * @param owner: owner of the generated `meth` symbol - * @param tpe: Type of the definition - * @param rhsFn: Function that receives the `meth` symbol and the a list of references to the `params` + * @param owner owner of the generated `meth` symbol + * @param tpe Type of the definition + * @param rhsFn Function that receives the `meth` symbol and the a list of references to the `params` */ def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block } @@ -1764,7 +1764,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Returns a type tree reference to the symbol * - * @param sym The type symbol for which we are creating a type tree reference. + * @param typeSymbol The type symbol for which we are creating a type tree reference. */ def ref(typeSymbol: Symbol): TypeTree } From 5ded04cb95137bea026823e06d4b342d0266542d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 19 Apr 2023 10:27:31 +0200 Subject: [PATCH 384/657] Set reference version to 3.3.0-RC4 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 2689aa65f972..97b4d31f5e02 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -80,7 +80,7 @@ object DottyJSPlugin extends AutoPlugin { object Build { import ScaladocConfigs._ - val referenceVersion = "3.3.0-RC3" + val referenceVersion = "3.3.0-RC4" val baseVersion = "3.3.1-RC1" From 21ddebefcdf96d0d5d8c840f20e1f82fc5a94701 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 19 Apr 2023 14:30:23 +0200 Subject: [PATCH 385/657] Fix name of variable in comment --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index bcd91336f627..7cc8b6fe1c23 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1407,7 +1407,7 @@ object SymDenotations { case Nil => Iterator.empty } - /** The symbol overriding this symbol in given subclass `ofclazz`. + /** The symbol overriding this symbol in given subclass `inClass`. * * @pre `inClass` is a subclass of this symbol's owner */ From 6bf24dfdbc40af63d241b19a14019acae3caaecf Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Wed, 19 Apr 2023 15:50:29 +0200 Subject: [PATCH 386/657] Wunused: Check if symbol exists before isValidMemberDef check closes lampepfl#17309 --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index ce05e6c125de..b3083223c555 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -681,7 +681,7 @@ object CheckUnused: /** A function is overriden. Either has `override flags` or parent has a matching member (type and name) */ private def isOverriden(using Context): Boolean = sym.is(Flags.Override) || - (if sym.exists then sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists) else false) + (sym.exists && sym.owner.thisType.parents.exists(p => sym.matchingMember(p).exists)) end extension @@ -708,7 +708,11 @@ object CheckUnused: extension (memDef: tpd.MemberDef) private def isValidMemberDef(using Context): Boolean = - !memDef.symbol.isUnusedAnnot && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) && !memDef.name.isWildcard && !memDef.symbol.owner.is(Extension) + memDef.symbol.exists + && !memDef.symbol.isUnusedAnnot + && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) + && !memDef.name.isWildcard + && !memDef.symbol.owner.is(ExtensionMethod) private def isValidParam(using Context): Boolean = val sym = memDef.symbol From 5a1d2d3bc7e79148e23148c0b49cb3dedf84daa2 Mon Sep 17 00:00:00 2001 From: Natsu Kagami Date: Wed, 19 Apr 2023 15:38:00 +0200 Subject: [PATCH 387/657] Allow contextual functions with erased parameters to be integrated - also add test from #17147 --- .../dotty/tools/dotc/core/Definitions.scala | 1 - .../transform/ContextFunctionResults.scala | 12 ++++++++--- tests/pos-custom-args/erased/tailrec.scala | 20 +++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 tests/pos-custom-args/erased/tailrec.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 148b314220a8..85aac4ce601c 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1509,7 +1509,6 @@ class Definitions { /** Is an context function class. * - ContextFunctionN for N >= 0 - * - ErasedContextFunctionN for N > 0 */ def isContextFunctionClass(cls: Symbol): Boolean = scalaClassName(cls).isContextFunction diff --git a/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala b/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala index 5863c360e728..b4eb71c541d3 100644 --- a/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala +++ b/compiler/src/dotty/tools/dotc/transform/ContextFunctionResults.scala @@ -20,7 +20,7 @@ object ContextFunctionResults: */ def annotateContextResults(mdef: DefDef)(using Context): Unit = def contextResultCount(rhs: Tree, tp: Type): Int = tp match - case defn.ContextFunctionType(_, resTpe, erasedParams) if !erasedParams.contains(true) /* Only enable for non-erased functions */ => + case defn.ContextFunctionType(_, resTpe, _) => rhs match case closureDef(meth) => 1 + contextResultCount(meth.rhs, resTpe) case _ => 0 @@ -116,8 +116,14 @@ object ContextFunctionResults: atPhase(erasurePhase)(integrateSelect(tree, n)) else tree match case Select(qual, name) => - if name == nme.apply && defn.isContextFunctionClass(tree.symbol.maybeOwner) then - integrateSelect(qual, n + 1) + if name == nme.apply then + qual.tpe match + case defn.ContextFunctionType(_, _, _) => + integrateSelect(qual, n + 1) + case _ if defn.isContextFunctionClass(tree.symbol.maybeOwner) => // for TermRefs + integrateSelect(qual, n + 1) + case _ => + n > 0 && contextResultCount(tree.symbol) >= n else n > 0 && contextResultCount(tree.symbol) >= n case Ident(name) => diff --git a/tests/pos-custom-args/erased/tailrec.scala b/tests/pos-custom-args/erased/tailrec.scala new file mode 100644 index 000000000000..cebcf4785c7a --- /dev/null +++ b/tests/pos-custom-args/erased/tailrec.scala @@ -0,0 +1,20 @@ +import scala.annotation.tailrec + +erased class Foo1 +class Foo2 + +@tailrec +final def test1(n: Int, acc: Int): (Foo1, Foo2) ?=> Int = + if n <= 0 then acc + else test1(n - 1, acc * n) + +@tailrec +final def test2(n: Int, acc: Int): Foo1 ?=> Int = + if n <= 0 then acc + else test2(n - 1, acc * n) + +@main def Test() = + given Foo1 = Foo1() + given Foo2 = Foo2() + test1(10, 0) + test2(10, 0) From 0ba35e69973e7c53c537539c420f63b357a35cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Thu, 20 Apr 2023 15:27:29 +0200 Subject: [PATCH 388/657] Fix #13757: Explicitly disallow higher-kinded scrutinees of match types. --- .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 5 +++++ compiler/src/dotty/tools/dotc/typer/Typer.scala | 5 ++++- tests/neg/i13757-match-type-anykind.scala | 16 ++++++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i13757-match-type-anykind.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 1fe38ce5e801..b3d9b2c1dc49 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -192,6 +192,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case UnusedNonUnitValueID // errorNumber 176 case ConstrProxyShadowsID // errorNumber 177 case MissingArgumentListID // errorNumber: 178 + case MatchTypeScrutineeCannotBeHigherKindedID // errorNumber: 179 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index fba08fd84d0c..1c3bd23de9c6 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2878,3 +2878,8 @@ class UnusedNonUnitValue(tp: Type)(using Context) def kind = MessageKind.PotentialIssue def msg(using Context) = i"unused value of type $tp" def explain(using Context) = "" + +class MatchTypeScrutineeCannotBeHigherKinded(tp: Type)(using Context) + extends TypeMsg(MatchTypeScrutineeCannotBeHigherKindedID) : + def msg(using Context) = i"the scrutinee of a match type cannot be higher-kinded" + def explain(using Context) = "" diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 16b256e69059..bfd85a94cc4b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2214,8 +2214,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (tree.bound.isEmpty && isFullyDefined(pt, ForceDegree.none)) TypeTree(pt) else typed(tree.bound) val sel1 = typed(tree.selector) + val sel1Tpe = sel1.tpe + if sel1Tpe.isLambdaSub then + report.error(MatchTypeScrutineeCannotBeHigherKinded(sel1Tpe), sel1.srcPos) val pt1 = if (bound1.isEmpty) pt else bound1.tpe - val cases1 = tree.cases.mapconserve(typedTypeCase(_, sel1.tpe, pt1)) + val cases1 = tree.cases.mapconserve(typedTypeCase(_, sel1Tpe, pt1)) assignType(cpy.MatchTypeTree(tree)(bound1, sel1, cases1), bound1, sel1, cases1) } diff --git a/tests/neg/i13757-match-type-anykind.scala b/tests/neg/i13757-match-type-anykind.scala new file mode 100644 index 000000000000..d8273e546dab --- /dev/null +++ b/tests/neg/i13757-match-type-anykind.scala @@ -0,0 +1,16 @@ +object Test: + type AnyKindMatchType1[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded // error + case Option[a] => Int + + type AnyKindMatchType2[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded + case Option => Int // error: Missing type parameter for Option + + type AnyKindMatchType3[X <: AnyKind] = X match // error: the scrutinee of a match type cannot be higher-kinded // error + case _ => Int + + type AnyKindMatchType4[X <: Option] = X match // error // error: the scrutinee of a match type cannot be higher-kinded // error + case _ => Int + + type AnyKindMatchType5[X[_]] = X match // error: the scrutinee of a match type cannot be higher-kinded // error + case _ => Int +end Test From 7576fbe34e7b8a26774bdea46cb604f8c0cea1e9 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 22 Apr 2023 13:21:57 +0200 Subject: [PATCH 389/657] Some additions and changes in tests --- tests/neg-custom-args/captures/filevar.scala | 18 +++++++++ tests/pending/pos/i16826.scala | 10 +++++ tests/pos-custom-args/captures/filevar.scala | 37 +++++++++++++++++++ .../captures/nested-classes.scala | 21 +++++++++++ tests/run/errorhandling/Result.scala | 4 +- 5 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 tests/neg-custom-args/captures/filevar.scala create mode 100644 tests/pending/pos/i16826.scala create mode 100644 tests/pos-custom-args/captures/filevar.scala create mode 100644 tests/pos-custom-args/captures/nested-classes.scala diff --git a/tests/neg-custom-args/captures/filevar.scala b/tests/neg-custom-args/captures/filevar.scala new file mode 100644 index 000000000000..9251be8f1592 --- /dev/null +++ b/tests/neg-custom-args/captures/filevar.scala @@ -0,0 +1,18 @@ +import language.experimental.captureChecking +import compiletime.uninitialized + +class File: + def write(x: String): Unit = ??? + +class Service: + var file: {*} File = uninitialized + def log = file.write("log") // error + +def withFile[T](op: (f: {*} File) => T): T = + op(new File) + +def test = + withFile: f => + val o = Service() + o.file = f + o.log diff --git a/tests/pending/pos/i16826.scala b/tests/pending/pos/i16826.scala new file mode 100644 index 000000000000..a938ab42dac3 --- /dev/null +++ b/tests/pending/pos/i16826.scala @@ -0,0 +1,10 @@ +import language.experimental.captureChecking +class A +class B(a: {*} A) +class C(a: {*} A): + def setB(b: {a} B): Unit = ??? + + +def test(a1: {*} A)(b1: {a1} B) = + val c = new C(a1) + c.setB(b1) diff --git a/tests/pos-custom-args/captures/filevar.scala b/tests/pos-custom-args/captures/filevar.scala new file mode 100644 index 000000000000..17a8281c4d29 --- /dev/null +++ b/tests/pos-custom-args/captures/filevar.scala @@ -0,0 +1,37 @@ +import language.experimental.captureChecking +import annotation.capability +import compiletime.uninitialized + +object test1: + class File: + def write(x: String): Unit = ??? + + class Service(f: {*} File): + def log = f.write("log") + + def withFile[T](op: (f: {*} File) => T): T = + op(new File) + + def test = + withFile: f => + val o = Service(f) + o.log + +object test2: + @capability class IO + + class File: + def write(x: String): Unit = ??? + + class Service(io: IO): + var file: {io} File = uninitialized + def log = file.write("log") + + def withFile[T](io: IO)(op: (f: {io} File) => T): T = + op(new File) + + def test(io: IO) = + withFile(io): f => + val o = Service(io) + o.file = f + o.log diff --git a/tests/pos-custom-args/captures/nested-classes.scala b/tests/pos-custom-args/captures/nested-classes.scala new file mode 100644 index 000000000000..2e893b7413a5 --- /dev/null +++ b/tests/pos-custom-args/captures/nested-classes.scala @@ -0,0 +1,21 @@ +import language.experimental.captureChecking +import annotation.{capability, constructorOnly} + +@capability class IO +class Blah +class Pkg(using @constructorOnly io: IO): + class Foo: + def m(foo: {io} Blah) = ??? +class Pkg2(using io: IO): + class Foo: + def m(foo: {io} Blah): Any = io; ??? + +def main(using io: IO) = + val pkg = Pkg() + val f = pkg.Foo() + f.m(???) + val pkg2 = Pkg2() + val f2 = pkg2.Foo() + f2.m(???) + + diff --git a/tests/run/errorhandling/Result.scala b/tests/run/errorhandling/Result.scala index 027c07c86769..07d7a9f90c8a 100644 --- a/tests/run/errorhandling/Result.scala +++ b/tests/run/errorhandling/Result.scala @@ -29,14 +29,14 @@ object Result: case err: Err[_] => err /** Validate both `r` and `other`; return a pair of successes or a list of failures. */ - def * [U](other: Result[U, E]): Result[(T, U), List[E]] = (r, other) match + def zip[U](other: Result[U, E]): Result[(T, U), List[E]] = (r, other) match case (Ok(x), Ok(y)) => Ok((x, y)) case (Ok(_), Err(e)) => Err(e :: Nil) case (Err(e), Ok(_)) => Err(e :: Nil) case (Err(e1), Err(e2)) => Err(e1 :: e2 :: Nil) /** Validate both `r` and `other`; return a tuple of successes or a list of failures. - * Unlike with `*`, the right hand side `other` must be a `Result` returning a `Tuple`, + * Unlike with `zip`, the right hand side `other` must be a `Result` returning a `Tuple`, * and the left hand side is added to it. See `Result.empty` for a convenient * right unit of chains of `*:`s. */ From 4e028bf37e28cd13cf9c1bf96078a14221947fdc Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 22 Apr 2023 13:55:50 +0200 Subject: [PATCH 390/657] Support extension methods imported from different objects Add a special case to name resolution so that when expanding an extension method from `e.m` to `m(e)` and `m` is imported by several imports on the same level, we try to typecheck under every such import and pick the successful alternative if it exists and is unambiguous. Fixes #16920 --- .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 9 ++ .../src/dotty/tools/dotc/typer/Typer.scala | 95 ++++++++++++++++--- .../reference/contextual/extension-methods.md | 11 ++- tests/neg/i13558.check | 26 ----- tests/neg/i16920.check | 88 +++++++++++++++++ tests/neg/i16920.scala | 57 +++++++++++ tests/{neg => pos}/i13558.scala | 4 +- tests/pos/i16920.scala | 76 +++++++++++++++ 9 files changed, 324 insertions(+), 43 deletions(-) delete mode 100644 tests/neg/i13558.check create mode 100644 tests/neg/i16920.check create mode 100644 tests/neg/i16920.scala rename tests/{neg => pos}/i13558.scala (87%) create mode 100644 tests/pos/i16920.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index b3d9b2c1dc49..cc2741bcb614 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -193,6 +193,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case ConstrProxyShadowsID // errorNumber 177 case MissingArgumentListID // errorNumber: 178 case MatchTypeScrutineeCannotBeHigherKindedID // errorNumber: 179 + case AmbiguousExtensionMethodID // errorNumber 180 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 1c3bd23de9c6..423c1cdef264 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -1434,6 +1434,15 @@ extends ReferenceMsg(AmbiguousOverloadID), NoDisambiguation { |""" } +class AmbiguousExtensionMethod(tree: untpd.Tree, expansion1: tpd.Tree, expansion2: tpd.Tree)(using Context) + extends ReferenceMsg(AmbiguousExtensionMethodID), NoDisambiguation: + def msg(using Context) = + i"""Ambiguous extension methods: + |both $expansion1 + |and $expansion2 + |are possible expansions of $tree""" + def explain(using Context) = "" + class ReassignmentToVal(name: Name)(using Context) extends TypeMsg(ReassignmentToValID) { def msg(using Context) = i"""Reassignment to val $name""" diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index bfd85a94cc4b..62edd12a00fa 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -159,8 +159,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * @param required flags the result's symbol must have * @param excluded flags the result's symbol must not have * @param pos indicates position to use for error reporting + * @param altImports a ListBuffer in which alternative imported references are + * collected in case `findRef` is called from an expansion of + * an extension method, i.e. when `e.m` is expanded to `m(e)` and + * a reference for `m` is searched. `null` in all other situations. */ - def findRef(name: Name, pt: Type, required: FlagSet, excluded: FlagSet, pos: SrcPos)(using Context): Type = { + def findRef(name: Name, pt: Type, required: FlagSet, excluded: FlagSet, pos: SrcPos, + altImports: mutable.ListBuffer[TermRef] | Null = null)(using Context): Type = { val refctx = ctx val noImports = ctx.mode.is(Mode.InPackageClauseName) def suppressErrors = excluded.is(ConstructorProxy) @@ -231,15 +236,52 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer fail(AmbiguousReference(name, newPrec, prevPrec, prevCtx)) previous - /** Recurse in outer context. If final result is same as `previous`, check that it - * is new or shadowed. This order of checking is necessary since an - * outer package-level definition might trump two conflicting inner - * imports, so no error should be issued in that case. See i7876.scala. + /** Assemble and check alternatives to an imported reference. This implies: + * - If we expand an extension method (i.e. altImports != null), + * search imports on the same level for other possible resolutions of `name`. + * The result and altImports together then contain all possible imported + * references of the highest possible precedence, where `NamedImport` beats + * `WildImport`. + * - Find a posssibly shadowing reference in an outer context. + * If the result is the same as `previous`, check that it is new or + * shadowed. This order of checking is necessary since an outer package-level + * definition might trump two conflicting inner imports, so no error should be + * issued in that case. See i7876.scala. + * @param previous the previously found reference (which is an import) + * @param prevPrec the precedence of the reference (either NamedImport or WildImport) + * @param prevCtx the context in which the reference was found + * @param using_Context the outer context of `precCtx` */ - def recurAndCheckNewOrShadowed(previous: Type, prevPrec: BindingPrec, prevCtx: Context)(using Context): Type = - val found = findRefRecur(previous, prevPrec, prevCtx) - if found eq previous then checkNewOrShadowed(found, prevPrec)(using prevCtx) - else found + def checkImportAlternatives(previous: Type, prevPrec: BindingPrec, prevCtx: Context)(using Context): Type = + + def addAltImport(altImp: TermRef) = + if !TypeComparer.isSameRef(previous, altImp) + && !altImports.uncheckedNN.exists(TypeComparer.isSameRef(_, altImp)) + then + altImports.uncheckedNN += altImp + + if altImports != null && ctx.isImportContext then + val curImport = ctx.importInfo.uncheckedNN + namedImportRef(curImport) match + case altImp: TermRef => + if prevPrec == WildImport then + // Discard all previously found references and continue with `altImp` + altImports.clear() + checkImportAlternatives(altImp, NamedImport, ctx)(using ctx.outer) + else + addAltImport(altImp) + checkImportAlternatives(previous, prevPrec, prevCtx)(using ctx.outer) + case _ => + if prevPrec == WildImport then + wildImportRef(curImport) match + case altImp: TermRef => addAltImport(altImp) + case _ => + checkImportAlternatives(previous, prevPrec, prevCtx)(using ctx.outer) + else + val found = findRefRecur(previous, prevPrec, prevCtx) + if found eq previous then checkNewOrShadowed(found, prevPrec)(using prevCtx) + else found + end checkImportAlternatives def selection(imp: ImportInfo, name: Name, checkBounds: Boolean): Type = imp.importSym.info match @@ -329,7 +371,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if (ctx.scope eq EmptyScope) previous else { var result: Type = NoType - val curOwner = ctx.owner /** Is curOwner a package object that should be skipped? @@ -450,11 +491,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer else if (isPossibleImport(NamedImport) && (curImport nen outer.importInfo)) { val namedImp = namedImportRef(curImport.uncheckedNN) if (namedImp.exists) - recurAndCheckNewOrShadowed(namedImp, NamedImport, ctx)(using outer) + checkImportAlternatives(namedImp, NamedImport, ctx)(using outer) else if (isPossibleImport(WildImport) && !curImport.nn.importSym.isCompleting) { val wildImp = wildImportRef(curImport.uncheckedNN) if (wildImp.exists) - recurAndCheckNewOrShadowed(wildImp, WildImport, ctx)(using outer) + checkImportAlternatives(wildImp, WildImport, ctx)(using outer) else { updateUnimported() loop(ctx)(using outer) @@ -3412,11 +3453,37 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def selectionProto = SelectionProto(tree.name, mbrProto, compat, privateOK = inSelect) def tryExtension(using Context): Tree = - findRef(tree.name, WildcardType, ExtensionMethod, EmptyFlags, qual.srcPos) match + val altImports = new mutable.ListBuffer[TermRef]() + findRef(tree.name, WildcardType, ExtensionMethod, EmptyFlags, qual.srcPos, altImports) match case ref: TermRef => - extMethodApply(untpd.TypedSplice(tpd.ref(ref).withSpan(tree.nameSpan)), qual, pt) + def tryExtMethod(ref: TermRef)(using Context) = + extMethodApply(untpd.TypedSplice(tpd.ref(ref).withSpan(tree.nameSpan)), qual, pt) + if altImports.isEmpty then + tryExtMethod(ref) + else + // Try all possible imports and collect successes and failures + val successes, failures = new mutable.ListBuffer[(Tree, TyperState)] + for alt <- ref :: altImports.toList do + val nestedCtx = ctx.fresh.setNewTyperState() + val app = tryExtMethod(alt)(using nestedCtx) + (if nestedCtx.reporter.hasErrors then failures else successes) + += ((app, nestedCtx.typerState)) + typr.println(i"multiple extensioin methods, success: ${successes.toList}, failure: ${failures.toList}") + + def pick(alt: (Tree, TyperState)): Tree = + val (app, ts) = alt + ts.commit() + app + + successes.toList match + case Nil => pick(failures.head) + case success :: Nil => pick(success) + case (expansion1, _) :: (expansion2, _) :: _ => + report.error(AmbiguousExtensionMethod(tree, expansion1, expansion2), tree.srcPos) + expansion1 case _ => EmptyTree + end tryExtension def nestedFailure(ex: TypeError) = rememberSearchFailure(qual, diff --git a/docs/_docs/reference/contextual/extension-methods.md b/docs/_docs/reference/contextual/extension-methods.md index 6a1504c25048..f4e75c4456df 100644 --- a/docs/_docs/reference/contextual/extension-methods.md +++ b/docs/_docs/reference/contextual/extension-methods.md @@ -244,7 +244,16 @@ The precise rules for resolving a selection to an extension method are as follow Assume a selection `e.m[Ts]` where `m` is not a member of `e`, where the type arguments `[Ts]` are optional, and where `T` is the expected type. The following two rewritings are tried in order: - 1. The selection is rewritten to `m[Ts](e)`. + 1. The selection is rewritten to `m[Ts](e)` and typechecked, using the following + slight modification of the name resolution rules: + + - If `m` is imported by several imports which are all on the nesting level, + try each import as an extension method instead of failing with an ambiguity. + If only one import leads to an expansion that typechecks without errors, pick + that expansion. If there are several such imports, but only one import which is + not a wildcard import, pick the expansion from that import. Otherwise, report + an ambiguous reference error. + 2. If the first rewriting does not typecheck with expected type `T`, and there is an extension method `m` in some eligible object `o`, the selection is rewritten to `o.m[Ts](e)`. An object `o` is _eligible_ if diff --git a/tests/neg/i13558.check b/tests/neg/i13558.check deleted file mode 100644 index 7b4b5215a0a3..000000000000 --- a/tests/neg/i13558.check +++ /dev/null @@ -1,26 +0,0 @@ --- [E008] Not Found Error: tests/neg/i13558.scala:23:14 ---------------------------------------------------------------- -23 | println(a.id) // error - | ^^^^ - | value id is not a member of testcode.A. - | An extension method was tried, but could not be fully constructed: - | - | testcode.ExtensionA.id(a) - | - | failed with: - | - | Reference to id is ambiguous. - | It is both imported by import testcode.ExtensionB._ - | and imported subsequently by import testcode.ExtensionA._ --- [E008] Not Found Error: tests/neg/i13558.scala:29:14 ---------------------------------------------------------------- -29 | println(a.id) // error - | ^^^^ - | value id is not a member of testcode.A. - | An extension method was tried, but could not be fully constructed: - | - | testcode.ExtensionB.id(a) - | - | failed with: - | - | Reference to id is ambiguous. - | It is both imported by import testcode.ExtensionA._ - | and imported subsequently by import testcode.ExtensionB._ diff --git a/tests/neg/i16920.check b/tests/neg/i16920.check new file mode 100644 index 000000000000..86496f24ca3c --- /dev/null +++ b/tests/neg/i16920.check @@ -0,0 +1,88 @@ +-- [E008] Not Found Error: tests/neg/i16920.scala:18:11 ---------------------------------------------------------------- +18 | "five".wow // error + | ^^^^^^^^^^ + | value wow is not a member of String. + | An extension method was tried, but could not be fully constructed: + | + | Two.wow("five") + | + | failed with: + | + | Found: ("five" : String) + | Required: Int +-- [E008] Not Found Error: tests/neg/i16920.scala:26:6 ----------------------------------------------------------------- +26 | 5.wow // error + | ^^^^^ + | value wow is not a member of Int. + | An extension method was tried, but could not be fully constructed: + | + | AlsoFails.wow(5) + | + | failed with: + | + | Found: (5 : Int) + | Required: Boolean +-- [E008] Not Found Error: tests/neg/i16920.scala:27:11 ---------------------------------------------------------------- +27 | "five".wow // error + | ^^^^^^^^^^ + | value wow is not a member of String. + | An extension method was tried, but could not be fully constructed: + | + | AlsoFails.wow("five") + | + | failed with: + | + | Found: ("five" : String) + | Required: Boolean +-- [E008] Not Found Error: tests/neg/i16920.scala:34:6 ----------------------------------------------------------------- +34 | 5.wow // error + | ^^^^^ + | value wow is not a member of Int. + | An extension method was tried, but could not be fully constructed: + | + | Three.wow(5) + | + | failed with: + | + | Ambiguous extension methods: + | both Three.wow(5) + | and Two.wow(5) + | are possible expansions of 5.wow +-- [E008] Not Found Error: tests/neg/i16920.scala:42:11 ---------------------------------------------------------------- +42 | "five".wow // error + | ^^^^^^^^^^ + | value wow is not a member of String. + | An extension method was tried, but could not be fully constructed: + | + | Two.wow("five") + | + | failed with: + | + | Found: ("five" : String) + | Required: Int +-- [E008] Not Found Error: tests/neg/i16920.scala:49:11 ---------------------------------------------------------------- +49 | "five".wow // error + | ^^^^^^^^^^ + | value wow is not a member of String. + | An extension method was tried, but could not be fully constructed: + | + | Two.wow("five") + | + | failed with: + | + | Found: ("five" : String) + | Required: Int +-- [E008] Not Found Error: tests/neg/i16920.scala:56:6 ----------------------------------------------------------------- +56 | 5.wow // error + | ^^^^^ + | value wow is not a member of Int. + | An extension method was tried, but could not be fully constructed: + | + | Three.wow(5) + | + | failed with: + | + | Ambiguous extension methods: + | both Three.wow(5) + | and Two.wow(5) + | are possible expansions of 5.wow diff --git a/tests/neg/i16920.scala b/tests/neg/i16920.scala new file mode 100644 index 000000000000..76953f015de2 --- /dev/null +++ b/tests/neg/i16920.scala @@ -0,0 +1,57 @@ +object One: + extension (s: String) + def wow: Unit = println(s) + +object Two: + extension (i: Int) + def wow: Unit = println(i) + +object Three: + extension (i: Int) + def wow: Unit = println(i) + +object Fails: + import One._ + def test: Unit = + import Two._ + 5.wow + "five".wow // error + +object AlsoFails: + extension (s: Boolean) + def wow = println(s) + import One._ + import Two._ + def test: Unit = + 5.wow // error + "five".wow // error + +object Fails2: + import One._ + import Two._ + import Three._ + def test: Unit = + 5.wow // error + "five".wow // ok + +object Fails3: + import One._ + import Two.wow + def test: Unit = + 5.wow // ok + "five".wow // error + +object Fails4: + import Two.wow + import One._ + def test: Unit = + 5.wow // ok + "five".wow // error + +object Fails5: + import One.wow + import Two.wow + import Three.wow + def test: Unit = + 5.wow // error + "five".wow // ok \ No newline at end of file diff --git a/tests/neg/i13558.scala b/tests/pos/i13558.scala similarity index 87% rename from tests/neg/i13558.scala rename to tests/pos/i13558.scala index 1d4e1c506e43..6f18b770f467 100644 --- a/tests/neg/i13558.scala +++ b/tests/pos/i13558.scala @@ -20,12 +20,12 @@ object Main { import ExtensionB._ import ExtensionA._ val a = A() - println(a.id) // error + println(a.id) // now ok } def main2(args: Array[String]): Unit = { import ExtensionA._ import ExtensionB._ val a = A() - println(a.id) // error + println(a.id) // now ok } } \ No newline at end of file diff --git a/tests/pos/i16920.scala b/tests/pos/i16920.scala new file mode 100644 index 000000000000..b0604b7c9dba --- /dev/null +++ b/tests/pos/i16920.scala @@ -0,0 +1,76 @@ +object One: + extension (s: String) + def wow: Unit = println(s) + +object Two: + extension (i: Int) + def wow: Unit = println(i) + +object Three: + extension (s: String) + def wow: Unit = println(s) + extension (i: Int) + def wow: Unit = println(i) + +object Four: + implicit class WowString(s: String): + def wow: Unit = println(s) + +object Five: + implicit class WowInt(i: Int): + def wow: Unit = println(i) + +object Compiles: + import Three._ + def test: Unit = + 5.wow + "five".wow + +object AlsoCompiles: + import Four._ + import Five._ + def test: Unit = + 5.wow + "five".wow + +object UsedToFail: + import One._ + import Compiles.* + import Two._ + def test: Unit = + 5.wow + "five".wow + +object Conflicting: + extension (i: Int) + def wow: Unit = println(i) + +object Named: + import One.wow + import Two.wow + import Conflicting._ + def test: Unit = + 5.wow // ok + "five".wow // ok + +object Named2: + import Conflicting._ + import One.wow + import Two.wow + def test: Unit = + 5.wow // ok + "five".wow // ok + +val Alias = Two + +object Named3: + import Alias._ + import Two._ + def test: Unit = + 5.wow // ok + +object Named4: + import Two._ + import Alias._ + def test: Unit = + 5.wow // ok From abbd01c27e96b4cb19b2960c48651ec4242a9ed4 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 7 Mar 2023 11:38:02 +0100 Subject: [PATCH 391/657] Put changes under experimental language import As long as this is not SIP approved, we need to put this under experimental. --- .../src/dotty/tools/dotc/config/Feature.scala | 1 + .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- .../reference/contextual/extension-methods.md | 4 ++- .../runtime/stdLibPatches/language.scala | 8 +++++ tests/neg/i13558.scala | 31 +++++++++++++++++++ tests/neg/i16920.check | 28 ++++++++--------- tests/neg/i16920.scala | 2 ++ tests/pos/i13558.scala | 1 + tests/pos/i16920.scala | 2 ++ 9 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 tests/neg/i13558.scala diff --git a/compiler/src/dotty/tools/dotc/config/Feature.scala b/compiler/src/dotty/tools/dotc/config/Feature.scala index 419ed5868cbf..e5ab8f65f55b 100644 --- a/compiler/src/dotty/tools/dotc/config/Feature.scala +++ b/compiler/src/dotty/tools/dotc/config/Feature.scala @@ -29,6 +29,7 @@ object Feature: val fewerBraces = experimental("fewerBraces") val saferExceptions = experimental("saferExceptions") val clauseInterleaving = experimental("clauseInterleaving") + val relaxedExtensionImports = experimental("relaxedExtensionImports") val pureFunctions = experimental("pureFunctions") val captureChecking = experimental("captureChecking") val into = experimental("into") diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 62edd12a00fa..4bc012b5b226 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -260,7 +260,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer then altImports.uncheckedNN += altImp - if altImports != null && ctx.isImportContext then + if Feature.enabled(Feature.relaxedExtensionImports) && altImports != null && ctx.isImportContext then val curImport = ctx.importInfo.uncheckedNN namedImportRef(curImport) match case altImp: TermRef => diff --git a/docs/_docs/reference/contextual/extension-methods.md b/docs/_docs/reference/contextual/extension-methods.md index f4e75c4456df..d98d80caafc5 100644 --- a/docs/_docs/reference/contextual/extension-methods.md +++ b/docs/_docs/reference/contextual/extension-methods.md @@ -252,7 +252,9 @@ The following two rewritings are tried in order: If only one import leads to an expansion that typechecks without errors, pick that expansion. If there are several such imports, but only one import which is not a wildcard import, pick the expansion from that import. Otherwise, report - an ambiguous reference error. + an ambiguous reference error. + + **Note**: This relaxation is currently enabled only under the `experimental.relaxedExtensionImports` language import. 2. If the first rewriting does not typecheck with expected type `T`, and there is an extension method `m` in some eligible object `o`, the selection is rewritten to `o.m[Ts](e)`. An object `o` is _eligible_ if diff --git a/library/src/scala/runtime/stdLibPatches/language.scala b/library/src/scala/runtime/stdLibPatches/language.scala index d92495c6f5aa..091e75fa06e1 100644 --- a/library/src/scala/runtime/stdLibPatches/language.scala +++ b/library/src/scala/runtime/stdLibPatches/language.scala @@ -69,6 +69,14 @@ object language: @compileTimeOnly("`clauseInterleaving` can only be used at compile time in import statements") object clauseInterleaving + /** Adds support for relaxed imports of extension methods. + * Extension methods with the same name can be imported from several places. + * + * @see [[http://dotty.epfl.ch/docs/reference/contextual/extension-methods]] + */ + @compileTimeOnly("`relaxedExtensionImports` can only be used at compile time in import statements") + object relaxedExtensionImports + /** Experimental support for pure function type syntax * * @see [[https://dotty.epfl.ch/docs/reference/experimental/purefuns]] diff --git a/tests/neg/i13558.scala b/tests/neg/i13558.scala new file mode 100644 index 000000000000..1d4e1c506e43 --- /dev/null +++ b/tests/neg/i13558.scala @@ -0,0 +1,31 @@ +package testcode + +class A + +class B + +object ExtensionA { + extension (self: A) { + def id = "A" + } +} +object ExtensionB { + extension (self: B) { + def id = "B" + } +} + +object Main { + def main1(args: Array[String]): Unit = { + import ExtensionB._ + import ExtensionA._ + val a = A() + println(a.id) // error + } + def main2(args: Array[String]): Unit = { + import ExtensionA._ + import ExtensionB._ + val a = A() + println(a.id) // error + } +} \ No newline at end of file diff --git a/tests/neg/i16920.check b/tests/neg/i16920.check index 86496f24ca3c..131ba4c6265e 100644 --- a/tests/neg/i16920.check +++ b/tests/neg/i16920.check @@ -1,5 +1,5 @@ --- [E008] Not Found Error: tests/neg/i16920.scala:18:11 ---------------------------------------------------------------- -18 | "five".wow // error +-- [E008] Not Found Error: tests/neg/i16920.scala:20:11 ---------------------------------------------------------------- +20 | "five".wow // error | ^^^^^^^^^^ | value wow is not a member of String. | An extension method was tried, but could not be fully constructed: @@ -10,8 +10,8 @@ | | Found: ("five" : String) | Required: Int --- [E008] Not Found Error: tests/neg/i16920.scala:26:6 ----------------------------------------------------------------- -26 | 5.wow // error +-- [E008] Not Found Error: tests/neg/i16920.scala:28:6 ----------------------------------------------------------------- +28 | 5.wow // error | ^^^^^ | value wow is not a member of Int. | An extension method was tried, but could not be fully constructed: @@ -22,8 +22,8 @@ | | Found: (5 : Int) | Required: Boolean --- [E008] Not Found Error: tests/neg/i16920.scala:27:11 ---------------------------------------------------------------- -27 | "five".wow // error +-- [E008] Not Found Error: tests/neg/i16920.scala:29:11 ---------------------------------------------------------------- +29 | "five".wow // error | ^^^^^^^^^^ | value wow is not a member of String. | An extension method was tried, but could not be fully constructed: @@ -34,8 +34,8 @@ | | Found: ("five" : String) | Required: Boolean --- [E008] Not Found Error: tests/neg/i16920.scala:34:6 ----------------------------------------------------------------- -34 | 5.wow // error +-- [E008] Not Found Error: tests/neg/i16920.scala:36:6 ----------------------------------------------------------------- +36 | 5.wow // error | ^^^^^ | value wow is not a member of Int. | An extension method was tried, but could not be fully constructed: @@ -48,8 +48,8 @@ | both Three.wow(5) | and Two.wow(5) | are possible expansions of 5.wow --- [E008] Not Found Error: tests/neg/i16920.scala:42:11 ---------------------------------------------------------------- -42 | "five".wow // error +-- [E008] Not Found Error: tests/neg/i16920.scala:44:11 ---------------------------------------------------------------- +44 | "five".wow // error | ^^^^^^^^^^ | value wow is not a member of String. | An extension method was tried, but could not be fully constructed: @@ -60,8 +60,8 @@ | | Found: ("five" : String) | Required: Int --- [E008] Not Found Error: tests/neg/i16920.scala:49:11 ---------------------------------------------------------------- -49 | "five".wow // error +-- [E008] Not Found Error: tests/neg/i16920.scala:51:11 ---------------------------------------------------------------- +51 | "five".wow // error | ^^^^^^^^^^ | value wow is not a member of String. | An extension method was tried, but could not be fully constructed: @@ -72,8 +72,8 @@ | | Found: ("five" : String) | Required: Int --- [E008] Not Found Error: tests/neg/i16920.scala:56:6 ----------------------------------------------------------------- -56 | 5.wow // error +-- [E008] Not Found Error: tests/neg/i16920.scala:58:6 ----------------------------------------------------------------- +58 | 5.wow // error | ^^^^^ | value wow is not a member of Int. | An extension method was tried, but could not be fully constructed: diff --git a/tests/neg/i16920.scala b/tests/neg/i16920.scala index 76953f015de2..38345e811c1f 100644 --- a/tests/neg/i16920.scala +++ b/tests/neg/i16920.scala @@ -1,3 +1,5 @@ +import language.experimental.relaxedExtensionImports + object One: extension (s: String) def wow: Unit = println(s) diff --git a/tests/pos/i13558.scala b/tests/pos/i13558.scala index 6f18b770f467..0c8be379f6a9 100644 --- a/tests/pos/i13558.scala +++ b/tests/pos/i13558.scala @@ -1,4 +1,5 @@ package testcode +import language.experimental.relaxedExtensionImports class A diff --git a/tests/pos/i16920.scala b/tests/pos/i16920.scala index b0604b7c9dba..dd4f5804a4fd 100644 --- a/tests/pos/i16920.scala +++ b/tests/pos/i16920.scala @@ -1,3 +1,5 @@ +import language.experimental.relaxedExtensionImports + object One: extension (s: String) def wow: Unit = println(s) From 81e9e1b09b44fd338270dd7c1ec08f567847de40 Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 7 Mar 2023 13:28:19 +0100 Subject: [PATCH 392/657] Update MiMaFilters --- project/MiMaFilters.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index cb15d82affb8..d53eeb7077a4 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -26,6 +26,8 @@ object MiMaFilters { ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$clauseInterleaving$"), ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.into"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$into$"), + ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.relaxedExtensionImports"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$relaxedExtensionImports$"), // end of New experimental features in 3.3.X // Added java.io.Serializable as LazyValControlState supertype From d1d6c2e9536ce331316f741b9c1c4c0520cef787 Mon Sep 17 00:00:00 2001 From: Decel <8268812+Decel@users.noreply.github.com> Date: Mon, 24 Apr 2023 12:18:17 +0200 Subject: [PATCH 393/657] Fix Tests and add bounds check for inlined functions --- .../src/dotty/tools/dotc/transform/PostTyper.scala | 1 + tests/neg/i17168.scala | 3 +++ tests/pos/i13332super.scala | 14 -------------- tests/run/anon-mirror-gen-local.scala | 6 +++--- tests/run/i13332a.scala | 2 +- 5 files changed, 8 insertions(+), 18 deletions(-) create mode 100644 tests/neg/i17168.scala delete mode 100644 tests/pos/i13332super.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 7f3e47c14732..597b5aa5885f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -363,6 +363,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case Inlined(call, bindings, expansion) if !call.isEmpty => val pos = call.sourcePos CrossVersionChecks.checkExperimentalRef(call.symbol, pos) + super.transform(call) val callTrace = Inlines.inlineCallTrace(call.symbol, pos)(using ctx.withSource(pos.source)) cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(using inlineContext(call))) case templ: Template => diff --git a/tests/neg/i17168.scala b/tests/neg/i17168.scala new file mode 100644 index 000000000000..6e82cc15abfc --- /dev/null +++ b/tests/neg/i17168.scala @@ -0,0 +1,3 @@ +type F[X <: String] = X + +val a = summon[F[Int] =:= Int] // error \ No newline at end of file diff --git a/tests/pos/i13332super.scala b/tests/pos/i13332super.scala deleted file mode 100644 index 1ab695d5d4a5..000000000000 --- a/tests/pos/i13332super.scala +++ /dev/null @@ -1,14 +0,0 @@ -import scala.deriving.Mirror - -trait MixinAMini { - lazy val mixinB = new MixinBMini() {} -} -trait MixinBMini { - sealed trait Lst // crucially, no companion is defined - case class Cn(h: Int, t: Lst) extends Lst - case object Nl extends Lst -} -trait SubABMini extends MixinAMini with MixinBMini { - val mirror_SubABMini_super_mixinB_Lst = - summon[Mirror.Of[SubABMini.super[MixinAMini].mixinB.Lst]] -} diff --git a/tests/run/anon-mirror-gen-local.scala b/tests/run/anon-mirror-gen-local.scala index 68fb9500d5ba..ccd1ac4fc602 100644 --- a/tests/run/anon-mirror-gen-local.scala +++ b/tests/run/anon-mirror-gen-local.scala @@ -55,7 +55,7 @@ class Outer5 { self => } } - lazy val o = new Outer5() // infinite init + final lazy val o = new Outer5() // infinite init } @@ -142,7 +142,7 @@ def locally3 = { class Bar extends Foo { def hello = - val mQux = summon[Mirror.Of[Bar.super.foo.type]] + val mQux = summon[Mirror.Of[foo.type]] assert(mQux.fromProduct(EmptyTuple) == Qux) } @@ -157,4 +157,4 @@ def locally3 = { testOuter6 locally1 locally2 - locally3 \ No newline at end of file + locally3 diff --git a/tests/run/i13332a.scala b/tests/run/i13332a.scala index 3478ed325467..c4bd33ede153 100644 --- a/tests/run/i13332a.scala +++ b/tests/run/i13332a.scala @@ -150,7 +150,7 @@ class SubUniverse extends Universe { trait Whole { trait MixinA { - lazy val mixinB = new MixinB() {} + final lazy val mixinB = new MixinB() {} } trait MixinB { object A extends MixinB { // by inheriting `MixinB`, we should not check for inheritance from the right From d26d076513b7240b4ae4c6393147603d95669ac6 Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 14 Apr 2023 11:46:41 +0200 Subject: [PATCH 394/657] Don't render the "$" for module Change of how to correct the Issue - Add a new method "normalizedFullName" - Call the method "ClassLikeSupport" Add the ownerNameChain function - To erase the $ in all the hierarchy --- .../tools/scaladoc/tasty/ClassLikeSupport.scala | 2 +- .../dotty/tools/scaladoc/tasty/NameNormalizer.scala | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 0c85dff0879f..86c745f12db7 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -525,7 +525,7 @@ trait ClassLikeSupport: experimental: Option[Annotation] = None ) = Member( name = symbol.normalizedName, - fullName = symbol.fullName, + fullName = symbol.normalizedFullName, dri = symbol.dri, kind = kind, visibility = symbol.getVisibility(), diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/NameNormalizer.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/NameNormalizer.scala index 687ad6ecbf44..196c3e056b36 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/NameNormalizer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/NameNormalizer.scala @@ -17,6 +17,18 @@ object NameNormalizer { val escaped = escapedName(constructorNormalizedName) escaped } + + def ownerNameChain: List[String] = { + import reflect.* + if s.isNoSymbol then List.empty + else if s == defn.EmptyPackageClass then List.empty + else if s == defn.RootPackage then List.empty + else if s == defn.RootClass then List.empty + else s.owner.ownerNameChain :+ s.normalizedName + } + + def normalizedFullName: String = + s.ownerNameChain.mkString(".") private val ignoredKeywords: Set[String] = Set("this") From 06226b11d4b81ad3398a46f656e9670d6ee21232 Mon Sep 17 00:00:00 2001 From: Rikito Taniguchi Date: Fri, 16 Sep 2022 14:23:55 +0200 Subject: [PATCH 395/657] feat: [SemanticDB] support LambdaType (convert from HKTypeLambda) https://github.com/scalameta/scalameta/pull/2867 This commit adds support for LambdaType that is converted from HKTypeLambda in Scala3 --- .../dotty/tools/dotc/semanticdb/PPrint.scala | 4 + .../dotty/tools/dotc/semanticdb/TypeOps.scala | 20 ++- .../dotc/semanticdb/generated/Type.scala | 130 ++++++++++++++++++ tests/semanticdb/expect/hk.expect.scala | 17 +++ tests/semanticdb/expect/hk.scala | 17 +++ tests/semanticdb/metac.expect | 106 +++++++++++++- 6 files changed, 287 insertions(+), 7 deletions(-) create mode 100644 tests/semanticdb/expect/hk.expect.scala create mode 100644 tests/semanticdb/expect/hk.scala diff --git a/compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala b/compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala index 6814d923a062..b53ee787f501 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/PPrint.scala @@ -196,6 +196,10 @@ class SymbolInformationPrinter (symtab: PrinterSymtab): s"${pprint(caseType.key)} => ${pprint(caseType.body)}" }.mkString(", ") s"${pprint(scrutinee)} match { ${casesStr} }" + case LambdaType(tparams, res) => + val params = tparams.infos.map(_.displayName).mkString("[", ", ", "]") + val resType = normal(res) + s"$params =>> $resType" case x => "" diff --git a/compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala b/compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala index 0ccaab48889a..b0d032c7d83b 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/TypeOps.scala @@ -483,9 +483,23 @@ class TypeOps: case NoPrefix => s.Type.Empty - // Not yet supported - case _: HKTypeLambda => - s.Type.Empty + case lambda: HKTypeLambda => + val paramSyms: List[SemanticSymbol] = lambda.paramNames.zip(lambda.paramInfos).map { (paramName, bounds) => + // def x[T[_]] = ??? + if paramName.isWildcard then + WildcardTypeSymbol(sym, bounds).tap(registerFakeSymbol) + else + paramRefSymtab.lookup(lambda, paramName).getOrElse { + TypeParamRefSymbol(sym, paramName, bounds).tap(registerFakeSymbol) + } + } + val parameters = + paramSyms.sscopeOpt(using LinkMode.HardlinkChildren) + val resType = loop(lambda.resType) + s.LambdaType( + parameters, + resType + ) case tvar: TypeVar => loop(tvar.stripped) diff --git a/compiler/src/dotty/tools/dotc/semanticdb/generated/Type.scala b/compiler/src/dotty/tools/dotc/semanticdb/generated/Type.scala index da24b4847e19..be9cc6034f2c 100644 --- a/compiler/src/dotty/tools/dotc/semanticdb/generated/Type.scala +++ b/compiler/src/dotty/tools/dotc/semanticdb/generated/Type.scala @@ -39,6 +39,7 @@ object Type { case __v: dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.ByNameType => __v.value case __v: dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.RepeatedType => __v.value case __v: dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.MatchType => __v.value + case __v: dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.LambdaType => __v.value case dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.Empty => Empty } override def toBase(__custom: dotty.tools.dotc.semanticdb.Type): dotty.tools.dotc.semanticdb.TypeMessage = dotty.tools.dotc.semanticdb.TypeMessage(__custom match { @@ -57,6 +58,7 @@ object Type { case __v: dotty.tools.dotc.semanticdb.ByNameType => dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.ByNameType(__v) case __v: dotty.tools.dotc.semanticdb.RepeatedType => dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.RepeatedType(__v) case __v: dotty.tools.dotc.semanticdb.MatchType => dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.MatchType(__v) + case __v: dotty.tools.dotc.semanticdb.LambdaType => dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.LambdaType(__v) case Empty => dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.Empty }) } @@ -129,6 +131,10 @@ final case class TypeMessage( val __value = sealedValue.matchType.get __size += 2 + SemanticdbOutputStream.computeUInt32SizeNoTag(__value.serializedSize) + __value.serializedSize }; + if (sealedValue.lambdaType.isDefined) { + val __value = sealedValue.lambdaType.get + __size += 2 + SemanticdbOutputStream.computeUInt32SizeNoTag(__value.serializedSize) + __value.serializedSize + }; __size } override def serializedSize: _root_.scala.Int = { @@ -231,6 +237,12 @@ final case class TypeMessage( _output__.writeUInt32NoTag(__m.serializedSize) __m.writeTo(_output__) }; + sealedValue.lambdaType.foreach { __v => + val __m = __v + _output__.writeTag(26, 2) + _output__.writeUInt32NoTag(__m.serializedSize) + __m.writeTo(_output__) + }; } def getTypeRef: dotty.tools.dotc.semanticdb.TypeRef = sealedValue.typeRef.getOrElse(dotty.tools.dotc.semanticdb.TypeRef.defaultInstance) def withTypeRef(__v: dotty.tools.dotc.semanticdb.TypeRef): TypeMessage = copy(sealedValue = dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.TypeRef(__v)) @@ -262,6 +274,8 @@ final case class TypeMessage( def withRepeatedType(__v: dotty.tools.dotc.semanticdb.RepeatedType): TypeMessage = copy(sealedValue = dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.RepeatedType(__v)) def getMatchType: dotty.tools.dotc.semanticdb.MatchType = sealedValue.matchType.getOrElse(dotty.tools.dotc.semanticdb.MatchType.defaultInstance) def withMatchType(__v: dotty.tools.dotc.semanticdb.MatchType): TypeMessage = copy(sealedValue = dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.MatchType(__v)) + def getLambdaType: dotty.tools.dotc.semanticdb.LambdaType = sealedValue.lambdaType.getOrElse(dotty.tools.dotc.semanticdb.LambdaType.defaultInstance) + def withLambdaType(__v: dotty.tools.dotc.semanticdb.LambdaType): TypeMessage = copy(sealedValue = dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.LambdaType(__v)) def clearSealedValue: TypeMessage = copy(sealedValue = dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.Empty) def withSealedValue(__v: dotty.tools.dotc.semanticdb.TypeMessage.SealedValue): TypeMessage = copy(sealedValue = __v) @@ -311,6 +325,8 @@ object TypeMessage extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc __sealedValue = dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.RepeatedType(__sealedValue.repeatedType.fold(LiteParser.readMessage[dotty.tools.dotc.semanticdb.RepeatedType](_input__))(LiteParser.readMessage(_input__, _))) case 202 => __sealedValue = dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.MatchType(__sealedValue.matchType.fold(LiteParser.readMessage[dotty.tools.dotc.semanticdb.MatchType](_input__))(LiteParser.readMessage(_input__, _))) + case 210 => + __sealedValue = dotty.tools.dotc.semanticdb.TypeMessage.SealedValue.LambdaType(__sealedValue.lambdaType.fold(LiteParser.readMessage[dotty.tools.dotc.semanticdb.LambdaType](_input__))(LiteParser.readMessage(_input__, _))) case tag => _input__.skipField(tag) } } @@ -345,6 +361,7 @@ object TypeMessage extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc def isByNameType: _root_.scala.Boolean = false def isRepeatedType: _root_.scala.Boolean = false def isMatchType: _root_.scala.Boolean = false + def isLambdaType: _root_.scala.Boolean = false def typeRef: _root_.scala.Option[dotty.tools.dotc.semanticdb.TypeRef] = _root_.scala.None def singleType: _root_.scala.Option[dotty.tools.dotc.semanticdb.SingleType] = _root_.scala.None def thisType: _root_.scala.Option[dotty.tools.dotc.semanticdb.ThisType] = _root_.scala.None @@ -360,6 +377,7 @@ object TypeMessage extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc def byNameType: _root_.scala.Option[dotty.tools.dotc.semanticdb.ByNameType] = _root_.scala.None def repeatedType: _root_.scala.Option[dotty.tools.dotc.semanticdb.RepeatedType] = _root_.scala.None def matchType: _root_.scala.Option[dotty.tools.dotc.semanticdb.MatchType] = _root_.scala.None + def lambdaType: _root_.scala.Option[dotty.tools.dotc.semanticdb.LambdaType] = _root_.scala.None } object SealedValue { @SerialVersionUID(0L) @@ -476,6 +494,13 @@ object TypeMessage extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc override def matchType: _root_.scala.Option[dotty.tools.dotc.semanticdb.MatchType] = Some(value) override def number: _root_.scala.Int = 25 } + @SerialVersionUID(0L) + final case class LambdaType(value: dotty.tools.dotc.semanticdb.LambdaType) extends dotty.tools.dotc.semanticdb.TypeMessage.SealedValue derives CanEqual { + type ValueType = dotty.tools.dotc.semanticdb.LambdaType + override def isLambdaType: _root_.scala.Boolean = true + override def lambdaType: _root_.scala.Option[dotty.tools.dotc.semanticdb.LambdaType] = Some(value) + override def number: _root_.scala.Int = 26 + } } final val TYPE_REF_FIELD_NUMBER = 2 final val SINGLE_TYPE_FIELD_NUMBER = 20 @@ -492,6 +517,7 @@ object TypeMessage extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc final val BY_NAME_TYPE_FIELD_NUMBER = 13 final val REPEATED_TYPE_FIELD_NUMBER = 14 final val MATCH_TYPE_FIELD_NUMBER = 25 + final val LAMBDA_TYPE_FIELD_NUMBER = 26 def of( sealedValue: dotty.tools.dotc.semanticdb.TypeMessage.SealedValue ): _root_.dotty.tools.dotc.semanticdb.TypeMessage = _root_.dotty.tools.dotc.semanticdb.TypeMessage( @@ -2034,3 +2060,107 @@ object MatchType extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc.s ) // @@protoc_insertion_point(GeneratedMessageCompanion[dotty.tools.dotc.semanticdb.MatchType]) } + +@SerialVersionUID(0L) +final case class LambdaType( + parameters: _root_.scala.Option[dotty.tools.dotc.semanticdb.Scope] = _root_.scala.None, + returnType: dotty.tools.dotc.semanticdb.Type = dotty.tools.dotc.semanticdb.LambdaType._typemapper_returnType.toCustom(dotty.tools.dotc.semanticdb.TypeMessage.defaultInstance) + ) extends dotty.tools.dotc.semanticdb.Type.NonEmpty with SemanticdbGeneratedMessage derives CanEqual { + @transient @sharable + private[this] var __serializedSizeMemoized: _root_.scala.Int = 0 + private[this] def __computeSerializedSize(): _root_.scala.Int = { + var __size = 0 + if (parameters.isDefined) { + val __value = parameters.get + __size += 1 + SemanticdbOutputStream.computeUInt32SizeNoTag(__value.serializedSize) + __value.serializedSize + }; + + { + val __value = dotty.tools.dotc.semanticdb.LambdaType._typemapper_returnType.toBase(returnType) + if (__value.serializedSize != 0) { + __size += 1 + SemanticdbOutputStream.computeUInt32SizeNoTag(__value.serializedSize) + __value.serializedSize + } + }; + __size + } + override def serializedSize: _root_.scala.Int = { + var __size = __serializedSizeMemoized + if (__size == 0) { + __size = __computeSerializedSize() + 1 + __serializedSizeMemoized = __size + } + __size - 1 + + } + def writeTo(`_output__`: SemanticdbOutputStream): _root_.scala.Unit = { + parameters.foreach { __v => + val __m = __v + _output__.writeTag(1, 2) + _output__.writeUInt32NoTag(__m.serializedSize) + __m.writeTo(_output__) + }; + { + val __v = dotty.tools.dotc.semanticdb.LambdaType._typemapper_returnType.toBase(returnType) + if (__v.serializedSize != 0) { + _output__.writeTag(2, 2) + _output__.writeUInt32NoTag(__v.serializedSize) + __v.writeTo(_output__) + } + }; + } + def getParameters: dotty.tools.dotc.semanticdb.Scope = parameters.getOrElse(dotty.tools.dotc.semanticdb.Scope.defaultInstance) + def clearParameters: LambdaType = copy(parameters = _root_.scala.None) + def withParameters(__v: dotty.tools.dotc.semanticdb.Scope): LambdaType = copy(parameters = Option(__v)) + def withReturnType(__v: dotty.tools.dotc.semanticdb.Type): LambdaType = copy(returnType = __v) + + + + + // @@protoc_insertion_point(GeneratedMessage[dotty.tools.dotc.semanticdb.LambdaType]) +} + +object LambdaType extends SemanticdbGeneratedMessageCompanion[dotty.tools.dotc.semanticdb.LambdaType] { + implicit def messageCompanion: SemanticdbGeneratedMessageCompanion[dotty.tools.dotc.semanticdb.LambdaType] = this + def parseFrom(`_input__`: SemanticdbInputStream): dotty.tools.dotc.semanticdb.LambdaType = { + var __parameters: _root_.scala.Option[dotty.tools.dotc.semanticdb.Scope] = _root_.scala.None + var __returnType: _root_.scala.Option[dotty.tools.dotc.semanticdb.TypeMessage] = _root_.scala.None + var _done__ = false + while (!_done__) { + val _tag__ = _input__.readTag() + _tag__ match { + case 0 => _done__ = true + case 10 => + __parameters = Option(__parameters.fold(LiteParser.readMessage[dotty.tools.dotc.semanticdb.Scope](_input__))(LiteParser.readMessage(_input__, _))) + case 18 => + __returnType = _root_.scala.Some(__returnType.fold(LiteParser.readMessage[dotty.tools.dotc.semanticdb.TypeMessage](_input__))(LiteParser.readMessage(_input__, _))) + case tag => _input__.skipField(tag) + } + } + dotty.tools.dotc.semanticdb.LambdaType( + parameters = __parameters, + returnType = dotty.tools.dotc.semanticdb.LambdaType._typemapper_returnType.toCustom(__returnType.getOrElse(dotty.tools.dotc.semanticdb.TypeMessage.defaultInstance)) + ) + } + + + + + + + lazy val defaultInstance = dotty.tools.dotc.semanticdb.LambdaType( + parameters = _root_.scala.None, + returnType = dotty.tools.dotc.semanticdb.LambdaType._typemapper_returnType.toCustom(dotty.tools.dotc.semanticdb.TypeMessage.defaultInstance) + ) + final val PARAMETERS_FIELD_NUMBER = 1 + final val RETURN_TYPE_FIELD_NUMBER = 2 + @transient @sharable + private[semanticdb] val _typemapper_returnType: SemanticdbTypeMapper[dotty.tools.dotc.semanticdb.TypeMessage, dotty.tools.dotc.semanticdb.Type] = implicitly[SemanticdbTypeMapper[dotty.tools.dotc.semanticdb.TypeMessage, dotty.tools.dotc.semanticdb.Type]] + def of( + parameters: _root_.scala.Option[dotty.tools.dotc.semanticdb.Scope], + returnType: dotty.tools.dotc.semanticdb.Type + ): _root_.dotty.tools.dotc.semanticdb.LambdaType = _root_.dotty.tools.dotc.semanticdb.LambdaType( + parameters, + returnType + ) + // @@protoc_insertion_point(GeneratedMessageCompanion[dotty.tools.dotc.semanticdb.LambdaType]) +} diff --git a/tests/semanticdb/expect/hk.expect.scala b/tests/semanticdb/expect/hk.expect.scala new file mode 100644 index 000000000000..5c6e6c294ed9 --- /dev/null +++ b/tests/semanticdb/expect/hk.expect.scala @@ -0,0 +1,17 @@ +package hk + +trait Monad/*<-hk::Monad#*/[M/*<-hk::Monad#[M]*/[_]] { + def pure/*<-hk::Monad#pure().*/[A/*<-hk::Monad#pure().[A]*/](a/*<-hk::Monad#pure().(a)*/: A/*->hk::Monad#pure().[A]*/): M/*->hk::Monad#[M]*/[A/*->hk::Monad#pure().[A]*/] = ???/*->scala::Predef.`???`().*/ + def flatMap/*<-hk::Monad#flatMap().*/[A/*<-hk::Monad#flatMap().[A]*/, B/*<-hk::Monad#flatMap().[B]*/](m/*<-hk::Monad#flatMap().(m)*/: M/*->hk::Monad#[M]*/[A/*->hk::Monad#flatMap().[A]*/])(f/*<-hk::Monad#flatMap().(f)*/: A/*->hk::Monad#flatMap().[A]*/ => M/*->hk::Monad#[M]*/[B/*->hk::Monad#flatMap().[B]*/]): M/*->hk::Monad#[M]*/[B/*->hk::Monad#flatMap().[B]*/] = ???/*->scala::Predef.`???`().*/ +} + +class EitherMonad/*<-hk::EitherMonad#*/[T/*<-hk::EitherMonad#[T]*/] extends Monad/*->hk::Monad#*/[[E/*<-hk::EitherMonad#``().[E]*/] =>> Either/*->scala::package.Either#*/[T/*->hk::EitherMonad#[T]*/, E]] { +} + +type MapKV/*<-hk::hk$package.MapKV#*/ = [K/*<-hk::hk$package.MapKV#[K]*/] =>> [V/*<-hk::hk$package.MapKV#[V]*/] =>> Map/*->scala::Predef.Map#*/[K/*->hk::hk$package.MapKV#[K]*/,V/*->hk::hk$package.MapKV#[V]*/] + +type MapV/*<-hk::hk$package.MapV#*/ = [_] =>> [V/*<-hk::hk$package.MapV#[V]*/] =>> Map/*->scala::Predef.Map#*/[String/*->scala::Predef.String#*/, V/*->hk::hk$package.MapV#[V]*/] + +type MapEither/*<-hk::hk$package.MapEither#*/ = [K/*<-hk::hk$package.MapEither#[K]*/] =>> [L/*<-hk::hk$package.MapEither#[L]*/] =>> [R/*<-hk::hk$package.MapEither#[R]*/] =>> Map/*->scala::Predef.Map#*/[K/*->hk::hk$package.MapEither#[K]*/, Either/*->scala::package.Either#*/[L/*->hk::hk$package.MapEither#[L]*/, R/*->hk::hk$package.MapEither#[R]*/]] + +type Id/*<-hk::hk$package.Id#*/[A/*<-hk::hk$package.Id#[A]*/] = A/*->hk::hk$package.Id#[A]*/ diff --git a/tests/semanticdb/expect/hk.scala b/tests/semanticdb/expect/hk.scala new file mode 100644 index 000000000000..dd24b6f6819a --- /dev/null +++ b/tests/semanticdb/expect/hk.scala @@ -0,0 +1,17 @@ +package hk + +trait Monad[M[_]] { + def pure[A](a: A): M[A] = ??? + def flatMap[A, B](m: M[A])(f: A => M[B]): M[B] = ??? +} + +class EitherMonad[T] extends Monad[[E] =>> Either[T, E]] { +} + +type MapKV = [K] =>> [V] =>> Map[K,V] + +type MapV = [_] =>> [V] =>> Map[String, V] + +type MapEither = [K] =>> [L] =>> [R] =>> Map[K, Either[L, R]] + +type Id[A] = A diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index f5556e28bd1b..0ec8a8e5d84c 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -48,7 +48,7 @@ Schema => SemanticDB v4 Uri => Advanced.scala Text => empty Language => Scala -Symbols => 60 entries +Symbols => 61 entries Occurrences => 138 entries Synthetics => 3 entries @@ -57,10 +57,11 @@ advanced/C# => class C [typeparam T ] extends Object { self: C[T] => +3 decls } advanced/C#[T] => typeparam T advanced/C#``(). => primary ctor [typeparam T ](): C[T] advanced/C#t(). => method t => T -advanced/HKClass# => class HKClass [typeparam F [typeparam T ] <: ] extends Object { self: HKClass[F] => +3 decls } -advanced/HKClass#[F] => typeparam F [typeparam T ] <: +advanced/HKClass# => class HKClass [typeparam F [typeparam T ] <: [U] =>> Tuple2[U, T]] extends Object { self: HKClass[F] => +3 decls } +advanced/HKClass#[F] => typeparam F [typeparam T ] <: [U] =>> Tuple2[U, T] advanced/HKClass#[F][T] => typeparam T -advanced/HKClass#``(). => primary ctor [typeparam F [typeparam T ] <: ](): HKClass[F] +advanced/HKClass#[F][U] => typeparam U +advanced/HKClass#``(). => primary ctor [typeparam F [typeparam T ] <: [U] =>> Tuple2[U, T]](): HKClass[F] advanced/HKClass#``().[F][T] => typeparam T advanced/HKClass#``().[F][U] => typeparam U advanced/HKClass#foo(). => method foo [typeparam T , typeparam U ](param x: F[T, U]): String @@ -3970,6 +3971,103 @@ Occurrences: [0:8..0:15): example <- example/ [2:6..2:24): FilenameWithSpaces <- example/FilenameWithSpaces# +expect/hk.scala +--------------- + +Summary: +Schema => SemanticDB v4 +Uri => hk.scala +Text => empty +Language => Scala +Symbols => 30 entries +Occurrences => 52 entries + +Symbols: +hk/EitherMonad# => class EitherMonad [typeparam T ] extends Object with Monad[[E] =>> Either[T, E]] { self: EitherMonad[T] => +2 decls } +hk/EitherMonad#[E] => typeparam E +hk/EitherMonad#[T] => typeparam T +hk/EitherMonad#``(). => primary ctor [typeparam T ](): EitherMonad[T] +hk/EitherMonad#``().[E] => typeparam E +hk/Monad# => trait Monad [typeparam M [type _ ]] extends Object { self: Monad[M] => +4 decls } +hk/Monad#[M] => typeparam M [type _ ] +hk/Monad#[M][_] => type _ +hk/Monad#``(). => primary ctor [typeparam M [type _ ]](): Monad[M] +hk/Monad#flatMap(). => method flatMap [typeparam A , typeparam B ](param m: M[A])(param f: Function1[A, M[B]]): M[B] +hk/Monad#flatMap().(f) => param f: Function1[A, M[B]] +hk/Monad#flatMap().(m) => param m: M[A] +hk/Monad#flatMap().[A] => typeparam A +hk/Monad#flatMap().[B] => typeparam B +hk/Monad#pure(). => method pure [typeparam A ](param a: A): M[A] +hk/Monad#pure().(a) => param a: A +hk/Monad#pure().[A] => typeparam A +hk/hk$package. => final package object hk extends Object { self: hk.type => +5 decls } +hk/hk$package.Id# => type Id [typeparam A ] = A +hk/hk$package.Id#[A] => typeparam A +hk/hk$package.MapEither# => type MapEither [typeparam K ] = [L] =>> [R] =>> Map[K, Either[L, R]] +hk/hk$package.MapEither#[K] => typeparam K +hk/hk$package.MapEither#[L] => typeparam L +hk/hk$package.MapEither#[R] => typeparam R +hk/hk$package.MapKV# => type MapKV [typeparam K ] = [V] =>> Map[K, V] +hk/hk$package.MapKV#[K] => typeparam K +hk/hk$package.MapKV#[V] => typeparam V +hk/hk$package.MapV# => type MapV [type _ ] = [V] =>> Map[String, V] +hk/hk$package.MapV#[V] => typeparam V +hk/hk$package.MapV#[_] => type _ + +Occurrences: +[0:8..0:10): hk <- hk/ +[2:6..2:11): Monad <- hk/Monad# +[2:12..2:13): M <- hk/Monad#[M] +[3:6..3:10): pure <- hk/Monad#pure(). +[3:11..3:12): A <- hk/Monad#pure().[A] +[3:14..3:15): a <- hk/Monad#pure().(a) +[3:17..3:18): A -> hk/Monad#pure().[A] +[3:21..3:22): M -> hk/Monad#[M] +[3:23..3:24): A -> hk/Monad#pure().[A] +[3:28..3:31): ??? -> scala/Predef.`???`(). +[4:6..4:13): flatMap <- hk/Monad#flatMap(). +[4:14..4:15): A <- hk/Monad#flatMap().[A] +[4:17..4:18): B <- hk/Monad#flatMap().[B] +[4:20..4:21): m <- hk/Monad#flatMap().(m) +[4:23..4:24): M -> hk/Monad#[M] +[4:25..4:26): A -> hk/Monad#flatMap().[A] +[4:29..4:30): f <- hk/Monad#flatMap().(f) +[4:32..4:33): A -> hk/Monad#flatMap().[A] +[4:37..4:38): M -> hk/Monad#[M] +[4:39..4:40): B -> hk/Monad#flatMap().[B] +[4:44..4:45): M -> hk/Monad#[M] +[4:46..4:47): B -> hk/Monad#flatMap().[B] +[4:51..4:54): ??? -> scala/Predef.`???`(). +[7:6..7:17): EitherMonad <- hk/EitherMonad# +[7:18..7:19): T <- hk/EitherMonad#[T] +[7:29..7:34): Monad -> hk/Monad# +[7:36..7:37): E <- hk/EitherMonad#``().[E] +[7:43..7:49): Either -> scala/package.Either# +[7:50..7:51): T -> hk/EitherMonad#[T] +[10:5..10:10): MapKV <- hk/hk$package.MapKV# +[10:14..10:15): K <- hk/hk$package.MapKV#[K] +[10:22..10:23): V <- hk/hk$package.MapKV#[V] +[10:29..10:32): Map -> scala/Predef.Map# +[10:33..10:34): K -> hk/hk$package.MapKV#[K] +[10:35..10:36): V -> hk/hk$package.MapKV#[V] +[12:5..12:9): MapV <- hk/hk$package.MapV# +[12:21..12:22): V <- hk/hk$package.MapV#[V] +[12:28..12:31): Map -> scala/Predef.Map# +[12:32..12:38): String -> scala/Predef.String# +[12:40..12:41): V -> hk/hk$package.MapV#[V] +[14:5..14:14): MapEither <- hk/hk$package.MapEither# +[14:18..14:19): K <- hk/hk$package.MapEither#[K] +[14:26..14:27): L <- hk/hk$package.MapEither#[L] +[14:34..14:35): R <- hk/hk$package.MapEither#[R] +[14:41..14:44): Map -> scala/Predef.Map# +[14:45..14:46): K -> hk/hk$package.MapEither#[K] +[14:48..14:54): Either -> scala/package.Either# +[14:55..14:56): L -> hk/hk$package.MapEither#[L] +[14:58..14:59): R -> hk/hk$package.MapEither#[R] +[16:5..16:7): Id <- hk/hk$package.Id# +[16:8..16:9): A <- hk/hk$package.Id#[A] +[16:13..16:14): A -> hk/hk$package.Id#[A] + expect/i5854.scala ------------------ From 351f602e67f707168c38faf6f2f7eaa6fec1f737 Mon Sep 17 00:00:00 2001 From: Jan-Pieter van den Heuvel Date: Tue, 28 Mar 2023 18:15:24 +0200 Subject: [PATCH 396/657] Fix deprecation check and add tests --- .../src/dotty/tools/dotc/core/Flags.scala | 1 + .../tools/dotc/typer/CrossVersionChecks.scala | 12 +++++++---- .../neg-custom-args/deprecation/i11022.check | 20 +++++++++++++++++++ .../neg-custom-args/deprecation/i11022.scala | 11 ++++++++++ tests/pos/i11022.scala | 3 +++ 5 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 tests/neg-custom-args/deprecation/i11022.check create mode 100644 tests/neg-custom-args/deprecation/i11022.scala create mode 100644 tests/pos/i11022.scala diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index f23dce020f10..47e7a85952b2 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -604,6 +604,7 @@ object Flags { val Scala2Trait: FlagSet = Scala2x | Trait val SyntheticArtifact: FlagSet = Synthetic | Artifact val SyntheticCase: FlagSet = Synthetic | Case + val SyntheticMethod: FlagSet = Synthetic | Method val SyntheticModule: FlagSet = Synthetic | Module val SyntheticOpaque: FlagSet = Synthetic | Opaque val SyntheticParam: FlagSet = Synthetic | Param diff --git a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala index ef9599be551c..84f4754e3c78 100644 --- a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala @@ -52,7 +52,8 @@ class CrossVersionChecks extends MiniPhase: owner.isDeprecated || isEnumOwner(owner) - /**Scan the chain of outer declaring scopes from the current context + /**Skip warnings for synthetic members of case classes during declaration and + * scan the chain of outer declaring scopes from the current context * a deprecation warning will be skipped if one the following holds * for a given declaring scope: * - the symbol associated with the scope is also deprecated. @@ -60,10 +61,13 @@ class CrossVersionChecks extends MiniPhase: * a module that declares `sym`, or the companion class of the * module that declares `sym`. */ - def skipWarning(using Context) = - ctx.owner.ownersIterator.exists(if sym.isEnumCase then isDeprecatedOrEnum else _.isDeprecated) + def skipWarning(using Context): Boolean = + (ctx.owner.is(Synthetic) && sym.is(CaseClass)) + || ctx.owner.ownersIterator.exists(if sym.isEnumCase then isDeprecatedOrEnum else _.isDeprecated) - for annot <- sym.getAnnotation(defn.DeprecatedAnnot) do + // Also check for deprecation of the companion class for synthetic methods + val toCheck = sym :: (if sym.isAllOf(SyntheticMethod) then sym.owner.companionClass :: Nil else Nil) + for sym <- toCheck; annot <- sym.getAnnotation(defn.DeprecatedAnnot) do if !skipWarning then val msg = annot.argumentConstant(0).map(": " + _.stringValue).getOrElse("") val since = annot.argumentConstant(1).map(" since " + _.stringValue).getOrElse("") diff --git a/tests/neg-custom-args/deprecation/i11022.check b/tests/neg-custom-args/deprecation/i11022.check new file mode 100644 index 000000000000..464f2827c49e --- /dev/null +++ b/tests/neg-custom-args/deprecation/i11022.check @@ -0,0 +1,20 @@ +-- Error: tests/neg-custom-args/deprecation/i11022.scala:8:7 ----------------------------------------------------------- +8 |val a: CaseClass = CaseClass(42) // error: deprecated type // error: deprecated apply method + | ^^^^^^^^^ + | class CaseClass is deprecated: no CaseClass +-- Error: tests/neg-custom-args/deprecation/i11022.scala:8:19 ---------------------------------------------------------- +8 |val a: CaseClass = CaseClass(42) // error: deprecated type // error: deprecated apply method + | ^^^^^^^^^ + | class CaseClass is deprecated: no CaseClass +-- Error: tests/neg-custom-args/deprecation/i11022.scala:9:7 ----------------------------------------------------------- +9 |val b: CaseClass = new CaseClass(42) // error: deprecated type // error: deprecated class + | ^^^^^^^^^ + | class CaseClass is deprecated: no CaseClass +-- Error: tests/neg-custom-args/deprecation/i11022.scala:9:23 ---------------------------------------------------------- +9 |val b: CaseClass = new CaseClass(42) // error: deprecated type // error: deprecated class + | ^^^^^^^^^ + | class CaseClass is deprecated: no CaseClass +-- Error: tests/neg-custom-args/deprecation/i11022.scala:10:14 --------------------------------------------------------- +10 |val c: Unit = CaseClass(42).magic() // error: deprecated apply method + | ^^^^^^^^^ + | class CaseClass is deprecated: no CaseClass diff --git a/tests/neg-custom-args/deprecation/i11022.scala b/tests/neg-custom-args/deprecation/i11022.scala new file mode 100644 index 000000000000..4608017eeed9 --- /dev/null +++ b/tests/neg-custom-args/deprecation/i11022.scala @@ -0,0 +1,11 @@ +@deprecated("no CaseClass") +case class CaseClass(rgb: Int): + def magic(): Unit = () + +object CaseClass: + def notDeprecated(): Unit = () + +val a: CaseClass = CaseClass(42) // error: deprecated type // error: deprecated apply method +val b: CaseClass = new CaseClass(42) // error: deprecated type // error: deprecated class +val c: Unit = CaseClass(42).magic() // error: deprecated apply method +val d: Unit = CaseClass.notDeprecated() // compiles diff --git a/tests/pos/i11022.scala b/tests/pos/i11022.scala new file mode 100644 index 000000000000..d020669049c5 --- /dev/null +++ b/tests/pos/i11022.scala @@ -0,0 +1,3 @@ +// scalac: -Werror -deprecation +@deprecated("no CaseClass") +case class CaseClass(rgb: Int) From 3133d8541d3767c833024fcbb35b0126708db066 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Apr 2023 17:22:50 +0200 Subject: [PATCH 397/657] Remove references to deprecated classes `scala.{Traversable,TraversableOnce,BufferedIterator}` are deprecated since 2.13.0. It seems these deprecated references where not found when the check was performed after `FirstTransform` as the types might have been dealiased in the `TypeTree`. --- compiler/src/dotty/tools/dotc/config/PathResolver.scala | 2 +- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- .../dotty/tools/dotc/parsing/xml/MarkupParserCommon.scala | 3 +-- .../src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala | 1 + compiler/src/dotty/tools/dotc/printing/Printer.scala | 6 +++--- compiler/src/dotty/tools/dotc/printing/Texts.scala | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/PathResolver.scala b/compiler/src/dotty/tools/dotc/config/PathResolver.scala index afa30e38dc2a..8b4eedb0e9d2 100644 --- a/compiler/src/dotty/tools/dotc/config/PathResolver.scala +++ b/compiler/src/dotty/tools/dotc/config/PathResolver.scala @@ -211,7 +211,7 @@ class PathResolver(using c: Context) { import classPathFactory._ // Assemble the elements! - def basis: List[Traversable[ClassPath]] = + def basis: List[Iterable[ClassPath]] = val release = Option(ctx.settings.javaOutputVersion.value).filter(_.nonEmpty) List( diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index beeaa2ee922e..aac1760fb1f7 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -299,7 +299,7 @@ object SymDenotations { } /** Add all given annotations to this symbol */ - final def addAnnotations(annots: TraversableOnce[Annotation])(using Context): Unit = + final def addAnnotations(annots: IterableOnce[Annotation])(using Context): Unit = annots.iterator.foreach(addAnnotation) @tailrec diff --git a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParserCommon.scala b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParserCommon.scala index 2c6c5361e51c..0f7d426fbd28 100644 --- a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParserCommon.scala +++ b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParserCommon.scala @@ -11,8 +11,7 @@ package xml import Utility._ import util.Chars.SU - - +import scala.collection.BufferedIterator /** This is not a public trait - it contains common code shared * between the library level XML parser and the compiler's. diff --git a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala index 77c5a1bf376b..b3f41fab9eaa 100644 --- a/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/xml/MarkupParsers.scala @@ -6,6 +6,7 @@ package xml import scala.language.unsafeNulls import scala.collection.mutable +import scala.collection.BufferedIterator import core.Contexts.Context import mutable.{ Buffer, ArrayBuffer, ListBuffer } import scala.util.control.ControlThrowable diff --git a/compiler/src/dotty/tools/dotc/printing/Printer.scala b/compiler/src/dotty/tools/dotc/printing/Printer.scala index 326630844dde..697ab063a646 100644 --- a/compiler/src/dotty/tools/dotc/printing/Printer.scala +++ b/compiler/src/dotty/tools/dotc/printing/Printer.scala @@ -174,15 +174,15 @@ abstract class Printer { atPrec(GlobalPrec) { elem.toText(this) } /** Render elements alternating with `sep` string */ - def toText(elems: Traversable[Showable], sep: String): Text = + def toText(elems: Iterable[Showable], sep: String): Text = Text(elems map (_ toText this), sep) /** Render elements within highest precedence */ - def toTextLocal(elems: Traversable[Showable], sep: String): Text = + def toTextLocal(elems: Iterable[Showable], sep: String): Text = atPrec(DotPrec) { toText(elems, sep) } /** Render elements within lowest precedence */ - def toTextGlobal(elems: Traversable[Showable], sep: String): Text = + def toTextGlobal(elems: Iterable[Showable], sep: String): Text = atPrec(GlobalPrec) { toText(elems, sep) } /** A plain printer without any embellishments */ diff --git a/compiler/src/dotty/tools/dotc/printing/Texts.scala b/compiler/src/dotty/tools/dotc/printing/Texts.scala index 7c040a78de5e..475e2c6900d5 100644 --- a/compiler/src/dotty/tools/dotc/printing/Texts.scala +++ b/compiler/src/dotty/tools/dotc/printing/Texts.scala @@ -173,7 +173,7 @@ object Texts { /** A concatenation of elements in `xs` and interspersed with * separator strings `sep`. */ - def apply(xs: Traversable[Text], sep: String = " "): Text = + def apply(xs: Iterable[Text], sep: String = " "): Text = if (sep == "\n") lines(xs) else { val ys = xs.filterNot(_.isEmpty) @@ -182,7 +182,7 @@ object Texts { } /** The given texts `xs`, each on a separate line */ - def lines(xs: Traversable[Text]): Vertical = Vertical(xs.toList.reverse) + def lines(xs: Iterable[Text]): Vertical = Vertical(xs.toList.reverse) extension (text: => Text) def provided(cond: Boolean): Text = if (cond) text else Str("") From 57c44794449ed53b4bd287960eec69f07d6be6b6 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Apr 2023 16:17:36 +0200 Subject: [PATCH 398/657] Move CrossVersionChecks before FirstTransform Fixes #17292 --- compiler/src/dotty/tools/dotc/Compiler.scala | 6 +-- .../tools/dotc/typer/CrossVersionChecks.scala | 41 ++++++------------- .../neg-custom-args/deprecation/14034b.scala | 4 +- .../no-experimental/14034.scala | 4 +- .../no-experimental/i17292.scala | 7 ++++ .../no-experimental/i17292b.scala | 21 ++++++++++ 6 files changed, 48 insertions(+), 35 deletions(-) create mode 100644 tests/neg-custom-args/no-experimental/i17292.scala create mode 100644 tests/neg-custom-args/no-experimental/i17292b.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index b0f7b0a533d7..a6118732d4ae 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -59,7 +59,8 @@ class Compiler { /** Phases dealing with the transformation from pickled trees to backend trees */ protected def transformPhases: List[List[Phase]] = List(new InstrumentCoverage) :: // Perform instrumentation for code coverage (if -coverage-out is set) - List(new FirstTransform, // Some transformations to put trees into a canonical form + List(new CrossVersionChecks, // Check issues related to deprecated and experimental + new FirstTransform, // Some transformations to put trees into a canonical form new CheckReentrant, // Internal use only: Check that compiled program has no data races involving global vars new ElimPackagePrefixes, // Eliminate references to package prefixes in Select nodes new CookComments, // Cook the comments: expand variables, doc, etc. @@ -71,8 +72,7 @@ class Compiler { new ElimRepeated, // Rewrite vararg parameters and arguments new RefChecks) :: // Various checks mostly related to abstract members and overriding List(new init.Checker) :: // Check initialization of objects - List(new CrossVersionChecks, // Check issues related to deprecated and experimental - new ProtectedAccessors, // Add accessors for protected members + List(new ProtectedAccessors, // Add accessors for protected members new ExtensionMethods, // Expand methods of value classes with extension methods new UncacheGivenAliases, // Avoid caching RHS of simple parameterless given aliases new ElimByName, // Map by-name parameters to functions diff --git a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala index ef9599be551c..0d68b0b1f766 100644 --- a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala @@ -18,9 +18,6 @@ class CrossVersionChecks extends MiniPhase: override def description: String = CrossVersionChecks.description - override def runsAfterGroupsOf: Set[String] = Set(FirstTransform.name) - // We assume all type trees except TypeTree have been eliminated - // Note: if a symbol has both @deprecated and @migration annotations and both // warnings are enabled, only the first one checked here will be emitted. // I assume that's a consequence of some code trying to avoid noise by suppressing @@ -69,18 +66,8 @@ class CrossVersionChecks extends MiniPhase: val since = annot.argumentConstant(1).map(" since " + _.stringValue).getOrElse("") report.deprecationWarning(em"${sym.showLocated} is deprecated${since}${msg}", pos) - private def checkExperimentalSignature(sym: Symbol, pos: SrcPos)(using Context): Unit = - class Checker extends TypeTraverser: - def traverse(tp: Type): Unit = - if tp.typeSymbol.isExperimental then - Feature.checkExperimentalDef(tp.typeSymbol, pos) - else - traverseChildren(tp) - if !sym.isInExperimentalScope then - new Checker().traverse(sym.info) - private def checkExperimentalAnnots(sym: Symbol)(using Context): Unit = - if !sym.isInExperimentalScope then + if sym.exists && !sym.isInExperimentalScope then for annot <- sym.annotations if annot.symbol.isExperimental do Feature.checkExperimentalDef(annot.symbol, annot.tree) @@ -119,13 +106,16 @@ class CrossVersionChecks extends MiniPhase: override def transformValDef(tree: ValDef)(using Context): ValDef = checkDeprecatedOvers(tree) checkExperimentalAnnots(tree.symbol) - checkExperimentalSignature(tree.symbol, tree) tree override def transformDefDef(tree: DefDef)(using Context): DefDef = checkDeprecatedOvers(tree) checkExperimentalAnnots(tree.symbol) - checkExperimentalSignature(tree.symbol, tree) + tree + + override def transformTypeDef(tree: TypeDef)(using Context): TypeDef = + // TODO do we need to check checkDeprecatedOvers(tree)? + checkExperimentalAnnots(tree.symbol) tree override def transformIdent(tree: Ident)(using Context): Ident = { @@ -157,19 +147,14 @@ class CrossVersionChecks extends MiniPhase: tree } - override def transformTypeDef(tree: TypeDef)(using Context): TypeDef = { - checkExperimentalAnnots(tree.symbol) + override def transformOther(tree: Tree)(using Context): Tree = + tree.foreachSubTree { // Find references in type trees and imports + case tree: Ident => transformIdent(tree) + case tree: Select => transformSelect(tree) + case tree: TypeTree => transformTypeTree(tree) + case _ => + } tree - } - - override def transformOther(tree: Tree)(using Context): Tree = tree match - case tree: Import => - tree.foreachSubTree { - case t: RefTree => checkUndesiredProperties(t.symbol, t.srcPos) - case _ => - } - tree - case _ => tree end CrossVersionChecks diff --git a/tests/neg-custom-args/deprecation/14034b.scala b/tests/neg-custom-args/deprecation/14034b.scala index d22a945fe10d..07960bba9574 100644 --- a/tests/neg-custom-args/deprecation/14034b.scala +++ b/tests/neg-custom-args/deprecation/14034b.scala @@ -9,6 +9,6 @@ type Foo0 = Exp // error type Foo = Option[Exp] // error type Bar = Option[exp.type] // error type Baz = Exp | Int // error -type Quux = [X] =>> X match // error - case Exp => Int +type Quux = [X] =>> X match + case Exp => Int // error type Quuz[A <: Exp] = Int // error diff --git a/tests/neg-custom-args/no-experimental/14034.scala b/tests/neg-custom-args/no-experimental/14034.scala index c0b4cc6899db..ab824c43395e 100644 --- a/tests/neg-custom-args/no-experimental/14034.scala +++ b/tests/neg-custom-args/no-experimental/14034.scala @@ -7,6 +7,6 @@ type Foo0 = Exp // error type Foo = Option[Exp] // error type Bar = Option[exp.type] // error type Baz = Exp | Int // error -type Quux = [X] =>> X match // error - case Exp => Int +type Quux = [X] =>> X match + case Exp => Int // error type Quuz[A <: Exp] = Int // error diff --git a/tests/neg-custom-args/no-experimental/i17292.scala b/tests/neg-custom-args/no-experimental/i17292.scala new file mode 100644 index 000000000000..381d252dbea8 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/i17292.scala @@ -0,0 +1,7 @@ +import annotation.experimental + +class Foo { @experimental type Bar = (Int, String) } + +val f: Foo = Foo() + +def g: Tuple.Elem[f.Bar, 0] = ??? // error diff --git a/tests/neg-custom-args/no-experimental/i17292b.scala b/tests/neg-custom-args/no-experimental/i17292b.scala new file mode 100644 index 000000000000..f644dd60ecd5 --- /dev/null +++ b/tests/neg-custom-args/no-experimental/i17292b.scala @@ -0,0 +1,21 @@ +import annotation.experimental +type A[T] = Int +class Foo { + @experimental type Bar = (Int, String) +} + +type Elem1[X <: Tuple, N <: Int] = X match { case x *: xs => N match { case 0 => x } } +type Elem2[X <: Tuple, N <: Int] + +val f: Foo = Foo() + +def bar1: f.Bar = ??? // error +def bar2 = // error + ??? : f.Bar // error + +def g0: Elem1[f.Bar, 0] = ??? // error +def g1(a: Elem1[f.Bar, 0]) = ??? // error +def g2 = + ??? : Elem1[f.Bar, 0] // error + +def h: Elem2[f.Bar, 0] = ??? // error From c2f63626fc3102945c73db8e4b19d7777aa81332 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2023 09:39:21 +0200 Subject: [PATCH 399/657] Rename `Spliced` to `SplicedExpr` --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 2 +- compiler/src/dotty/tools/dotc/inlines/Inliner.scala | 4 ++-- compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala | 2 +- compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/Splicer.scala | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index c2147b6af2d3..77b34335eeb8 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -1044,7 +1044,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } /** Extractors for splices */ - object Spliced { + object SplicedExpr { /** Extracts the content of a spliced expression tree. * The result can be the contents of a term splice, which * will return a term tree. diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index f4612e49ccac..ba6300d2e3d3 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -817,7 +817,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree = def cancelQuotes(tree: Tree): Tree = tree match - case Quoted(Spliced(inner)) => inner + case Quoted(SplicedExpr(inner)) => inner case _ => tree val locked = ctx.typerState.ownedVars val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match { @@ -1071,7 +1071,7 @@ class Inliner(val call: tpd.Tree)(using Context): level += 1 try apply(syms, body) finally level -= 1 - case Spliced(body) => + case SplicedExpr(body) => level -= 1 try apply(syms, body) finally level += 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 33bc5a7ef10e..c3aa98e6ef30 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -290,7 +290,7 @@ object PrepareInlineable { if (inlined.is(Macro) && !ctx.isAfterTyper) { def checkMacro(tree: Tree): Unit = tree match { - case Spliced(code) => + case SplicedExpr(code) => if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index adaacafa764a..49da11afd9b3 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -61,7 +61,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { - case Spliced(t) => + case SplicedExpr(t) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) @@ -69,7 +69,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } finally inQuoteOrSplice = old - case tree @ Spliced(splicedTree) => + case tree @ SplicedExpr(splicedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index bc4119ad0cff..cd41fb358d30 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -158,7 +158,7 @@ object Splicer { case Apply(Select(Apply(fn, quoted :: Nil), nme.apply), _) if fn.symbol == defn.QuotedRuntime_exprQuote => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case Spliced(_) => + case SplicedExpr(_) => report.error("Quoted argument of macros may not have splices", tree.srcPos) case _ => traverseChildren(tree) From 938d3ae5f72192d03b084cc84e37bb677d5dc29a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2023 09:50:10 +0200 Subject: [PATCH 400/657] Split `Quoted` extractor into `QuotedExpr` and `QuotedTypeOf` --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 13 ++++++++++-- .../dotty/tools/dotc/inlines/Inliner.scala | 8 ++++++-- .../dotc/staging/TreeMapWithStages.scala | 20 ++++++++++--------- .../dotty/tools/dotc/transform/Splicer.scala | 4 ++-- .../tools/dotc/transform/patmat/Space.scala | 2 +- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 77b34335eeb8..f2a49254678f 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -1027,7 +1027,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } /** Extractors for quotes */ - object Quoted { + object QuotedExpr { /** Extracts the content of a quoted tree. * The result can be the contents of a term or type quote, which * will return a term or type tree respectively. @@ -1036,7 +1036,16 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => if tree.symbol == defn.QuotedRuntime_exprQuote then // quoted.runtime.Expr.quote[T]() Some(tree.args.head) - else if tree.symbol == defn.QuotedTypeModule_of then + else None + } + + object QuotedTypeOf { + /** Extracts the content of a quoted tree. + * The result can be the contents of a term or type quote, which + * will return a term or type tree respectively. + */ + def unapply(tree: tpd.Apply)(using Context): Option[tpd.Tree] = + if tree.symbol == defn.QuotedTypeModule_of then // quoted.Type.of[](quotes) val TypeApply(_, body :: _) = tree.fun: @unchecked Some(body) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index ba6300d2e3d3..50cd933532ae 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -817,7 +817,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree = def cancelQuotes(tree: Tree): Tree = tree match - case Quoted(SplicedExpr(inner)) => inner + case QuotedExpr(SplicedExpr(inner)) => inner case _ => tree val locked = ctx.typerState.ownedVars val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match { @@ -1067,7 +1067,11 @@ class Inliner(val call: tpd.Tree)(using Context): else tree match { case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) - case Quoted(body) => + case QuotedExpr(body) => + level += 1 + try apply(syms, body) + finally level -= 1 + case QuotedTypeOf(body) => level += 1 try apply(syms, body) finally level -= 1 diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 49da11afd9b3..096454de9a7f 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -49,15 +49,17 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } tree match { - case Apply(Select(Quoted(quotedTree), _), _) if quotedTree.isType => - dropEmptyBlocks(quotedTree) match - case SplicedType(t) => - // Optimization: `quoted.Type.of[x.Underlying]` --> `x` - transform(t) - case _ => - super.transform(tree) + case Apply(Select(QuotedTypeOf(SplicedType(t)), _), _) => + // Optimization: `quoted.Type.of[x.Underlying]` --> `x` + transform(t) + + case tree @ QuotedTypeOf(quotedTree) => + val old = inQuoteOrSplice + inQuoteOrSplice = true + try transformQuotation(quotedTree, tree) + finally inQuoteOrSplice = old - case tree @ Quoted(quotedTree) => + case tree @ QuotedExpr(quotedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { @@ -73,7 +75,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { - case Quoted(t) => + case QuotedExpr(t) => // Optimization: `${ 'x }` --> `x` transform(t) case _ => transformSplice(splicedTree, tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index cd41fb358d30..11e88585f144 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -44,7 +44,7 @@ object Splicer { * See: `Staging` */ def splice(tree: Tree, splicePos: SrcPos, spliceExpansionPos: SrcPos, classLoader: ClassLoader)(using Context): Tree = tree match { - case Quoted(quotedTree) => quotedTree + case QuotedExpr(quotedTree) => quotedTree case _ => val macroOwner = newSymbol(ctx.owner, nme.MACROkw, Macro | Synthetic, defn.AnyType, coord = tree.span) try @@ -136,7 +136,7 @@ object Splicer { * See: `Staging` */ def checkValidMacroBody(tree: Tree)(using Context): Unit = tree match { - case Quoted(_) => // ok + case QuotedExpr(_) => // ok case _ => type Env = Set[Symbol] diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 3e05310d7249..5db7c61aebc0 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -312,7 +312,7 @@ object SpaceEngine { def isIrrefutableQuotedPattern(unapp: tpd.Tree, implicits: List[tpd.Tree], pt: Type)(using Context): Boolean = { implicits.headOption match // pattern '{ $x: T } - case Some(tpd.Apply(tpd.Select(tpd.Quoted(tpd.TypeApply(fn, List(tpt))), nme.apply), _)) + case Some(tpd.Apply(tpd.Select(tpd.QuotedExpr(tpd.TypeApply(fn, List(tpt))), nme.apply), _)) if unapp.symbol.owner.eq(defn.QuoteMatching_ExprMatchModule) && fn.symbol.eq(defn.QuotedRuntimePatterns_patternHole) => pt <:< defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe) From b7ca9b1e051c5f3dbca6eb07b226b31f5492ba69 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2023 10:28:01 +0200 Subject: [PATCH 401/657] Split `transformQuotation` into `transformQuotedExpr` and `transformQuotedType` --- .../tools/dotc/staging/CrossStageSafety.scala | 67 ++++++++++--------- .../dotc/staging/TreeMapWithStages.scala | 20 +++--- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 400a846194e7..9dc12d553abf 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -98,46 +98,47 @@ class CrossStageSafety extends TreeMapWithStages { } /** Transform quoted trees while maintaining level correctness */ - override protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = { - val taggedTypes = new QuoteTypeTags(quote.span) - + override protected def transformQuotedExpr(body: Tree, quote: Apply)(using Context): Tree = { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) val stripAnnotsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) + val transformedBody = transformQuoteBody(body, quote) + // `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T2]()` + val TypeApply(fun, targs) = quote.fun: @unchecked + val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) + cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), transformedBody :: Nil) + } - def transformBody() = - val contextWithQuote = - if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext) - else quoteContext - val transformedBody = transform(body)(using contextWithQuote) - taggedTypes.getTypeTags match - case Nil => transformedBody - case tags => tpd.Block(tags, transformedBody).withSpan(body.span) - - if body.isTerm then - val transformedBody = transformBody() - // `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T2]()` - val TypeApply(fun, targs) = quote.fun: @unchecked - val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), transformedBody :: Nil) - else - body.tpe match - case DirectTypeOf(termRef) => - // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` - ref(termRef).withSpan(quote.span) - case _ => - transformBody() match - case DirectTypeOf.Healed(termRef) => - // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` - ref(termRef).withSpan(quote.span) - case transformedBody => - val quotes = quote.args.mapConserve(transform) - // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` - val TypeApply(fun, _) = quote.fun: @unchecked - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes) + override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { + if (ctx.property(InAnnotation).isDefined) + report.error("Cannot have a quote in an annotation", quote.srcPos) + body.tpe match + case DirectTypeOf(termRef) => + // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` + ref(termRef).withSpan(quote.span) + case _ => + transformQuoteBody(body, quote) match + case DirectTypeOf.Healed(termRef) => + // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` + ref(termRef).withSpan(quote.span) + case transformedBody => + val quotes = quote.args.mapConserve(transform) + // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` + val TypeApply(fun, _) = quote.fun: @unchecked + cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes) + } + private def transformQuoteBody(body: Tree, quote: Apply)(using Context): Tree = { + val taggedTypes = new QuoteTypeTags(quote.span) + val contextWithQuote = + if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext) + else quoteContext + val transformedBody = transform(body)(using contextWithQuote) + taggedTypes.getTypeTags match + case Nil => transformedBody + case tags => tpd.Block(tags, transformedBody).withSpan(body.span) } /** Transform splice diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 096454de9a7f..c88a6aafebe6 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -24,14 +24,17 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { /** Transform the quote `quote` which contains the quoted `body`. * * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` + */ + protected def transformQuotedExpr(body: Tree, quote: Apply)(using Context): Tree = + cpy.Apply(quote)(quote.fun, body :: Nil) + + /** Transform the quote `quote` which contains the quoted `body`. + * * - `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` */ - protected def transformQuotation(body: Tree, quote: Apply)(using Context): Tree = - if body.isTerm then - cpy.Apply(quote)(quote.fun, body :: Nil) - else - val TypeApply(fun, _) = quote.fun: @unchecked - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args) + protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = + val TypeApply(fun, _) = quote.fun: @unchecked + cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args) /** Transform the expression splice `splice` which contains the spliced `body`. */ protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree @@ -56,7 +59,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { case tree @ QuotedTypeOf(quotedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true - try transformQuotation(quotedTree, tree) + try transformQuotedType(quotedTree, tree) finally inQuoteOrSplice = old case tree @ QuotedExpr(quotedTree) => @@ -67,7 +70,8 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) - case _ => transformQuotation(quotedTree, tree) + case _ => + transformQuotedExpr(quotedTree, tree) } finally inQuoteOrSplice = old From deb03a7191f5523b8fc787edb8c0cec5022da21a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2023 13:52:40 +0200 Subject: [PATCH 402/657] Add `QuotedExpr` to encode quotes directly in the AST --- .../dotty/tools/dotc/CompilationUnit.scala | 6 ++-- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 13 --------- compiler/src/dotty/tools/dotc/ast/Trees.scala | 14 ++++++++++ compiler/src/dotty/tools/dotc/ast/tpd.scala | 3 ++ compiler/src/dotty/tools/dotc/ast/untpd.scala | 1 + .../tools/dotc/core/tasty/TreePickler.scala | 8 ++++++ .../tools/dotc/core/tasty/TreeUnpickler.scala | 5 ++++ .../dotty/tools/dotc/inlines/Inliner.scala | 10 +++++-- .../dotc/inlines/PrepareInlineable.scala | 3 +- .../tools/dotc/printing/RefinedPrinter.scala | 5 ++-- .../tools/dotc/staging/CrossStageSafety.scala | 17 +++++------ .../dotc/staging/TreeMapWithStages.scala | 8 +++--- .../dotty/tools/dotc/transform/Inlining.scala | 8 ++++-- .../tools/dotc/transform/PickleQuotes.scala | 9 +++--- .../tools/dotc/transform/PostTyper.scala | 5 +++- .../dotty/tools/dotc/transform/Splicer.scala | 24 ++++++++-------- .../dotty/tools/dotc/transform/Splicing.scala | 28 ++++++++----------- .../dotty/tools/dotc/transform/SymUtils.scala | 4 --- .../tools/dotc/transform/patmat/Space.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 7 +++-- .../dotty/tools/dotc/typer/TypeAssigner.scala | 6 ++++ .../src/dotty/tools/dotc/typer/Typer.scala | 7 +++++ 22 files changed, 115 insertions(+), 78 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 046b649941b1..897befdda4a5 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -154,11 +154,13 @@ object CompilationUnit { var containsCaptureChecking = false var containsMacroAnnotation = false def traverse(tree: Tree)(using Context): Unit = { - if (tree.symbol.isQuote) - containsQuote = true if tree.symbol.is(Flags.Inline) then containsInline = true tree match + case tpd.QuotedExpr(_, _) => + containsQuote = true + case tree: tpd.Apply if tree.symbol == defn.QuotedTypeModule_of => + containsQuote = true case Import(qual, selectors) => tpd.languageImport(qual) match case Some(prefix) => diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index f2a49254678f..96f189425180 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -1026,19 +1026,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => case t => assert(t.span.exists, i"$t") } - /** Extractors for quotes */ - object QuotedExpr { - /** Extracts the content of a quoted tree. - * The result can be the contents of a term or type quote, which - * will return a term or type tree respectively. - */ - def unapply(tree: tpd.Apply)(using Context): Option[tpd.Tree] = - if tree.symbol == defn.QuotedRuntime_exprQuote then - // quoted.runtime.Expr.quote[T]() - Some(tree.args.head) - else None - } - object QuotedTypeOf { /** Extracts the content of a quoted tree. * The result can be the contents of a term or type quote, which diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 1c1e80922c05..0028cf21c244 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -677,6 +677,11 @@ object Trees { override def isType = expansion.isType } + case class QuotedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + extends TermTree[T] { + type ThisTree[+T <: Untyped] = QuotedExpr[T] + } + /** A type tree that represents an existing or inferred type */ case class TypeTree[+T <: Untyped]()(implicit @constructorOnly src: SourceFile) extends DenotingTree[T] with TypTree[T] { @@ -1087,6 +1092,7 @@ object Trees { type SeqLiteral = Trees.SeqLiteral[T] type JavaSeqLiteral = Trees.JavaSeqLiteral[T] type Inlined = Trees.Inlined[T] + type QuotedExpr = Trees.QuotedExpr[T] type TypeTree = Trees.TypeTree[T] type InferredTypeTree = Trees.InferredTypeTree[T] type SingletonTypeTree = Trees.SingletonTypeTree[T] @@ -1257,6 +1263,10 @@ object Trees { case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree))) } + def QuotedExpr(tree: Tree)(expr: Tree, tpt: Tree)(using Context): QuotedExpr = tree match { + case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree + case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree))) + } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree case _ => finalize(tree, untpd.SingletonTypeTree(ref)(sourceFile(tree))) @@ -1494,6 +1504,8 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) + case tree @ QuotedExpr(expr, tpt) => + cpy.QuotedExpr(tree)(transform(expr), transform(tpt)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1635,6 +1647,8 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) + case QuotedExpr(expr, tpt) => + this(this(x, expr), tpt) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index c6e0bc581b59..88a822876043 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,6 +170,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) + def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr = + ta.assignType(untpd.QuotedExpr(expr, tpt), tpt) + def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index a262c3658399..7088739ddef3 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -401,6 +401,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): SeqLiteral = new SeqLiteral(elems, elemtpt) def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) + def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 8a396921f32b..d704c6eac771 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,6 +665,14 @@ class TreePickler(pickler: TastyPickler) { pickleTree(hi) pickleTree(alias) } + case QuotedExpr(expr, tpt) => + pickleTree( + // scala.quoted.runtime.Expr.quoted[]() + ref(defn.QuotedRuntime_exprQuote) + .appliedToTypeTree(tpt) + .appliedTo(expr) + .withSpan(tree.span) + ) case Hole(_, idx, args, _, tpt) => writeByte(HOLE) withLength { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 428047d96e0c..a982e389f468 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1267,6 +1267,10 @@ class TreeUnpickler(reader: TastyReader, res.withAttachment(SuppressedApplyToNone, ()) else res + def quotedExpr(fn: Tree, args: List[Tree]): Tree = + val TypeApply(_, targs) = fn: @unchecked + QuotedExpr(args.head, targs.head) + def simplifyLub(tree: Tree): Tree = tree.overwriteType(tree.tpe.simplified) tree @@ -1283,6 +1287,7 @@ class TreeUnpickler(reader: TastyReader, val fn = readTree() val args = until(end)(readTree()) if fn.symbol.isConstructor then constructorApply(fn, args) + else if fn.symbol == defn.QuotedRuntime_exprQuote then quotedExpr(fn, args) else tpd.Apply(fn, args) case TYPEAPPLY => tpd.TypeApply(readTree(), until(end)(readTpt())) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 50cd933532ae..e4c503206abf 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -817,7 +817,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree = def cancelQuotes(tree: Tree): Tree = tree match - case QuotedExpr(SplicedExpr(inner)) => inner + case QuotedExpr(SplicedExpr(inner), _) => inner case _ => tree val locked = ctx.typerState.ownedVars val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match { @@ -835,10 +835,14 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree = val locked = ctx.typerState.ownedVars val tree1 = inlineIfNeeded(constToLiteral(BetaReduce(super.typedTypeApply(tree, pt))), pt, locked) - if tree1.symbol.isQuote then + if tree1.symbol == defn.QuotedTypeModule_of then ctx.compilationUnit.needsStaging = true tree1 + override def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = + ctx.compilationUnit.needsStaging = true + super.typedQuotedExpr(tree, pt) + override def typedMatch(tree: untpd.Match, pt: Type)(using Context): Tree = val tree1 = if tree.isInline then @@ -1067,7 +1071,7 @@ class Inliner(val call: tpd.Tree)(using Context): else tree match { case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) - case QuotedExpr(body) => + case QuotedExpr(body, _) => level += 1 try apply(syms, body) finally level -= 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index c3aa98e6ef30..50c6a72a5b12 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -91,7 +91,8 @@ object PrepareInlineable { } private def stagingContext(tree: Tree)(using Context): Context = tree match - case tree: Apply if tree.symbol.isQuote => StagingLevel.quoteContext + case tree: QuotedExpr => StagingLevel.quoteContext + case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext case tree: Apply if tree.symbol.isExprSplice => StagingLevel.spliceContext case _ => ctx } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 014e5ddf0d66..2f354259b33c 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -432,8 +432,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { changePrec (GlobalPrec) { keywordStr("throw ") ~ toText(args.head) } - else if (!printDebug && fun.hasType && fun.symbol == defn.QuotedRuntime_exprQuote) - keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}") else if (!printDebug && fun.hasType && fun.symbol.isExprSplice) keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}") else @@ -724,6 +722,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) + case QuotedExpr(expr, tpt) => + val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) + keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 9dc12d553abf..e432baf9068c 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -98,17 +98,14 @@ class CrossStageSafety extends TreeMapWithStages { } /** Transform quoted trees while maintaining level correctness */ - override protected def transformQuotedExpr(body: Tree, quote: Apply)(using Context): Tree = { + override protected def transformQuotedExpr(body: Tree, quote: QuotedExpr)(using Context): Tree = { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) - + val transformedBody = transformQuoteBody(body, quote.span) val stripAnnotsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val transformedBody = transformQuoteBody(body, quote) - // `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T2]()` - val TypeApply(fun, targs) = quote.fun: @unchecked - val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe)))) - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), transformedBody :: Nil) + val tpt1 = TypeTree(healType(quote.tpt.srcPos)(stripAnnotsDeep(quote.tpt.tpe))) + cpy.QuotedExpr(quote)(transformedBody, tpt1) } override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { @@ -119,7 +116,7 @@ class CrossStageSafety extends TreeMapWithStages { // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` ref(termRef).withSpan(quote.span) case _ => - transformQuoteBody(body, quote) match + transformQuoteBody(body, quote.span) match case DirectTypeOf.Healed(termRef) => // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` ref(termRef).withSpan(quote.span) @@ -130,8 +127,8 @@ class CrossStageSafety extends TreeMapWithStages { cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes) } - private def transformQuoteBody(body: Tree, quote: Apply)(using Context): Tree = { - val taggedTypes = new QuoteTypeTags(quote.span) + private def transformQuoteBody(body: Tree, span: Span)(using Context): Tree = { + val taggedTypes = new QuoteTypeTags(span) val contextWithQuote = if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext) else quoteContext diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index c88a6aafebe6..a35fa363aa6b 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -25,8 +25,8 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { * * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` */ - protected def transformQuotedExpr(body: Tree, quote: Apply)(using Context): Tree = - cpy.Apply(quote)(quote.fun, body :: Nil) + protected def transformQuotedExpr(body: Tree, quote: QuotedExpr)(using Context): Tree = + cpy.QuotedExpr(quote)(body, quote.tpt) /** Transform the quote `quote` which contains the quoted `body`. * @@ -62,7 +62,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { try transformQuotedType(quotedTree, tree) finally inQuoteOrSplice = old - case tree @ QuotedExpr(quotedTree) => + case tree @ QuotedExpr(quotedTree, _) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { @@ -79,7 +79,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { - case QuotedExpr(t) => + case QuotedExpr(t, _) => // Optimization: `${ 'x }` --> `x` transform(t) case _ => transformSplice(splicedTree, tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index d6b7f3141b96..a7644fa2a528 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -46,7 +46,9 @@ class Inlining extends MacroTransform { new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case _: GenericApply if tree.symbol.isQuote => + case _: QuotedExpr => + traverseChildren(tree)(using StagingLevel.quoteContext) + case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => traverseChildren(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol.isExprSplice => traverseChildren(tree)(using StagingLevel.spliceContext) @@ -98,7 +100,9 @@ class Inlining extends MacroTransform { val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 else Inlines.inlineCall(tree1) - case _: GenericApply if tree.symbol.isQuote => + case _: QuotedExpr => + super.transform(tree)(using StagingLevel.quoteContext) + case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => super.transform(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol.isExprSplice => super.transform(tree)(using StagingLevel.spliceContext) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 62174c806f09..6507bed179c3 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -83,8 +83,10 @@ class PickleQuotes extends MacroTransform { override def checkPostCondition(tree: Tree)(using Context): Unit = tree match + case tree: QuotedExpr => + assert(Inlines.inInlineMethod) case tree: RefTree if !Inlines.inInlineMethod => - assert(!tree.symbol.isQuote) + assert(tree.symbol != defn.QuotedTypeModule_of) assert(!tree.symbol.isExprSplice) case _ : TypeDef if !Inlines.inInlineMethod => assert(!tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot), @@ -97,9 +99,8 @@ class PickleQuotes extends MacroTransform { protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case Apply(Select(Apply(TypeApply(fn, List(tpt)), List(code)),nme.apply), List(quotes)) - if fn.symbol == defn.QuotedRuntime_exprQuote => - val (contents, codeWithHoles) = makeHoles(code) + case Apply(Select(QuotedExpr(expr, tpt), nme.apply), List(quotes)) => + val (contents, codeWithHoles) = makeHoles(expr) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles) val pickled = PickleQuotes(quotes, codeWithHoles2, contents, tpt.tpe, false) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 7f3e47c14732..5cfa74fd5610 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -342,7 +342,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val patterns1 = transform(patterns) cpy.UnApply(tree)(transform(fun), transform(implicits), patterns1) case tree: TypeApply => - if tree.symbol.isQuote then + if tree.symbol == defn.QuotedTypeModule_of then ctx.compilationUnit.needsStaging = true if tree.symbol.is(Inline) then ctx.compilationUnit.needsInlining = true @@ -485,6 +485,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) => superAcc.withInvalidCurrentClass(super.transform(tree)) + case QuotedExpr(expr, _) => + ctx.compilationUnit.needsStaging = true + super.transform(tree) case tree => super.transform(tree) } diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 11e88585f144..2981bdafc958 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -44,7 +44,7 @@ object Splicer { * See: `Staging` */ def splice(tree: Tree, splicePos: SrcPos, spliceExpansionPos: SrcPos, classLoader: ClassLoader)(using Context): Tree = tree match { - case QuotedExpr(quotedTree) => quotedTree + case QuotedExpr(quotedTree, _) => quotedTree case _ => val macroOwner = newSymbol(ctx.owner, nme.MACROkw, Macro | Synthetic, defn.AnyType, coord = tree.span) try @@ -136,7 +136,7 @@ object Splicer { * See: `Staging` */ def checkValidMacroBody(tree: Tree)(using Context): Unit = tree match { - case QuotedExpr(_) => // ok + case QuotedExpr(_, _) => // ok case _ => type Env = Set[Symbol] @@ -155,7 +155,7 @@ object Splicer { case Block(Nil, expr) => checkIfValidArgument(expr) case Typed(expr, _) => checkIfValidArgument(expr) - case Apply(Select(Apply(fn, quoted :: Nil), nme.apply), _) if fn.symbol == defn.QuotedRuntime_exprQuote => + case Apply(Select(QuotedExpr(expr, tpt), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match case SplicedExpr(_) => @@ -163,7 +163,7 @@ object Splicer { case _ => traverseChildren(tree) } - noSpliceChecker.traverse(quoted) + noSpliceChecker.traverse(expr) case Apply(TypeApply(fn, List(quoted)), _)if fn.symbol == defn.QuotedTypeModule_of => // OK @@ -203,7 +203,7 @@ object Splicer { case Typed(expr, _) => checkIfValidStaticCall(expr) - case Apply(Select(Apply(fn, quoted :: Nil), nme.apply), _) if fn.symbol == defn.QuotedRuntime_exprQuote => + case Apply(Select(QuotedExpr(quoted, tpt), nme.apply), _) => // OK, canceled and warning emitted case Call(fn, args) @@ -240,15 +240,15 @@ object Splicer { override protected def interpretTree(tree: Tree)(implicit env: Env): Object = tree match { // Interpret level -1 quoted code `'{...}` (assumed without level 0 splices) - case Apply(Select(Apply(TypeApply(fn, _), quoted :: Nil), nme.apply), _) if fn.symbol == defn.QuotedRuntime_exprQuote => - val quoted1 = quoted match { - case quoted: Ident if quoted.symbol.isAllOf(InlineByNameProxy) => + case Apply(Select(QuotedExpr(expr, _), nme.apply), _) => + val expr1 = expr match { + case expr: Ident if expr.symbol.isAllOf(InlineByNameProxy) => // inline proxy for by-name parameter - quoted.symbol.defTree.asInstanceOf[DefDef].rhs - case Inlined(EmptyTree, _, quoted) => quoted - case _ => quoted + expr.symbol.defTree.asInstanceOf[DefDef].rhs + case Inlined(EmptyTree, _, expr1) => expr1 + case _ => expr } - new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(quoted1, ctx.owner)).withSpan(quoted1.span), SpliceScope.getCurrent) + new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(expr1, ctx.owner)).withSpan(expr1.span), SpliceScope.getCurrent) // Interpret level -1 `Type.of[T]` case Apply(TypeApply(fn, quoted :: Nil), _) if fn.symbol == defn.QuotedTypeModule_of => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index e959f5d1451d..f5e536d85a55 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -87,8 +87,7 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = assert(level == 0) tree match - case Apply(Select(Apply(TypeApply(fn,_), List(code)),nme.apply),List(quotes)) - if fn.symbol == defn.QuotedRuntime_exprQuote => + case Apply(Select(QuotedExpr(expr, _), nme.apply),List(quotes)) => QuoteTransformer().transform(tree) case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of => QuoteTransformer().transform(tree) @@ -136,8 +135,7 @@ class Splicing extends MacroTransform: typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) - case Apply(Select(Apply(TypeApply(fn,_), List(code)),nme.apply),List(quotes)) - if fn.symbol == defn.QuotedRuntime_exprQuote => + case Apply(Select(QuotedExpr(expr, tpt), nme.apply),List(quotes)) => super.transform(tree)(using quoteContext) case _: Template => for sym <- tree.symbol.owner.info.decls do @@ -240,17 +238,16 @@ class Splicing extends MacroTransform: case Apply(fn, args) if fn.symbol == defn.QuotedRuntime_exprNestedSplice => val newArgs = args.mapConserve(arg => transform(arg)(using spliceContext)) cpy.Apply(tree)(fn, newArgs) - case Apply(sel @ Select(app @ Apply(fn, args),nme.apply), quotesArgs) - if fn.symbol == defn.QuotedRuntime_exprQuote => - args match - case List(tree: RefTree) if isCaptured(tree.symbol) => - capturedTerm(tree) + case Apply(sel @ Select(app @ QuotedExpr(expr, tpt), nme.apply), quotesArgs) => + expr match + case expr: RefTree if isCaptured(expr.symbol) => + capturedTerm(expr) case _ => - val newArgs = withCurrentQuote(quotesArgs.head) { - if level > 1 then args.mapConserve(arg => transform(arg)(using quoteContext)) - else args.mapConserve(arg => transformLevel0QuoteContent(arg)(using quoteContext)) + val newExpr = withCurrentQuote(quotesArgs.head) { + if level > 1 then transform(expr)(using quoteContext) + else transformLevel0QuoteContent(expr)(using quoteContext) } - cpy.Apply(tree)(cpy.Select(sel)(cpy.Apply(app)(fn, newArgs), nme.apply), quotesArgs) + cpy.Apply(tree)(cpy.Select(sel)(cpy.QuotedExpr(app)(newExpr, tpt), nme.apply), quotesArgs) case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => val newContent = capturedPartTypes(tpt) @@ -419,10 +416,7 @@ class Splicing extends MacroTransform: .appliedTo(closure) private def quoted(expr: Tree)(using Context): Tree = - val tpe = expr.tpe.widenTermRefExpr - ref(defn.QuotedRuntime_exprQuote) - .appliedToType(tpe) - .appliedTo(expr) + QuotedExpr(expr, TypeTree(expr.tpe.widenTermRefExpr)) .select(nme.apply) .appliedTo(quotes.nn) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index b945f5820523..a5844dc0b538 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -315,10 +315,6 @@ object SymUtils: def reachableRawTypeRef(using Context) = self.reachableTypeRef.appliedTo(self.typeParams.map(_ => TypeBounds.emptyPolyKind)) - /** Is symbol a quote operation? */ - def isQuote(using Context): Boolean = - self == defn.QuotedRuntime_exprQuote || self == defn.QuotedTypeModule_of - /** Is symbol a term splice operation? */ def isExprSplice(using Context): Boolean = self == defn.QuotedRuntime_exprSplice || self == defn.QuotedRuntime_exprNestedSplice diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 5db7c61aebc0..f7518ec8b9f5 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -312,7 +312,7 @@ object SpaceEngine { def isIrrefutableQuotedPattern(unapp: tpd.Tree, implicits: List[tpd.Tree], pt: Type)(using Context): Boolean = { implicits.headOption match // pattern '{ $x: T } - case Some(tpd.Apply(tpd.Select(tpd.QuotedExpr(tpd.TypeApply(fn, List(tpt))), nme.apply), _)) + case Some(tpd.Apply(tpd.Select(tpd.QuotedExpr(tpd.TypeApply(fn, List(tpt)), _), nme.apply), _)) if unapp.symbol.owner.eq(defn.QuoteMatching_ExprMatchModule) && fn.symbol.eq(defn.QuotedRuntimePatterns_patternHole) => pt <:< defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 8473bd168bc5..e8e0ad7cf4a2 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -60,7 +60,9 @@ trait QuotesAndSplices { EmptyTree else val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.quoted) - makeInlineable(typedApply(exprQuoteTree, pt)(using pushQuotes(qctx)).select(nme.apply).appliedTo(qctx).withSpan(tree.span)) + val quotedExpr = typedApply(exprQuoteTree, pt)(using pushQuotes(qctx)) match + case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => QuotedExpr(quotedExpr, tpt) + makeInlineable(quotedExpr.select(nme.apply).appliedTo(qctx).withSpan(tree.span)) } private def makeInlineable(tree: Tree)(using Context): Tree = @@ -75,6 +77,7 @@ trait QuotesAndSplices { tree.expr match { case untpd.Quote(innerExpr) if innerExpr.isTerm => report.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.srcPos) + return typed(innerExpr, pt) case _ => } if (ctx.mode.is(Mode.QuotedPattern)) @@ -441,7 +444,7 @@ trait QuotesAndSplices { val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (tree.quoted.isTerm) ref(defn.QuotedRuntime_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx) + if (tree.quoted.isTerm) tpd.QuotedExpr(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx) val matchModule = if tree.quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6ac45cbcf04d..2d921f4dbcb1 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -392,6 +392,12 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(using Context): Inlined = tree.withType(avoidingType(expansion, bindings)) + def assignType(tree: untpd.QuotedExpr, tpt: Tree)(using Context): QuotedExpr = + val lambdaType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe)) + tree.withType(lambdaType) + def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(using Context): If = tree.withType(thenp.tpe | elsep.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 4bc012b5b226..ddf16f213676 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2045,6 +2045,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer bindings1, expansion1) } + def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem) + assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) + .withNotNullInfo(expr1.notNullInfo) + def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) .withType( @@ -3076,6 +3082,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree case tree: untpd.Quote => typedQuote(tree, pt) + case tree: untpd.QuotedExpr => typedQuotedExpr(tree, pt) case tree: untpd.Splice => typedSplice(tree, pt) case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here case tree: untpd.Hole => typedHole(tree, pt) From b7a7227e55558de2c483731d34b829d2d4018203 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 21 Apr 2023 17:28:25 +0200 Subject: [PATCH 403/657] Add `SplicedExpr` to encode splices directly in the AST --- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 10 ------ compiler/src/dotty/tools/dotc/ast/Trees.scala | 16 +++++++++ compiler/src/dotty/tools/dotc/ast/tpd.scala | 3 ++ compiler/src/dotty/tools/dotc/ast/untpd.scala | 1 + .../tools/dotc/core/tasty/TreePickler.scala | 17 +++++++++ .../tools/dotc/core/tasty/TreeUnpickler.scala | 13 +++++++ .../dotty/tools/dotc/inlines/Inliner.scala | 35 +++++++++---------- .../dotc/inlines/PrepareInlineable.scala | 6 ++-- .../tools/dotc/printing/RefinedPrinter.scala | 6 ++-- .../tools/dotc/staging/CrossStageSafety.scala | 20 +++++------ .../dotc/staging/TreeMapWithStages.scala | 9 ++--- .../tools/dotc/transform/ElimByName.scala | 6 ++++ .../tools/dotc/transform/FirstTransform.scala | 3 ++ .../dotty/tools/dotc/transform/Inlining.scala | 4 +-- .../tools/dotc/transform/PickleQuotes.scala | 3 +- .../dotty/tools/dotc/transform/Splicer.scala | 2 +- .../dotty/tools/dotc/transform/Splicing.scala | 19 +++++----- .../dotty/tools/dotc/transform/SymUtils.scala | 4 --- .../tools/dotc/typer/QuotesAndSplices.scala | 18 ++++++---- .../dotty/tools/dotc/typer/TypeAssigner.scala | 3 ++ .../src/dotty/tools/dotc/typer/Typer.scala | 11 +++++- 21 files changed, 134 insertions(+), 75 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 96f189425180..5b409b6fec9a 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -1039,16 +1039,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => else None } - /** Extractors for splices */ - object SplicedExpr { - /** Extracts the content of a spliced expression tree. - * The result can be the contents of a term splice, which - * will return a term tree. - */ - def unapply(tree: tpd.Apply)(using Context): Option[tpd.Tree] = - if tree.symbol.isExprSplice then Some(tree.args.head) else None - } - /** Extractors for type splices */ object SplicedType { /** Extracts the content of a spliced type tree. diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 0028cf21c244..63e4ab859899 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -682,6 +682,11 @@ object Trees { type ThisTree[+T <: Untyped] = QuotedExpr[T] } + case class SplicedExpr[+T <: Untyped] private[ast] (spliced: Tree[T], tpt: Tree[T], outerQuotes: Tree[T])(implicit @constructorOnly src: SourceFile) + extends TermTree[T] { + type ThisTree[+T <: Untyped] = SplicedExpr[T] + } + /** A type tree that represents an existing or inferred type */ case class TypeTree[+T <: Untyped]()(implicit @constructorOnly src: SourceFile) extends DenotingTree[T] with TypTree[T] { @@ -1093,6 +1098,7 @@ object Trees { type JavaSeqLiteral = Trees.JavaSeqLiteral[T] type Inlined = Trees.Inlined[T] type QuotedExpr = Trees.QuotedExpr[T] + type SplicedExpr = Trees.SplicedExpr[T] type TypeTree = Trees.TypeTree[T] type InferredTypeTree = Trees.InferredTypeTree[T] type SingletonTypeTree = Trees.SingletonTypeTree[T] @@ -1267,6 +1273,10 @@ object Trees { case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree))) } + def SplicedExpr(tree: Tree)(spliced: Tree, tpt: Tree, outerQuotes: Tree)(using Context): SplicedExpr = tree match { + case tree: SplicedExpr if (spliced eq tree.spliced) && (tpt eq tree.tpt) && (outerQuotes eq tree.outerQuotes) => tree + case _ => finalize(tree, untpd.SplicedExpr(spliced, tpt, outerQuotes)(sourceFile(tree))) + } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree case _ => finalize(tree, untpd.SingletonTypeTree(ref)(sourceFile(tree))) @@ -1372,6 +1382,8 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) + def SplicedExpr(tree: SplicedExpr)(spliced: Tree = tree.spliced, tpt: Tree = tree.tpt, outerQuotes: Tree = tree.outerQuotes)(using Context): SplicedExpr = + SplicedExpr(tree: Tree)(spliced, tpt, outerQuotes) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, args, content, tpt) @@ -1506,6 +1518,8 @@ object Trees { if (trees1 eq trees) tree else Thicket(trees1) case tree @ QuotedExpr(expr, tpt) => cpy.QuotedExpr(tree)(transform(expr), transform(tpt)) + case tree @ SplicedExpr(spliced, tpt, outerQuotes) => + cpy.SplicedExpr(tree)(transform(spliced), transform(tpt), transform(outerQuotes)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1649,6 +1663,8 @@ object Trees { this(x, ts) case QuotedExpr(expr, tpt) => this(this(x, expr), tpt) + case SplicedExpr(spliced, tpt, outerQuotes) => + this(this(this(x, spliced), tpt), outerQuotes) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 88a822876043..e7c3ba93f72c 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -173,6 +173,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr = ta.assignType(untpd.QuotedExpr(expr, tpt), tpt) + def SplicedExpr(spliced: Tree, tpt: Tree, outerQuotes: Tree)(using Context): SplicedExpr = + ta.assignType(untpd.SplicedExpr(spliced, tpt, outerQuotes), tpt) + def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 7088739ddef3..b63df5f6999b 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -402,6 +402,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt) + def SplicedExpr(spliced: Tree, tpt: Tree, outerQuotes: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(spliced, tpt, outerQuotes) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index d704c6eac771..a57ce2c00a5d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -673,6 +673,23 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(expr) .withSpan(tree.span) ) + case SplicedExpr(spliced, tpt, EmptyTree) => + pickleTree( + // scala.quoted.runtime.Expr.splice[]() + ref(defn.QuotedRuntime_exprSplice) + .appliedToTypeTree(tpt) + .appliedTo(spliced) + .withSpan(tree.span) + ) + case SplicedExpr(spliced, tpt, quotes) => + pickleTree( + // scala.quoted.runtime.Expr.nestedSplice[]()() + ref(defn.QuotedRuntime_exprNestedSplice) + .appliedToTypeTree(tpt) + .appliedTo(quotes) + .appliedTo(spliced) + .withSpan(tree.span) + ) case Hole(_, idx, args, _, tpt) => writeByte(HOLE) withLength { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index a982e389f468..eb0dbdbc8bbb 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1271,6 +1271,17 @@ class TreeUnpickler(reader: TastyReader, val TypeApply(_, targs) = fn: @unchecked QuotedExpr(args.head, targs.head) + def splicedExpr(fn: Tree, args: List[Tree]): Tree = + val TypeApply(_, targs) = fn: @unchecked + SplicedExpr(args.head, targs.head, EmptyTree) + + def nestedSpliceExpr(fn: Tree, args: List[Tree]): Tree = + fn match + case Apply(TypeApply(_, targs), outerQuotes :: Nil) => + SplicedExpr(args.head, targs.head, outerQuotes) + case _ => // nestedSplice[T](quotes) + tpd.Apply(fn, args) + def simplifyLub(tree: Tree): Tree = tree.overwriteType(tree.tpe.simplified) tree @@ -1288,6 +1299,8 @@ class TreeUnpickler(reader: TastyReader, val args = until(end)(readTree()) if fn.symbol.isConstructor then constructorApply(fn, args) else if fn.symbol == defn.QuotedRuntime_exprQuote then quotedExpr(fn, args) + else if fn.symbol == defn.QuotedRuntime_exprSplice then splicedExpr(fn, args) + else if fn.symbol == defn.QuotedRuntime_exprNestedSplice then nestedSpliceExpr(fn, args) else tpd.Apply(fn, args) case TYPEAPPLY => tpd.TypeApply(readTree(), until(end)(readTpt())) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index e4c503206abf..bb16cf7f84b3 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -815,22 +815,8 @@ class Inliner(val call: tpd.Tree)(using Context): super.typedValDef(vdef1, sym) override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree = - def cancelQuotes(tree: Tree): Tree = - tree match - case QuotedExpr(SplicedExpr(inner), _) => inner - case _ => tree val locked = ctx.typerState.ownedVars - val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match { - case res: Apply if res.symbol == defn.QuotedRuntime_exprSplice - && StagingLevel.level == 0 - && !hasInliningErrors => - val expanded = expandMacro(res.args.head, tree.srcPos) - transform.TreeChecker.checkMacroGeneratedTree(res, expanded) - typedExpr(expanded) // Inline calls and constant fold code generated by the macro - case res => - specializeEq(inlineIfNeeded(res, pt, locked)) - } - res + specializeEq(inlineIfNeeded(constToLiteral(BetaReduce(super.typedApply(tree, pt))), pt, locked)) override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree = val locked = ctx.typerState.ownedVars @@ -840,8 +826,21 @@ class Inliner(val call: tpd.Tree)(using Context): tree1 override def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = - ctx.compilationUnit.needsStaging = true - super.typedQuotedExpr(tree, pt) + super.typedQuotedExpr(tree, pt) match + case QuotedExpr(SplicedExpr(inner, _, _), _) => inner + case tree1 => + ctx.compilationUnit.needsStaging = true + tree1 + + override def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = + super.typedSplicedExpr(tree, pt) match + case tree1 @ SplicedExpr(spliced, tpt, EmptyTree) + if StagingLevel.level == 0 + && !hasInliningErrors => + val expanded = expandMacro(spliced, tree1.srcPos) + transform.TreeChecker.checkMacroGeneratedTree(tree1, expanded) + typedExpr(expanded) // Inline calls and constant fold code generated by the macro + case tree1 => tree1 override def typedMatch(tree: untpd.Match, pt: Type)(using Context): Tree = val tree1 = @@ -1079,7 +1078,7 @@ class Inliner(val call: tpd.Tree)(using Context): level += 1 try apply(syms, body) finally level -= 1 - case SplicedExpr(body) => + case SplicedExpr(body, _, _) => level -= 1 try apply(syms, body) finally level += 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 50c6a72a5b12..c26b9bd21362 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -93,7 +93,7 @@ object PrepareInlineable { private def stagingContext(tree: Tree)(using Context): Context = tree match case tree: QuotedExpr => StagingLevel.quoteContext case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext - case tree: Apply if tree.symbol.isExprSplice => StagingLevel.spliceContext + case tree: SplicedExpr => StagingLevel.spliceContext case _ => ctx } @@ -155,7 +155,7 @@ object PrepareInlineable { val qual = qualifier(refPart) inlining.println(i"adding receiver passing inline accessor for $tree/$refPart -> (${qual.tpe}, $refPart: ${refPart.getClass}, $argss%, %") - // Need to dealias in order to cagtch all possible references to abstracted over types in + // Need to dealias in order to catch all possible references to abstracted over types in // substitutions val dealiasMap = new TypeMap { def apply(t: Type) = mapOver(t.dealias) @@ -291,7 +291,7 @@ object PrepareInlineable { if (inlined.is(Macro) && !ctx.isAfterTyper) { def checkMacro(tree: Tree): Unit = tree match { - case SplicedExpr(code) => + case SplicedExpr(code, _, _) => if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 2f354259b33c..42ce138c52c7 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -432,8 +432,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { changePrec (GlobalPrec) { keywordStr("throw ") ~ toText(args.head) } - else if (!printDebug && fun.hasType && fun.symbol.isExprSplice) - keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}") else toTextLocal(fun) ~ "(" @@ -725,6 +723,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case QuotedExpr(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") + case SplicedExpr(spliced, tpt, quotes) => + val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) + val quotesText = (keywordStr("(using ") ~ toTextGlobal(quotes) ~ keywordStr(")")).provided(printDebug) + keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(spliced) ~ keywordStr("}") ~ quotesText case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index e432baf9068c..e514043d515b 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -143,20 +143,16 @@ class CrossStageSafety extends TreeMapWithStages { * - If inside inlined code, expand the macro code. * - If inside of a macro definition, check the validity of the macro. */ - protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree = { + protected def transformSplice(body: Tree, splice: SplicedExpr)(using Context): Tree = { val body1 = transform(body)(using spliceContext) - splice.fun match { - case fun @ TypeApply(_, _ :: Nil) => - // Type of the splice itself must also be healed - // `quoted.runtime.Expr.quote[F[T]](... T ...)` --> `internal.Quoted.expr[F[$t]](... T ...)` + val tpt1 = + if level == 0 then + transform(splice.tpt) + else val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) - cpy.Apply(splice)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), body1 :: Nil) - case f @ Apply(fun @ TypeApply(_, _), quotes :: Nil) => - // Type of the splice itself must also be healed - // `quoted.runtime.Expr.quote[F[T]](... T ...)` --> `internal.Quoted.expr[F[$t]](... T ...)` - val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) - cpy.Apply(splice)(cpy.Apply(f)(cpy.TypeApply(fun)(fun.fun, tpd.TypeTree(tp) :: Nil), quotes :: Nil), body1 :: Nil) - } + TypeTree(tp).withSpan(splice.tpt.span) + val outerQuotes1 = splice.outerQuotes + cpy.SplicedExpr(splice)(body1, tpt1, outerQuotes1) } protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index a35fa363aa6b..ac750677c6b2 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -37,7 +37,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args) /** Transform the expression splice `splice` which contains the spliced `body`. */ - protected def transformSplice(body: Tree, splice: Apply)(using Context): Tree + protected def transformSplice(body: Tree, splice: SplicedExpr)(using Context): Tree /** Transform the type splice `splice` which contains the spliced `body`. */ protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree @@ -66,7 +66,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { - case SplicedExpr(t) => + case SplicedExpr(t, _, _) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) @@ -75,14 +75,15 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } finally inQuoteOrSplice = old - case tree @ SplicedExpr(splicedTree) => + case tree @ SplicedExpr(splicedTree, _, _) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { case QuotedExpr(t, _) => // Optimization: `${ 'x }` --> `x` transform(t) - case _ => transformSplice(splicedTree, tree) + case _ => + transformSplice(splicedTree, tree) } finally inQuoteOrSplice = old diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 151e841f0e48..34be121070ea 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -159,6 +159,12 @@ class ElimByName extends MiniPhase, InfoTransformer: else tree } + override def transformOther(tree: Tree)(using Context): Tree = tree match + case tree @ SplicedExpr(spliced, tpt, outerQuotes) => + assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) + cpy.SplicedExpr(tree)(transformAllDeep(spliced), tpt, outerQuotes) + case tree => tree + object ElimByName: val name: String = "elimByName" val description: String = "map by-name parameters to functions" diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index a7e0795ce195..1094c3c0bd0a 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,6 +153,9 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformOther(tree: Tree)(using Context): Tree = tree match { case tree: Export => EmptyTree case tree: NamedArg => transformAllDeep(tree.arg) + case tree @ SplicedExpr(spliced, tpt, outerQuotes) => + assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) + cpy.SplicedExpr(tree)(transformAllDeep(spliced), transformAllDeep(tpt), outerQuotes) case tree => if (tree.isType) toTypeTree(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index a7644fa2a528..1fd719251421 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -50,7 +50,7 @@ class Inlining extends MacroTransform { traverseChildren(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => traverseChildren(tree)(using StagingLevel.quoteContext) - case _: GenericApply if tree.symbol.isExprSplice => + case _: SplicedExpr => traverseChildren(tree)(using StagingLevel.spliceContext) case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) @@ -104,7 +104,7 @@ class Inlining extends MacroTransform { super.transform(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => super.transform(tree)(using StagingLevel.quoteContext) - case _: GenericApply if tree.symbol.isExprSplice => + case _: SplicedExpr => super.transform(tree)(using StagingLevel.spliceContext) case _: PackageDef => super.transform(tree) match diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 6507bed179c3..c020a53e7fdf 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -85,9 +85,10 @@ class PickleQuotes extends MacroTransform { tree match case tree: QuotedExpr => assert(Inlines.inInlineMethod) + case tree: SplicedExpr => + assert(Inlines.inInlineMethod) case tree: RefTree if !Inlines.inInlineMethod => assert(tree.symbol != defn.QuotedTypeModule_of) - assert(!tree.symbol.isExprSplice) case _ : TypeDef if !Inlines.inInlineMethod => assert(!tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot), s"${tree.symbol} should have been removed by PickledQuotes because it has a @quoteTypeTag") diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 2981bdafc958..c448cb20900b 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -158,7 +158,7 @@ object Splicer { case Apply(Select(QuotedExpr(expr, tpt), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case SplicedExpr(_) => + case SplicedExpr(_, _, _) => report.error("Quoted argument of macros may not have splices", tree.srcPos) case _ => traverseChildren(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index f5e536d85a55..d25c1e9a9895 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -112,15 +112,15 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case Apply(fn, List(splicedCode)) if fn.symbol == defn.QuotedRuntime_exprNestedSplice => + case tree: SplicedExpr => if level > 1 then - val splicedCode1 = super.transform(splicedCode)(using spliceContext) - cpy.Apply(tree)(fn, List(splicedCode1)) + val spliced1 = super.transform(tree.spliced)(using spliceContext) + cpy.SplicedExpr(tree)(spliced1, tree.tpt, tree.outerQuotes) else val holeIdx = numHoles numHoles += 1 val splicer = SpliceTransformer(ctx.owner, quotedDefs.contains) - val newSplicedCode1 = splicer.transformSplice(splicedCode, tree.tpe, holeIdx)(using spliceContext) + val newSplicedCode1 = splicer.transformSplice(tree.spliced, tree.tpe, holeIdx)(using spliceContext) val newSplicedCode2 = Level0QuoteTransformer.transform(newSplicedCode1)(using spliceContext) newSplicedCode2 case tree: TypeDef if tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => @@ -235,9 +235,9 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case Apply(fn, args) if fn.symbol == defn.QuotedRuntime_exprNestedSplice => - val newArgs = args.mapConserve(arg => transform(arg)(using spliceContext)) - cpy.Apply(tree)(fn, newArgs) + case SplicedExpr(spliced, tpt, outerQuotes) => + val spliced1 = transform(spliced)(using spliceContext) + cpy.SplicedExpr(tree)(spliced1, tpt, outerQuotes) case Apply(sel @ Select(app @ QuotedExpr(expr, tpt), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => @@ -410,10 +410,7 @@ class Splicing extends MacroTransform: body(using ctx.withOwner(meth)).changeOwner(ctx.owner, meth) } }) - ref(defn.QuotedRuntime_exprNestedSplice) - .appliedToType(tpe) - .appliedTo(Literal(Constant(null))) // Dropped when creating the Hole that contains it - .appliedTo(closure) + SplicedExpr(closure, TypeTree(tpe), Literal(Constant(null))) private def quoted(expr: Tree)(using Context): Tree = QuotedExpr(expr, TypeTree(expr.tpe.widenTermRefExpr)) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index a5844dc0b538..feb841ba5c6c 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -315,10 +315,6 @@ object SymUtils: def reachableRawTypeRef(using Context) = self.reachableTypeRef.appliedTo(self.typeParams.map(_ => TypeBounds.emptyPolyKind)) - /** Is symbol a term splice operation? */ - def isExprSplice(using Context): Boolean = - self == defn.QuotedRuntime_exprSplice || self == defn.QuotedRuntime_exprNestedSplice - /** Is symbol a type splice operation? */ def isTypeSplice(using Context): Boolean = self == defn.QuotedType_splice diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index e8e0ad7cf4a2..e562470aea5e 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -88,7 +88,7 @@ trait QuotesAndSplices { using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(spliceOwner(ctx))) val baseType = pat.tpe.baseType(defn.QuotedExprClass) val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType - ref(defn.QuotedRuntime_exprSplice).appliedToType(argType).appliedTo(pat) + SplicedExpr(pat, TypeTree(argType), EmptyTree).withSpan(tree.span) } else { report.error(em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) @@ -108,11 +108,17 @@ trait QuotesAndSplices { val (outerQctx, ctx1) = popQuotes() val internalSplice = + untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) outerQctx match case Some(qctxRef) => untpd.Apply(untpd.Apply(untpd.ref(defn.QuotedRuntime_exprNestedSplice.termRef), qctxRef), tree.expr) case _ => untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) - typedApply(internalSplice, pt)(using ctx1).withSpan(tree.span) + typedApply(internalSplice, pt)(using ctx1).withSpan(tree.span) match + case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => + cpy.SplicedExpr(tree)(spliced, tpt, EmptyTree) + case tree @ Apply(Apply(TypeApply(_, tpt :: Nil), quotes :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprNestedSplice => + cpy.SplicedExpr(tree)(spliced, tpt, quotes) + case tree => tree } } @@ -228,12 +234,12 @@ trait QuotesAndSplices { val freshTypeBindingsBuff = new mutable.ListBuffer[Tree] val typePatBuf = new mutable.ListBuffer[Tree] override def transform(tree: Tree)(using Context) = tree match { - case Typed(Apply(fn, pat :: Nil), tpt) if fn.symbol.isExprSplice && !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => + case Typed(SplicedExpr(pat, _, outerQuotes), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => val tpt1 = transform(tpt) // Transform type bindings val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil) - val newSplice = ref(defn.QuotedRuntime_exprSplice).appliedToType(tpt1.tpe).appliedTo(Typed(pat, exprTpt)) + val newSplice = cpy.SplicedExpr(tree)(pat, tpt1, outerQuotes) transform(newSplice) - case Apply(TypeApply(fn, targs), Apply(sp, pat :: Nil) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => + case Apply(TypeApply(fn, targs), SplicedExpr(pat, _, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => args match // TODO support these patterns. Possibly using scala.quoted.util.Var case SeqLiteral(args, _) => for arg <- args; if arg.symbol.is(Mutable) do @@ -245,7 +251,7 @@ trait QuotesAndSplices { val pat1 = if (patType eq patType1) pat else pat.withType(patType1) patBuf += pat1 } - case Apply(fn, pat :: Nil) if fn.symbol.isExprSplice => + case SplicedExpr(pat, _, _) => try ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) finally { val patType = pat.tpe.widen diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 2d921f4dbcb1..9fe679a0583b 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -398,6 +398,9 @@ trait TypeAssigner { .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe)) tree.withType(lambdaType) + def assignType(tree: untpd.SplicedExpr, tpt: Tree)(using Context): SplicedExpr = + tree.withType(tpt.tpe) + def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(using Context): If = tree.withType(thenp.tpe | elsep.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index ddf16f213676..822f8e253932 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2049,7 +2049,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem) assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) - .withNotNullInfo(expr1.notNullInfo) + + def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val splicedType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) + val spliced1 = typed(tree.spliced, splicedType) + val outerQuotes1 = typed(tree.outerQuotes, defn.QuotesClass.typeRef) + assignType(cpy.SplicedExpr(tree)(spliced1, tpt1, outerQuotes1), tpt1) def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) @@ -3084,6 +3092,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree: untpd.Quote => typedQuote(tree, pt) case tree: untpd.QuotedExpr => typedQuotedExpr(tree, pt) case tree: untpd.Splice => typedSplice(tree, pt) + case tree: untpd.SplicedExpr => typedSplicedExpr(tree, pt) case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here case tree: untpd.Hole => typedHole(tree, pt) case _ => typedUnadapted(desugar(tree, pt), pt, locked) From fe64a3e6fc3c45acaf88ad40d73471a152ba5713 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 11:19:35 +0200 Subject: [PATCH 404/657] Remove splice outer quotes context This field is no longer used and can be removed. Removing it is backwards compatible with 3.0 and later releases. --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 20 +++++----- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 11 +----- .../tools/dotc/core/tasty/TreeUnpickler.scala | 6 +-- .../dotty/tools/dotc/inlines/Inliner.scala | 6 +-- .../dotc/inlines/PrepareInlineable.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 5 +-- .../dotty/tools/dotc/quoted/Interpreter.scala | 1 - .../tools/dotc/staging/CrossStageSafety.scala | 4 +- .../dotty/tools/dotc/staging/HealType.scala | 1 - .../tools/dotc/staging/QuoteContext.scala | 34 ----------------- .../dotc/staging/TreeMapWithStages.scala | 4 +- .../tools/dotc/transform/ElimByName.scala | 4 +- .../tools/dotc/transform/FirstTransform.scala | 4 +- .../dotty/tools/dotc/transform/Inlining.scala | 1 - .../dotty/tools/dotc/transform/Splicer.scala | 2 +- .../dotty/tools/dotc/transform/Splicing.scala | 9 ++--- .../dotty/tools/dotc/transform/Staging.scala | 1 - .../tools/dotc/typer/QuotesAndSplices.scala | 38 ++++++++----------- .../src/dotty/tools/dotc/typer/Typer.scala | 7 ++-- 21 files changed, 53 insertions(+), 113 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/staging/QuoteContext.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 63e4ab859899..dc37b1ef7180 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -682,7 +682,7 @@ object Trees { type ThisTree[+T <: Untyped] = QuotedExpr[T] } - case class SplicedExpr[+T <: Untyped] private[ast] (spliced: Tree[T], tpt: Tree[T], outerQuotes: Tree[T])(implicit @constructorOnly src: SourceFile) + case class SplicedExpr[+T <: Untyped] private[ast] (spliced: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = SplicedExpr[T] } @@ -1273,9 +1273,9 @@ object Trees { case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree))) } - def SplicedExpr(tree: Tree)(spliced: Tree, tpt: Tree, outerQuotes: Tree)(using Context): SplicedExpr = tree match { - case tree: SplicedExpr if (spliced eq tree.spliced) && (tpt eq tree.tpt) && (outerQuotes eq tree.outerQuotes) => tree - case _ => finalize(tree, untpd.SplicedExpr(spliced, tpt, outerQuotes)(sourceFile(tree))) + def SplicedExpr(tree: Tree)(spliced: Tree, tpt: Tree)(using Context): SplicedExpr = tree match { + case tree: SplicedExpr if (spliced eq tree.spliced) && (tpt eq tree.tpt) => tree + case _ => finalize(tree, untpd.SplicedExpr(spliced, tpt)(sourceFile(tree))) } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree @@ -1382,8 +1382,8 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) - def SplicedExpr(tree: SplicedExpr)(spliced: Tree = tree.spliced, tpt: Tree = tree.tpt, outerQuotes: Tree = tree.outerQuotes)(using Context): SplicedExpr = - SplicedExpr(tree: Tree)(spliced, tpt, outerQuotes) + def SplicedExpr(tree: SplicedExpr)(spliced: Tree = tree.spliced, tpt: Tree = tree.tpt)(using Context): SplicedExpr = + SplicedExpr(tree: Tree)(spliced, tpt) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, args, content, tpt) @@ -1518,8 +1518,8 @@ object Trees { if (trees1 eq trees) tree else Thicket(trees1) case tree @ QuotedExpr(expr, tpt) => cpy.QuotedExpr(tree)(transform(expr), transform(tpt)) - case tree @ SplicedExpr(spliced, tpt, outerQuotes) => - cpy.SplicedExpr(tree)(transform(spliced), transform(tpt), transform(outerQuotes)) + case tree @ SplicedExpr(spliced, tpt) => + cpy.SplicedExpr(tree)(transform(spliced), transform(tpt)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1663,8 +1663,8 @@ object Trees { this(x, ts) case QuotedExpr(expr, tpt) => this(this(x, expr), tpt) - case SplicedExpr(spliced, tpt, outerQuotes) => - this(this(this(x, spliced), tpt), outerQuotes) + case SplicedExpr(spliced, tpt) => + this(this(x, spliced), tpt) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index e7c3ba93f72c..bc2498e7ca9b 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -173,8 +173,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr = ta.assignType(untpd.QuotedExpr(expr, tpt), tpt) - def SplicedExpr(spliced: Tree, tpt: Tree, outerQuotes: Tree)(using Context): SplicedExpr = - ta.assignType(untpd.SplicedExpr(spliced, tpt, outerQuotes), tpt) + def SplicedExpr(spliced: Tree, tpt: Tree)(using Context): SplicedExpr = + ta.assignType(untpd.SplicedExpr(spliced, tpt), tpt) def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index b63df5f6999b..c8d083ef792d 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -402,7 +402,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt) - def SplicedExpr(spliced: Tree, tpt: Tree, outerQuotes: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(spliced, tpt, outerQuotes) + def SplicedExpr(spliced: Tree, tpt: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(spliced, tpt) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index a57ce2c00a5d..0762ea476021 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -673,7 +673,7 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(expr) .withSpan(tree.span) ) - case SplicedExpr(spliced, tpt, EmptyTree) => + case SplicedExpr(spliced, tpt) => pickleTree( // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) @@ -681,15 +681,6 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(spliced) .withSpan(tree.span) ) - case SplicedExpr(spliced, tpt, quotes) => - pickleTree( - // scala.quoted.runtime.Expr.nestedSplice[]()() - ref(defn.QuotedRuntime_exprNestedSplice) - .appliedToTypeTree(tpt) - .appliedTo(quotes) - .appliedTo(spliced) - .withSpan(tree.span) - ) case Hole(_, idx, args, _, tpt) => writeByte(HOLE) withLength { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index eb0dbdbc8bbb..153fc7b3922d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1273,12 +1273,12 @@ class TreeUnpickler(reader: TastyReader, def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - SplicedExpr(args.head, targs.head, EmptyTree) + SplicedExpr(args.head, targs.head) def nestedSpliceExpr(fn: Tree, args: List[Tree]): Tree = fn match - case Apply(TypeApply(_, targs), outerQuotes :: Nil) => - SplicedExpr(args.head, targs.head, outerQuotes) + case Apply(TypeApply(_, targs), _ :: Nil) => // nestedSplice[T](quotes)(expr) + SplicedExpr(args.head, targs.head) case _ => // nestedSplice[T](quotes) tpd.Apply(fn, args) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index bb16cf7f84b3..02e1bbdeacce 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -827,14 +827,14 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = super.typedQuotedExpr(tree, pt) match - case QuotedExpr(SplicedExpr(inner, _, _), _) => inner + case QuotedExpr(SplicedExpr(inner, _), _) => inner case tree1 => ctx.compilationUnit.needsStaging = true tree1 override def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = super.typedSplicedExpr(tree, pt) match - case tree1 @ SplicedExpr(spliced, tpt, EmptyTree) + case tree1 @ SplicedExpr(spliced, tpt) if StagingLevel.level == 0 && !hasInliningErrors => val expanded = expandMacro(spliced, tree1.srcPos) @@ -1078,7 +1078,7 @@ class Inliner(val call: tpd.Tree)(using Context): level += 1 try apply(syms, body) finally level -= 1 - case SplicedExpr(body, _, _) => + case SplicedExpr(body, _) => level -= 1 try apply(syms, body) finally level += 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index c26b9bd21362..b94b76316b1b 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -291,7 +291,7 @@ object PrepareInlineable { if (inlined.is(Macro) && !ctx.isAfterTyper) { def checkMacro(tree: Tree): Unit = tree match { - case SplicedExpr(code, _, _) => + case SplicedExpr(code, _) => if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 42ce138c52c7..0d899b8b5909 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -723,10 +723,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case QuotedExpr(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") - case SplicedExpr(spliced, tpt, quotes) => + case SplicedExpr(spliced, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) - val quotesText = (keywordStr("(using ") ~ toTextGlobal(quotes) ~ keywordStr(")")).provided(printDebug) - keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(spliced) ~ keywordStr("}") ~ quotesText + keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(spliced) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala index a98284f4078d..c9a77dbfa151 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Interpreter.scala @@ -24,7 +24,6 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.TypeErasure import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.quoted._ -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.typer.ImportInfo.withRootImports import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.reporting.Message diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index e514043d515b..512e5ab6ab4c 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -10,7 +10,6 @@ import dotty.tools.dotc.core.NameKinds._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags.* import dotty.tools.dotc.util.Property @@ -151,8 +150,7 @@ class CrossStageSafety extends TreeMapWithStages { else val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) TypeTree(tp).withSpan(splice.tpt.span) - val outerQuotes1 = splice.outerQuotes - cpy.SplicedExpr(splice)(body1, tpt1, outerQuotes1) + cpy.SplicedExpr(splice)(body1, tpt1) } protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 50c2584b2fd0..337c7e8c9b3b 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -7,7 +7,6 @@ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags.* import dotty.tools.dotc.transform.SymUtils._ diff --git a/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala b/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala deleted file mode 100644 index 8e25bba7110c..000000000000 --- a/compiler/src/dotty/tools/dotc/staging/QuoteContext.scala +++ /dev/null @@ -1,34 +0,0 @@ -package dotty.tools.dotc.staging - -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.Contexts._ -import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.util.Property -import dotty.tools.dotc.staging.StagingLevel.* - -object QuoteContext { - - /** A key to be used in a context property that tracks the quotation stack. - * Stack containing the Quotes references received by the surrounding quotes. - */ - private val QuotesStack = new Property.Key[List[tpd.Tree]] - - /** Context with an incremented quotation level and pushes a reference to a Quotes on the quote context stack */ - def pushQuotes(quotes: tpd.Tree)(using Context): Context = - val old = ctx.property(QuotesStack).getOrElse(List.empty) - quoteContext.setProperty(QuotesStack, quotes :: old) - - /** Context with a decremented quotation level and pops the Some of top of the quote context stack or None if the stack is empty. - * The quotation stack could be empty if we are in a top level splice or an erroneous splice directly within a top level splice. - */ - def popQuotes()(using Context): (Option[tpd.Tree], Context) = - val ctx1 = spliceContext - val head = - ctx.property(QuotesStack) match - case Some(x :: xs) => - ctx1.setProperty(QuotesStack, xs) - Some(x) - case _ => - None // Splice at level 0 or lower - (head, ctx1) -} diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index ac750677c6b2..1ed6d4c37e19 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -66,7 +66,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { - case SplicedExpr(t, _, _) => + case SplicedExpr(t, _) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) @@ -75,7 +75,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } finally inQuoteOrSplice = old - case tree @ SplicedExpr(splicedTree, _, _) => + case tree @ SplicedExpr(splicedTree, _) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 34be121070ea..6e264385fd3e 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -160,9 +160,9 @@ class ElimByName extends MiniPhase, InfoTransformer: } override def transformOther(tree: Tree)(using Context): Tree = tree match - case tree @ SplicedExpr(spliced, tpt, outerQuotes) => + case tree @ SplicedExpr(spliced, tpt) => assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.SplicedExpr(tree)(transformAllDeep(spliced), tpt, outerQuotes) + cpy.SplicedExpr(tree)(transformAllDeep(spliced), tpt) case tree => tree object ElimByName: diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 1094c3c0bd0a..7a9e3cc324d3 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,9 +153,9 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformOther(tree: Tree)(using Context): Tree = tree match { case tree: Export => EmptyTree case tree: NamedArg => transformAllDeep(tree.arg) - case tree @ SplicedExpr(spliced, tpt, outerQuotes) => + case tree @ SplicedExpr(spliced, tpt) => assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.SplicedExpr(tree)(transformAllDeep(spliced), transformAllDeep(tpt), outerQuotes) + cpy.SplicedExpr(tree)(transformAllDeep(spliced), transformAllDeep(tpt)) case tree => if (tree.isType) toTypeTree(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 1fd719251421..341633e30960 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -12,7 +12,6 @@ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.inlines.Inlines import dotty.tools.dotc.ast.TreeMapWithImplicits import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel import scala.collection.mutable.ListBuffer diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index c448cb20900b..2e6549bc8ec8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -158,7 +158,7 @@ object Splicer { case Apply(Select(QuotedExpr(expr, tpt), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case SplicedExpr(_, _, _) => + case SplicedExpr(_, _) => report.error("Quoted argument of macros may not have splices", tree.srcPos) case _ => traverseChildren(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index d25c1e9a9895..ed271f20c5bf 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -21,7 +21,6 @@ import dotty.tools.dotc.core.Names._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.config.ScalaRelease.* -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags @@ -115,7 +114,7 @@ class Splicing extends MacroTransform: case tree: SplicedExpr => if level > 1 then val spliced1 = super.transform(tree.spliced)(using spliceContext) - cpy.SplicedExpr(tree)(spliced1, tree.tpt, tree.outerQuotes) + cpy.SplicedExpr(tree)(spliced1, tree.tpt) else val holeIdx = numHoles numHoles += 1 @@ -235,9 +234,9 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case SplicedExpr(spliced, tpt, outerQuotes) => + case SplicedExpr(spliced, tpt) => val spliced1 = transform(spliced)(using spliceContext) - cpy.SplicedExpr(tree)(spliced1, tpt, outerQuotes) + cpy.SplicedExpr(tree)(spliced1, tpt) case Apply(sel @ Select(app @ QuotedExpr(expr, tpt), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => @@ -410,7 +409,7 @@ class Splicing extends MacroTransform: body(using ctx.withOwner(meth)).changeOwner(ctx.owner, meth) } }) - SplicedExpr(closure, TypeTree(tpe), Literal(Constant(null))) + SplicedExpr(closure, TypeTree(tpe)) private def quoted(expr: Tree)(using Context): Tree = QuotedExpr(expr, TypeTree(expr.tpe.widenTermRefExpr)) diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 4fb5f5910440..a025f86a1db6 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -10,7 +10,6 @@ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.transform.SymUtils._ -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.CrossStageSafety import dotty.tools.dotc.staging.HealType diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index e562470aea5e..1f3b5c758324 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -15,7 +15,6 @@ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.inlines.PrepareInlineable -import dotty.tools.dotc.staging.QuoteContext.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.typer.Implicits._ @@ -42,15 +41,15 @@ trait QuotesAndSplices { report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } - val qctx = inferImplicitArg(defn.QuotesClass.typeRef, tree.span) + val quotes = inferImplicitArg(defn.QuotesClass.typeRef, tree.span) - if qctx.tpe.isInstanceOf[SearchFailureType] then - report.error(missingArgMsg(qctx, defn.QuotesClass.typeRef, ""), ctx.source.atSpan(tree.span)) - else if !qctx.tpe.isStable then - report.error(em"Quotes require stable Quotes, but found non stable $qctx", qctx.srcPos) + if quotes.tpe.isInstanceOf[SearchFailureType] then + report.error(missingArgMsg(quotes, defn.QuotesClass.typeRef, ""), ctx.source.atSpan(tree.span)) + else if !quotes.tpe.isStable then + report.error(em"Quotes require stable Quotes, but found non stable $quotes", quotes.srcPos) if ctx.mode.is(Mode.Pattern) then - typedQuotePattern(tree, pt, qctx).withSpan(tree.span) + typedQuotePattern(tree, pt, quotes).withSpan(tree.span) else if tree.quoted.isType then val msg = em"""Quoted types `'[..]` can only be used in patterns. | @@ -60,9 +59,9 @@ trait QuotesAndSplices { EmptyTree else val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.quoted) - val quotedExpr = typedApply(exprQuoteTree, pt)(using pushQuotes(qctx)) match + val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => QuotedExpr(quotedExpr, tpt) - makeInlineable(quotedExpr.select(nme.apply).appliedTo(qctx).withSpan(tree.span)) + makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) } private def makeInlineable(tree: Tree)(using Context): Tree = @@ -88,7 +87,7 @@ trait QuotesAndSplices { using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(spliceOwner(ctx))) val baseType = pat.tpe.baseType(defn.QuotedExprClass) val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType - SplicedExpr(pat, TypeTree(argType), EmptyTree).withSpan(tree.span) + SplicedExpr(pat, TypeTree(argType)).withSpan(tree.span) } else { report.error(em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) @@ -105,19 +104,12 @@ trait QuotesAndSplices { markAsMacro(ctx) } - val (outerQctx, ctx1) = popQuotes() - val internalSplice = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) - outerQctx match - case Some(qctxRef) => untpd.Apply(untpd.Apply(untpd.ref(defn.QuotedRuntime_exprNestedSplice.termRef), qctxRef), tree.expr) - case _ => untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) - typedApply(internalSplice, pt)(using ctx1).withSpan(tree.span) match + typedApply(internalSplice, pt)(using spliceContext).withSpan(tree.span) match case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => - cpy.SplicedExpr(tree)(spliced, tpt, EmptyTree) - case tree @ Apply(Apply(TypeApply(_, tpt :: Nil), quotes :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprNestedSplice => - cpy.SplicedExpr(tree)(spliced, tpt, quotes) + cpy.SplicedExpr(tree)(spliced, tpt) case tree => tree } } @@ -234,12 +226,12 @@ trait QuotesAndSplices { val freshTypeBindingsBuff = new mutable.ListBuffer[Tree] val typePatBuf = new mutable.ListBuffer[Tree] override def transform(tree: Tree)(using Context) = tree match { - case Typed(SplicedExpr(pat, _, outerQuotes), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => + case Typed(SplicedExpr(pat, _), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => val tpt1 = transform(tpt) // Transform type bindings val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil) - val newSplice = cpy.SplicedExpr(tree)(pat, tpt1, outerQuotes) + val newSplice = cpy.SplicedExpr(tree)(pat, tpt1) transform(newSplice) - case Apply(TypeApply(fn, targs), SplicedExpr(pat, _, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => + case Apply(TypeApply(fn, targs), SplicedExpr(pat, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => args match // TODO support these patterns. Possibly using scala.quoted.util.Var case SeqLiteral(args, _) => for arg <- args; if arg.symbol.is(Mutable) do @@ -251,7 +243,7 @@ trait QuotesAndSplices { val pat1 = if (patType eq patType1) pat else pat.withType(patType1) patBuf += pat1 } - case SplicedExpr(pat, _, _) => + case SplicedExpr(pat, _) => try ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) finally { val patType = pat.tpe.widen diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 822f8e253932..b1d281f35976 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2047,7 +2047,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem) + val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using StagingLevel.quoteContext) assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = @@ -2055,9 +2055,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val splicedType = // Quotes ?=> Expr[T] defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val spliced1 = typed(tree.spliced, splicedType) - val outerQuotes1 = typed(tree.outerQuotes, defn.QuotesClass.typeRef) - assignType(cpy.SplicedExpr(tree)(spliced1, tpt1, outerQuotes1), tpt1) + val spliced1 = typed(tree.spliced, splicedType)(using StagingLevel.spliceContext) + assignType(cpy.SplicedExpr(tree)(spliced1, tpt1), tpt1) def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) From 161ef49f6be3a8a7adb1cdf835ae78abd9f95393 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 11:34:27 +0200 Subject: [PATCH 405/657] Rename `SplicedExpr.{spliced=>expr}` --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 20 +++++++++---------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 ++-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 4 ++-- .../dotty/tools/dotc/inlines/Inliner.scala | 4 ++-- .../tools/dotc/printing/RefinedPrinter.scala | 4 ++-- .../tools/dotc/transform/FirstTransform.scala | 4 ++-- .../dotty/tools/dotc/transform/Splicing.scala | 12 +++++------ .../src/dotty/tools/dotc/typer/Typer.scala | 4 ++-- 9 files changed, 29 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index dc37b1ef7180..b95e2a41ff09 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -682,7 +682,7 @@ object Trees { type ThisTree[+T <: Untyped] = QuotedExpr[T] } - case class SplicedExpr[+T <: Untyped] private[ast] (spliced: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + case class SplicedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = SplicedExpr[T] } @@ -1273,9 +1273,9 @@ object Trees { case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree))) } - def SplicedExpr(tree: Tree)(spliced: Tree, tpt: Tree)(using Context): SplicedExpr = tree match { - case tree: SplicedExpr if (spliced eq tree.spliced) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.SplicedExpr(spliced, tpt)(sourceFile(tree))) + def SplicedExpr(tree: Tree)(expr: Tree, tpt: Tree)(using Context): SplicedExpr = tree match { + case tree: SplicedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree + case _ => finalize(tree, untpd.SplicedExpr(expr, tpt)(sourceFile(tree))) } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree @@ -1382,8 +1382,8 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) - def SplicedExpr(tree: SplicedExpr)(spliced: Tree = tree.spliced, tpt: Tree = tree.tpt)(using Context): SplicedExpr = - SplicedExpr(tree: Tree)(spliced, tpt) + def SplicedExpr(tree: SplicedExpr)(expr: Tree = tree.expr, tpt: Tree = tree.tpt)(using Context): SplicedExpr = + SplicedExpr(tree: Tree)(expr, tpt) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, args, content, tpt) @@ -1518,8 +1518,8 @@ object Trees { if (trees1 eq trees) tree else Thicket(trees1) case tree @ QuotedExpr(expr, tpt) => cpy.QuotedExpr(tree)(transform(expr), transform(tpt)) - case tree @ SplicedExpr(spliced, tpt) => - cpy.SplicedExpr(tree)(transform(spliced), transform(tpt)) + case tree @ SplicedExpr(expr, tpt) => + cpy.SplicedExpr(tree)(transform(expr), transform(tpt)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1663,8 +1663,8 @@ object Trees { this(x, ts) case QuotedExpr(expr, tpt) => this(this(x, expr), tpt) - case SplicedExpr(spliced, tpt) => - this(this(x, spliced), tpt) + case SplicedExpr(expr, tpt) => + this(this(x, expr), tpt) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index bc2498e7ca9b..50b288f6ddc1 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -173,8 +173,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr = ta.assignType(untpd.QuotedExpr(expr, tpt), tpt) - def SplicedExpr(spliced: Tree, tpt: Tree)(using Context): SplicedExpr = - ta.assignType(untpd.SplicedExpr(spliced, tpt), tpt) + def SplicedExpr(expr: Tree, tpt: Tree)(using Context): SplicedExpr = + ta.assignType(untpd.SplicedExpr(expr, tpt), tpt) def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index c8d083ef792d..151851d89a43 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -402,7 +402,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt) - def SplicedExpr(spliced: Tree, tpt: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(spliced, tpt) + def SplicedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(expr, tpt) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 0762ea476021..f41d3b57f8f0 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -673,12 +673,12 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(expr) .withSpan(tree.span) ) - case SplicedExpr(spliced, tpt) => + case SplicedExpr(expr, tpt) => pickleTree( // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) .appliedToTypeTree(tpt) - .appliedTo(spliced) + .appliedTo(expr) .withSpan(tree.span) ) case Hole(_, idx, args, _, tpt) => diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 02e1bbdeacce..c03e86970d90 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -834,10 +834,10 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = super.typedSplicedExpr(tree, pt) match - case tree1 @ SplicedExpr(spliced, tpt) + case tree1 @ SplicedExpr(expr, tpt) if StagingLevel.level == 0 && !hasInliningErrors => - val expanded = expandMacro(spliced, tree1.srcPos) + val expanded = expandMacro(expr, tree1.srcPos) transform.TreeChecker.checkMacroGeneratedTree(tree1, expanded) typedExpr(expanded) // Inline calls and constant fold code generated by the macro case tree1 => tree1 diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 0d899b8b5909..be407ded2f82 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -723,9 +723,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case QuotedExpr(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") - case SplicedExpr(spliced, tpt) => + case SplicedExpr(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) - keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(spliced) ~ keywordStr("}") + keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 7a9e3cc324d3..cad54982d10d 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,9 +153,9 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformOther(tree: Tree)(using Context): Tree = tree match { case tree: Export => EmptyTree case tree: NamedArg => transformAllDeep(tree.arg) - case tree @ SplicedExpr(spliced, tpt) => + case tree @ SplicedExpr(expr, tpt) => assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.SplicedExpr(tree)(transformAllDeep(spliced), transformAllDeep(tpt)) + cpy.SplicedExpr(tree)(transformAllDeep(expr), transformAllDeep(tpt)) case tree => if (tree.isType) toTypeTree(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index ed271f20c5bf..ca3acb6b8fac 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -113,13 +113,13 @@ class Splicing extends MacroTransform: tree match case tree: SplicedExpr => if level > 1 then - val spliced1 = super.transform(tree.spliced)(using spliceContext) - cpy.SplicedExpr(tree)(spliced1, tree.tpt) + val expr1 = super.transform(tree.expr)(using spliceContext) + cpy.SplicedExpr(tree)(expr1, tree.tpt) else val holeIdx = numHoles numHoles += 1 val splicer = SpliceTransformer(ctx.owner, quotedDefs.contains) - val newSplicedCode1 = splicer.transformSplice(tree.spliced, tree.tpe, holeIdx)(using spliceContext) + val newSplicedCode1 = splicer.transformSplice(tree.expr, tree.tpe, holeIdx)(using spliceContext) val newSplicedCode2 = Level0QuoteTransformer.transform(newSplicedCode1)(using spliceContext) newSplicedCode2 case tree: TypeDef if tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => @@ -234,9 +234,9 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case SplicedExpr(spliced, tpt) => - val spliced1 = transform(spliced)(using spliceContext) - cpy.SplicedExpr(tree)(spliced1, tpt) + case SplicedExpr(expr, tpt) => + val expr1 = transform(expr)(using spliceContext) + cpy.SplicedExpr(tree)(expr1, tpt) case Apply(sel @ Select(app @ QuotedExpr(expr, tpt), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b1d281f35976..b13bc5ec0bea 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2055,8 +2055,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val splicedType = // Quotes ?=> Expr[T] defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val spliced1 = typed(tree.spliced, splicedType)(using StagingLevel.spliceContext) - assignType(cpy.SplicedExpr(tree)(spliced1, tpt1), tpt1) + val expr1 = typed(tree.expr, splicedType)(using StagingLevel.spliceContext) + assignType(cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) From cf30d30cc60bfc1d5a8e2e87b040ebbd3ffd98e3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 11:51:41 +0200 Subject: [PATCH 406/657] Use QuotedExpr instead of Quote trees --- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 9 ------- .../dotty/tools/dotc/parsing/Parsers.scala | 6 ++--- .../tools/dotc/printing/RefinedPrinter.scala | 4 +--- .../tools/dotc/typer/QuotesAndSplices.scala | 24 +++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 10 ++++---- 6 files changed, 23 insertions(+), 32 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index c1dd78451bae..bf24702e4036 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1986,7 +1986,7 @@ object desugar { trees foreach collect case Block(Nil, expr) => collect(expr) - case Quote(expr) => + case QuotedExpr(expr, _) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { case Splice(expr) => collect(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 151851d89a43..e2f0a44cea99 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -111,7 +111,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def isType: Boolean = !isTerm } case class Throw(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree - case class Quote(quoted: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree case class Splice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree { def isInBraces: Boolean = span.end != expr.span.end } @@ -624,10 +623,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: Throw if expr eq tree.expr => tree case _ => finalize(tree, untpd.Throw(expr)(tree.source)) } - def Quote(tree: Tree)(quoted: Tree)(using Context): Tree = tree match { - case tree: Quote if quoted eq tree.quoted => tree - case _ => finalize(tree, untpd.Quote(quoted)(tree.source)) - } def Splice(tree: Tree)(expr: Tree)(using Context): Tree = tree match { case tree: Splice if expr eq tree.expr => tree case _ => finalize(tree, untpd.Splice(expr)(tree.source)) @@ -713,8 +708,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.Tuple(tree)(transform(trees)) case Throw(expr) => cpy.Throw(tree)(transform(expr)) - case Quote(t) => - cpy.Quote(tree)(transform(t)) case Splice(expr) => cpy.Splice(tree)(transform(expr)) case ForYield(enums, expr) => @@ -774,8 +767,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(x, trees) case Throw(expr) => this(x, expr) - case Quote(t) => - this(x, t) case Splice(expr) => this(x, expr) case ForYield(enums, expr) => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 15a639743c15..12e038f560d3 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1262,7 +1262,7 @@ object Parsers { } } in.nextToken() - Quote(t) + QuotedExpr(t, EmptyTree) } else if !in.featureEnabled(Feature.symbolLiterals) then @@ -2494,10 +2494,10 @@ object Parsers { case QUOTE => atSpan(in.skipToken()) { withinStaged(StageKind.Quoted | (if (location.inPattern) StageKind.QuotedPattern else 0)) { - Quote { + val expr = if (in.token == LBRACKET) inBrackets(typ()) else stagedBlock() - } + QuotedExpr(expr, EmptyTree) } } case NEW => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index be407ded2f82..9910f471756e 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -712,8 +712,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } case Number(digits, kind) => digits - case Quote(tree) if tree.isTerm => - keywordStr("'{") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}") case Splice(tree) => keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}") case Thicket(trees) => @@ -721,7 +719,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) case QuotedExpr(expr, tpt) => - val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) + val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case SplicedExpr(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 1f3b5c758324..5e9279dbc819 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -34,9 +34,9 @@ trait QuotesAndSplices { /** Translate `'{ e }` into `scala.quoted.Expr.apply(e)` and `'[T]` into `scala.quoted.Type.apply[T]` * while tracking the quotation level in the context. */ - def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = { + def typedQuote(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = { record("typedQuote") - tree.quoted match { + tree.expr match { case untpd.Splice(innerExpr) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => @@ -50,7 +50,7 @@ trait QuotesAndSplices { if ctx.mode.is(Mode.Pattern) then typedQuotePattern(tree, pt, quotes).withSpan(tree.span) - else if tree.quoted.isType then + else if tree.expr.isType then val msg = em"""Quoted types `'[..]` can only be used in patterns. | |Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead. @@ -58,7 +58,7 @@ trait QuotesAndSplices { report.error(msg, tree.srcPos) EmptyTree else - val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.quoted) + val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.expr) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => QuotedExpr(quotedExpr, tpt) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) @@ -74,7 +74,7 @@ trait QuotesAndSplices { record("typedSplice") checkSpliceOutsideQuote(tree) tree.expr match { - case untpd.Quote(innerExpr) if innerExpr.isTerm => + case untpd.QuotedExpr(innerExpr, _) if innerExpr.isTerm => report.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.srcPos) return typed(innerExpr, pt) case _ => @@ -382,13 +382,13 @@ trait QuotesAndSplices { * ) => ... * ``` */ - private def typedQuotePattern(tree: untpd.Quote, pt: Type, qctx: Tree)(using Context): Tree = { - if tree.quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then + private def typedQuotePattern(tree: untpd.QuotedExpr, pt: Type, qctx: Tree)(using Context): Tree = { + val quoted = tree.expr + if quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then report.error("Quote pattern can only match scrutinees of type scala.quoted.Expr", tree.srcPos) - else if tree.quoted.isType && !pt.derivesFrom(defn.QuotedTypeClass) then + else if quoted.isType && !pt.derivesFrom(defn.QuotedTypeClass) then report.error("Quote pattern can only match scrutinees of type scala.quoted.Type", tree.srcPos) - val quoted = tree.quoted val exprPt = pt.baseType(if quoted.isType then defn.QuotedTypeClass else defn.QuotedExprClass) val quotedPt = exprPt.argInfos.headOption match { case Some(argPt: ValueType) => argPt // excludes TypeBounds @@ -440,12 +440,12 @@ trait QuotesAndSplices { if splices.isEmpty then ref(defn.EmptyTupleModule.termRef) else typed(untpd.Tuple(splices.map(x => untpd.TypedSplice(replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType) - val quoteClass = if (tree.quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass + val quoteClass = if (quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (tree.quoted.isTerm) tpd.QuotedExpr(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) + if (quoted.isTerm) tpd.QuotedExpr(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx) - val matchModule = if tree.quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch + val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch val unapplyFun = qctx.asInstance(defn.QuoteMatchingClass.typeRef).select(matchModule).select(nme.unapply) UnApply( diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b13bc5ec0bea..cf239ab4fc33 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2046,9 +2046,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using StagingLevel.quoteContext) - assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) + if tree.tpt.isEmpty then + typedQuote(tree, pt) + else + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using StagingLevel.quoteContext) + assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) @@ -3088,7 +3091,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree: untpd.ParsedTry => typedTry(tree, pt) case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree - case tree: untpd.Quote => typedQuote(tree, pt) case tree: untpd.QuotedExpr => typedQuotedExpr(tree, pt) case tree: untpd.Splice => typedSplice(tree, pt) case tree: untpd.SplicedExpr => typedSplicedExpr(tree, pt) From f0d758b2620106273eeb6387fdd6fc95d025e348 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 12:54:35 +0200 Subject: [PATCH 407/657] Use SplicedExpr instead of Splice trees --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 6 +++--- compiler/src/dotty/tools/dotc/ast/Trees.scala | 1 + compiler/src/dotty/tools/dotc/ast/untpd.scala | 11 ----------- .../src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 4 +--- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 6 +++--- compiler/src/dotty/tools/dotc/typer/Typer.scala | 16 +++++++++------- 8 files changed, 19 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index bf24702e4036..1c519a602d5a 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -338,9 +338,9 @@ object desugar { def quotedPattern(tree: untpd.Tree, expectedTpt: untpd.Tree)(using Context): untpd.Tree = { def adaptToExpectedTpt(tree: untpd.Tree): untpd.Tree = tree match { // Add the expected type as an ascription - case _: untpd.Splice => + case _: untpd.SplicedExpr => untpd.Typed(tree, expectedTpt).withSpan(tree.span) - case Typed(expr: untpd.Splice, tpt) => + case Typed(expr: untpd.SplicedExpr, tpt) => cpy.Typed(tree)(expr, untpd.makeAndType(tpt, expectedTpt).withSpan(tpt.span)) // Propagate down the expected type to the leafs of the expression @@ -1989,7 +1989,7 @@ object desugar { case QuotedExpr(expr, _) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { - case Splice(expr) => collect(expr) + case SplicedExpr(expr, _) => collect(expr) case _ => traverseChildren(tree) } }.traverse(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index b95e2a41ff09..9d487c46ff88 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -685,6 +685,7 @@ object Trees { case class SplicedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = SplicedExpr[T] + def isInBraces: Boolean = span.end != expr.span.end } /** A type tree that represents an existing or inferred type */ diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index e2f0a44cea99..b9f5bb751862 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -111,9 +111,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def isType: Boolean = !isTerm } case class Throw(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree - case class Splice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree { - def isInBraces: Boolean = span.end != expr.span.end - } case class ForYield(enums: List[Tree], expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree case class ForDo(enums: List[Tree], body: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree case class GenFrom(pat: Tree, expr: Tree, checkMode: GenCheckMode)(implicit @constructorOnly src: SourceFile) extends Tree @@ -623,10 +620,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: Throw if expr eq tree.expr => tree case _ => finalize(tree, untpd.Throw(expr)(tree.source)) } - def Splice(tree: Tree)(expr: Tree)(using Context): Tree = tree match { - case tree: Splice if expr eq tree.expr => tree - case _ => finalize(tree, untpd.Splice(expr)(tree.source)) - } def ForYield(tree: Tree)(enums: List[Tree], expr: Tree)(using Context): TermTree = tree match { case tree: ForYield if (enums eq tree.enums) && (expr eq tree.expr) => tree case _ => finalize(tree, untpd.ForYield(enums, expr)(tree.source)) @@ -708,8 +701,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.Tuple(tree)(transform(trees)) case Throw(expr) => cpy.Throw(tree)(transform(expr)) - case Splice(expr) => - cpy.Splice(tree)(transform(expr)) case ForYield(enums, expr) => cpy.ForYield(tree)(transform(enums), transform(expr)) case ForDo(enums, body) => @@ -767,8 +758,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(x, trees) case Throw(expr) => this(x, expr) - case Splice(expr) => - this(x, expr) case ForYield(enums, expr) => this(this(x, enums), expr) case ForDo(enums, body) => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 12e038f560d3..8fa5fe54bc28 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1764,7 +1764,7 @@ object Parsers { syntaxError(em"$msg\n\nHint: $hint", Span(start, in.lastOffset)) Ident(nme.ERROR.toTypeName) else - Splice(expr) + SplicedExpr(expr, EmptyTree) } /** SimpleType ::= SimpleLiteral diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 9910f471756e..5b8b367e21be 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -712,8 +712,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } case Number(digits, kind) => digits - case Splice(tree) => - keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}") case Thicket(trees) => "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => @@ -722,7 +720,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case SplicedExpr(expr, tpt) => - val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug) + val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 79d6501ccb2d..77c9eb519db1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1097,7 +1097,7 @@ trait Applications extends Compatibility { } else { val app = tree.fun match - case _: untpd.Splice if ctx.mode.is(Mode.QuotedPattern) => typedAppliedSplice(tree, pt) + case _: untpd.SplicedExpr if ctx.mode.is(Mode.QuotedPattern) => typedAppliedSplice(tree, pt) case _ => realApply app match { case Apply(fn @ Select(left, _), right :: Nil) if fn.hasType => diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 5e9279dbc819..a61798fab4b7 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -37,7 +37,7 @@ trait QuotesAndSplices { def typedQuote(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = { record("typedQuote") tree.expr match { - case untpd.Splice(innerExpr) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => + case untpd.SplicedExpr(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } @@ -70,7 +70,7 @@ trait QuotesAndSplices { } /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ - def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = { + def typedSplice(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = { record("typedSplice") checkSpliceOutsideQuote(tree) tree.expr match { @@ -123,7 +123,7 @@ trait QuotesAndSplices { */ def typedAppliedSplice(tree: untpd.Apply, pt: Type)(using Context): Tree = { assert(ctx.mode.is(Mode.QuotedPattern)) - val untpd.Apply(splice: untpd.Splice, args) = tree: @unchecked + val untpd.Apply(splice: untpd.SplicedExpr, args) = tree: @unchecked if !isFullyDefined(pt, ForceDegree.flipBottom) then report.error(em"Type must be fully defined.", splice.srcPos) tree.withType(UnspecifiedErrorType) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cf239ab4fc33..0a20335bc1d1 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2054,12 +2054,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val splicedType = // Quotes ?=> Expr[T] - defn.FunctionType(1, isContextual = true) - .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val expr1 = typed(tree.expr, splicedType)(using StagingLevel.spliceContext) - assignType(cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) + if tree.tpt.isEmpty then + typedSplice(tree, pt) + else + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val splicedType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) + val expr1 = typed(tree.expr, splicedType)(using StagingLevel.spliceContext) + assignType(cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) @@ -3092,7 +3095,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree case tree: untpd.QuotedExpr => typedQuotedExpr(tree, pt) - case tree: untpd.Splice => typedSplice(tree, pt) case tree: untpd.SplicedExpr => typedSplicedExpr(tree, pt) case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here case tree: untpd.Hole => typedHole(tree, pt) From c6adab8e942d5cb0d428e9a12690e02cb9c2ed33 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 13:03:33 +0200 Subject: [PATCH 408/657] Move typedQuotedExpr/typedSplicedExpr to QuotesAndSplices --- .../tools/dotc/typer/QuotesAndSplices.scala | 35 ++++++++++++++----- .../src/dotty/tools/dotc/typer/Typer.scala | 19 ---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index a61798fab4b7..d47c7abf3fc3 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -31,10 +31,33 @@ trait QuotesAndSplices { import tpd._ + def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = + if tree.tpt.isEmpty then + typedQuoteSyntactic(tree, pt) + else + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using quoteContext) + assignType(untpd.cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) + + def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = + if tree.tpt.isEmpty then + typedSpliceSyntactic(tree, pt) + else + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val splicedType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) + val expr1 = typed(tree.expr, splicedType)(using spliceContext) + assignType(untpd.cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) + + def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = + val tpt = typedType(tree.tpt) + assignType(tree, tpt) + /** Translate `'{ e }` into `scala.quoted.Expr.apply(e)` and `'[T]` into `scala.quoted.Type.apply[T]` * while tracking the quotation level in the context. */ - def typedQuote(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = { + private def typedQuoteSyntactic(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = { record("typedQuote") tree.expr match { case untpd.SplicedExpr(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => @@ -70,7 +93,7 @@ trait QuotesAndSplices { } /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ - def typedSplice(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = { + private def typedSpliceSyntactic(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = { record("typedSplice") checkSpliceOutsideQuote(tree) tree.expr match { @@ -130,7 +153,7 @@ trait QuotesAndSplices { else if splice.isInBraces then // ${x}(...) match an application val typedArgs = args.map(arg => typedExpr(arg)) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) - val splice1 = typedSplice(splice, defn.FunctionOf(argTypes, pt)) + val splice1 = typedSpliceSyntactic(splice, defn.FunctionOf(argTypes, pt)) Apply(splice1.select(nme.apply), typedArgs).withType(pt).withSpan(tree.span) else // $x(...) higher-order quasipattern val typedArgs = args.map { @@ -143,7 +166,7 @@ trait QuotesAndSplices { if args.isEmpty then report.error("Missing arguments for open pattern", tree.srcPos) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) - val typedPat = typedSplice(splice, defn.FunctionOf(argTypes, pt)) + val typedPat = typedSpliceSyntactic(splice, defn.FunctionOf(argTypes, pt)) ref(defn.QuotedRuntimePatterns_patternHigherOrderHole).appliedToType(pt).appliedTo(typedPat, SeqLiteral(typedArgs, TypeTree(defn.AnyType))) } @@ -165,10 +188,6 @@ trait QuotesAndSplices { using spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) pat.select(tpnme.Underlying) - def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = - val tpt = typedType(tree.tpt) - assignType(tree, tpt) - private def checkSpliceOutsideQuote(tree: untpd.Tree)(using Context): Unit = if (level == 0 && !ctx.owner.ownersIterator.exists(_.isInlineMethod)) report.error("Splice ${...} outside quotes '{...} or inline method", tree.srcPos) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0a20335bc1d1..aa0243146241 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2045,25 +2045,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer bindings1, expansion1) } - def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = - if tree.tpt.isEmpty then - typedQuote(tree, pt) - else - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using StagingLevel.quoteContext) - assignType(cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) - - def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = - if tree.tpt.isEmpty then - typedSplice(tree, pt) - else - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val splicedType = // Quotes ?=> Expr[T] - defn.FunctionType(1, isContextual = true) - .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val expr1 = typed(tree.expr, splicedType)(using StagingLevel.spliceContext) - assignType(cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) - def completeTypeTree(tree: untpd.TypeTree, pt: Type, original: untpd.Tree)(using Context): TypeTree = tree.withSpan(original.span).withAttachmentsFrom(original) .withType( From 3a045c4f6a63f7b5bcd04ae0a8c6163eef4800ce Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 13:18:56 +0200 Subject: [PATCH 409/657] Rename QuotedExpr to Quote and SplicedExpr to Splice --- .../dotty/tools/dotc/CompilationUnit.scala | 2 +- .../src/dotty/tools/dotc/ast/Desugar.scala | 8 ++-- compiler/src/dotty/tools/dotc/ast/Trees.scala | 40 +++++++++---------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 8 ++-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 4 +- .../tools/dotc/core/tasty/TreePickler.scala | 4 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 6 +-- .../dotty/tools/dotc/inlines/Inliner.scala | 16 ++++---- .../dotc/inlines/PrepareInlineable.scala | 6 +-- .../dotty/tools/dotc/parsing/Parsers.scala | 6 +-- .../tools/dotc/printing/RefinedPrinter.scala | 4 +- .../tools/dotc/staging/CrossStageSafety.scala | 8 ++-- .../dotc/staging/TreeMapWithStages.scala | 16 ++++---- .../tools/dotc/transform/ElimByName.scala | 4 +- .../tools/dotc/transform/FirstTransform.scala | 4 +- .../dotty/tools/dotc/transform/Inlining.scala | 8 ++-- .../tools/dotc/transform/PickleQuotes.scala | 6 +-- .../tools/dotc/transform/PostTyper.scala | 2 +- .../dotty/tools/dotc/transform/Splicer.scala | 12 +++--- .../dotty/tools/dotc/transform/Splicing.scala | 20 +++++----- .../tools/dotc/transform/patmat/Space.scala | 2 +- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 36 ++++++++--------- .../dotty/tools/dotc/typer/TypeAssigner.scala | 4 +- .../src/dotty/tools/dotc/typer/Typer.scala | 4 +- 25 files changed, 116 insertions(+), 116 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 897befdda4a5..9a41f685f6c3 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -157,7 +157,7 @@ object CompilationUnit { if tree.symbol.is(Flags.Inline) then containsInline = true tree match - case tpd.QuotedExpr(_, _) => + case tpd.Quote(_, _) => containsQuote = true case tree: tpd.Apply if tree.symbol == defn.QuotedTypeModule_of => containsQuote = true diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 1c519a602d5a..5c2410d3fbdc 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -338,9 +338,9 @@ object desugar { def quotedPattern(tree: untpd.Tree, expectedTpt: untpd.Tree)(using Context): untpd.Tree = { def adaptToExpectedTpt(tree: untpd.Tree): untpd.Tree = tree match { // Add the expected type as an ascription - case _: untpd.SplicedExpr => + case _: untpd.Splice => untpd.Typed(tree, expectedTpt).withSpan(tree.span) - case Typed(expr: untpd.SplicedExpr, tpt) => + case Typed(expr: untpd.Splice, tpt) => cpy.Typed(tree)(expr, untpd.makeAndType(tpt, expectedTpt).withSpan(tpt.span)) // Propagate down the expected type to the leafs of the expression @@ -1986,10 +1986,10 @@ object desugar { trees foreach collect case Block(Nil, expr) => collect(expr) - case QuotedExpr(expr, _) => + case Quote(expr, _) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { - case SplicedExpr(expr, _) => collect(expr) + case Splice(expr, _) => collect(expr) case _ => traverseChildren(tree) } }.traverse(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 9d487c46ff88..65a0cd6ba710 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -677,14 +677,14 @@ object Trees { override def isType = expansion.isType } - case class QuotedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Quote[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { - type ThisTree[+T <: Untyped] = QuotedExpr[T] + type ThisTree[+T <: Untyped] = Quote[T] } - case class SplicedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Splice[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { - type ThisTree[+T <: Untyped] = SplicedExpr[T] + type ThisTree[+T <: Untyped] = Splice[T] def isInBraces: Boolean = span.end != expr.span.end } @@ -1098,8 +1098,8 @@ object Trees { type SeqLiteral = Trees.SeqLiteral[T] type JavaSeqLiteral = Trees.JavaSeqLiteral[T] type Inlined = Trees.Inlined[T] - type QuotedExpr = Trees.QuotedExpr[T] - type SplicedExpr = Trees.SplicedExpr[T] + type Quote = Trees.Quote[T] + type Splice = Trees.Splice[T] type TypeTree = Trees.TypeTree[T] type InferredTypeTree = Trees.InferredTypeTree[T] type SingletonTypeTree = Trees.SingletonTypeTree[T] @@ -1270,13 +1270,13 @@ object Trees { case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree))) } - def QuotedExpr(tree: Tree)(expr: Tree, tpt: Tree)(using Context): QuotedExpr = tree match { - case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree))) + def Quote(tree: Tree)(expr: Tree, tpt: Tree)(using Context): Quote = tree match { + case tree: Quote if (expr eq tree.expr) && (tpt eq tree.tpt) => tree + case _ => finalize(tree, untpd.Quote(expr, tpt)(sourceFile(tree))) } - def SplicedExpr(tree: Tree)(expr: Tree, tpt: Tree)(using Context): SplicedExpr = tree match { - case tree: SplicedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.SplicedExpr(expr, tpt)(sourceFile(tree))) + def Splice(tree: Tree)(expr: Tree, tpt: Tree)(using Context): Splice = tree match { + case tree: Splice if (expr eq tree.expr) && (tpt eq tree.tpt) => tree + case _ => finalize(tree, untpd.Splice(expr, tpt)(sourceFile(tree))) } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree @@ -1383,8 +1383,8 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) - def SplicedExpr(tree: SplicedExpr)(expr: Tree = tree.expr, tpt: Tree = tree.tpt)(using Context): SplicedExpr = - SplicedExpr(tree: Tree)(expr, tpt) + def Splice(tree: Splice)(expr: Tree = tree.expr, tpt: Tree = tree.tpt)(using Context): Splice = + Splice(tree: Tree)(expr, tpt) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, args, content, tpt) @@ -1517,10 +1517,10 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case tree @ QuotedExpr(expr, tpt) => - cpy.QuotedExpr(tree)(transform(expr), transform(tpt)) - case tree @ SplicedExpr(expr, tpt) => - cpy.SplicedExpr(tree)(transform(expr), transform(tpt)) + case tree @ Quote(expr, tpt) => + cpy.Quote(tree)(transform(expr), transform(tpt)) + case tree @ Splice(expr, tpt) => + cpy.Splice(tree)(transform(expr), transform(tpt)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1662,9 +1662,9 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) - case QuotedExpr(expr, tpt) => + case Quote(expr, tpt) => this(this(x, expr), tpt) - case SplicedExpr(expr, tpt) => + case Splice(expr, tpt) => this(this(x, expr), tpt) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 50b288f6ddc1..66c0bedc12c3 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,11 +170,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr = - ta.assignType(untpd.QuotedExpr(expr, tpt), tpt) + def Quote(expr: Tree, tpt: Tree)(using Context): Quote = + ta.assignType(untpd.Quote(expr, tpt), tpt) - def SplicedExpr(expr: Tree, tpt: Tree)(using Context): SplicedExpr = - ta.assignType(untpd.SplicedExpr(expr, tpt), tpt) + def Splice(expr: Tree, tpt: Tree)(using Context): Splice = + ta.assignType(untpd.Splice(expr, tpt), tpt) def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index b9f5bb751862..45b2fcd49595 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -397,8 +397,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): SeqLiteral = new SeqLiteral(elems, elemtpt) def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) - def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt) - def SplicedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): SplicedExpr = new SplicedExpr(expr, tpt) + def Quote(expr: Tree, tpt: Tree)(implicit src: SourceFile): Quote = new Quote(expr, tpt) + def Splice(expr: Tree, tpt: Tree)(implicit src: SourceFile): Splice = new Splice(expr, tpt) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index f41d3b57f8f0..864004a3e5ec 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,7 +665,7 @@ class TreePickler(pickler: TastyPickler) { pickleTree(hi) pickleTree(alias) } - case QuotedExpr(expr, tpt) => + case Quote(expr, tpt) => pickleTree( // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) @@ -673,7 +673,7 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(expr) .withSpan(tree.span) ) - case SplicedExpr(expr, tpt) => + case Splice(expr, tpt) => pickleTree( // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 153fc7b3922d..79d2008eb698 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1269,16 +1269,16 @@ class TreeUnpickler(reader: TastyReader, def quotedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - QuotedExpr(args.head, targs.head) + Quote(args.head, targs.head) def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - SplicedExpr(args.head, targs.head) + Splice(args.head, targs.head) def nestedSpliceExpr(fn: Tree, args: List[Tree]): Tree = fn match case Apply(TypeApply(_, targs), _ :: Nil) => // nestedSplice[T](quotes)(expr) - SplicedExpr(args.head, targs.head) + Splice(args.head, targs.head) case _ => // nestedSplice[T](quotes) tpd.Apply(fn, args) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index c03e86970d90..e2fd4c6688c1 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -825,16 +825,16 @@ class Inliner(val call: tpd.Tree)(using Context): ctx.compilationUnit.needsStaging = true tree1 - override def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = - super.typedQuotedExpr(tree, pt) match - case QuotedExpr(SplicedExpr(inner, _), _) => inner + override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = + super.typedQuote(tree, pt) match + case Quote(Splice(inner, _), _) => inner case tree1 => ctx.compilationUnit.needsStaging = true tree1 - override def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = - super.typedSplicedExpr(tree, pt) match - case tree1 @ SplicedExpr(expr, tpt) + override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = + super.typedSplice(tree, pt) match + case tree1 @ Splice(expr, tpt) if StagingLevel.level == 0 && !hasInliningErrors => val expanded = expandMacro(expr, tree1.srcPos) @@ -1070,7 +1070,7 @@ class Inliner(val call: tpd.Tree)(using Context): else tree match { case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) - case QuotedExpr(body, _) => + case Quote(body, _) => level += 1 try apply(syms, body) finally level -= 1 @@ -1078,7 +1078,7 @@ class Inliner(val call: tpd.Tree)(using Context): level += 1 try apply(syms, body) finally level -= 1 - case SplicedExpr(body, _) => + case Splice(body, _) => level -= 1 try apply(syms, body) finally level += 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index b94b76316b1b..11d4d9881230 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -91,9 +91,9 @@ object PrepareInlineable { } private def stagingContext(tree: Tree)(using Context): Context = tree match - case tree: QuotedExpr => StagingLevel.quoteContext + case tree: Quote => StagingLevel.quoteContext case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext - case tree: SplicedExpr => StagingLevel.spliceContext + case tree: Splice => StagingLevel.spliceContext case _ => ctx } @@ -291,7 +291,7 @@ object PrepareInlineable { if (inlined.is(Macro) && !ctx.isAfterTyper) { def checkMacro(tree: Tree): Unit = tree match { - case SplicedExpr(code, _) => + case Splice(code, _) => if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 8fa5fe54bc28..c2e2e4466d98 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1262,7 +1262,7 @@ object Parsers { } } in.nextToken() - QuotedExpr(t, EmptyTree) + Quote(t, EmptyTree) } else if !in.featureEnabled(Feature.symbolLiterals) then @@ -1764,7 +1764,7 @@ object Parsers { syntaxError(em"$msg\n\nHint: $hint", Span(start, in.lastOffset)) Ident(nme.ERROR.toTypeName) else - SplicedExpr(expr, EmptyTree) + Splice(expr, EmptyTree) } /** SimpleType ::= SimpleLiteral @@ -2497,7 +2497,7 @@ object Parsers { val expr = if (in.token == LBRACKET) inBrackets(typ()) else stagedBlock() - QuotedExpr(expr, EmptyTree) + Quote(expr, EmptyTree) } } case NEW => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 5b8b367e21be..b2b9196b0a13 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -716,10 +716,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) - case QuotedExpr(expr, tpt) => + case Quote(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") - case SplicedExpr(expr, tpt) => + case Splice(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 512e5ab6ab4c..748b053d7eaa 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -97,14 +97,14 @@ class CrossStageSafety extends TreeMapWithStages { } /** Transform quoted trees while maintaining level correctness */ - override protected def transformQuotedExpr(body: Tree, quote: QuotedExpr)(using Context): Tree = { + override protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) val transformedBody = transformQuoteBody(body, quote.span) val stripAnnotsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) val tpt1 = TypeTree(healType(quote.tpt.srcPos)(stripAnnotsDeep(quote.tpt.tpe))) - cpy.QuotedExpr(quote)(transformedBody, tpt1) + cpy.Quote(quote)(transformedBody, tpt1) } override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { @@ -142,7 +142,7 @@ class CrossStageSafety extends TreeMapWithStages { * - If inside inlined code, expand the macro code. * - If inside of a macro definition, check the validity of the macro. */ - protected def transformSplice(body: Tree, splice: SplicedExpr)(using Context): Tree = { + protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree = { val body1 = transform(body)(using spliceContext) val tpt1 = if level == 0 then @@ -150,7 +150,7 @@ class CrossStageSafety extends TreeMapWithStages { else val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) TypeTree(tp).withSpan(splice.tpt.span) - cpy.SplicedExpr(splice)(body1, tpt1) + cpy.Splice(splice)(body1, tpt1) } protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 1ed6d4c37e19..a292b4b079be 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -25,8 +25,8 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { * * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` */ - protected def transformQuotedExpr(body: Tree, quote: QuotedExpr)(using Context): Tree = - cpy.QuotedExpr(quote)(body, quote.tpt) + protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = + cpy.Quote(quote)(body, quote.tpt) /** Transform the quote `quote` which contains the quoted `body`. * @@ -37,7 +37,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args) /** Transform the expression splice `splice` which contains the spliced `body`. */ - protected def transformSplice(body: Tree, splice: SplicedExpr)(using Context): Tree + protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree /** Transform the type splice `splice` which contains the spliced `body`. */ protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree @@ -62,24 +62,24 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { try transformQuotedType(quotedTree, tree) finally inQuoteOrSplice = old - case tree @ QuotedExpr(quotedTree, _) => + case tree @ Quote(quotedTree, _) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { - case SplicedExpr(t, _) => + case Splice(t, _) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) case _ => - transformQuotedExpr(quotedTree, tree) + transformQuote(quotedTree, tree) } finally inQuoteOrSplice = old - case tree @ SplicedExpr(splicedTree, _) => + case tree @ Splice(splicedTree, _) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { - case QuotedExpr(t, _) => + case Quote(t, _) => // Optimization: `${ 'x }` --> `x` transform(t) case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 6e264385fd3e..976df4f436f7 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -160,9 +160,9 @@ class ElimByName extends MiniPhase, InfoTransformer: } override def transformOther(tree: Tree)(using Context): Tree = tree match - case tree @ SplicedExpr(spliced, tpt) => + case tree @ Splice(spliced, tpt) => assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.SplicedExpr(tree)(transformAllDeep(spliced), tpt) + cpy.Splice(tree)(transformAllDeep(spliced), tpt) case tree => tree object ElimByName: diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index cad54982d10d..9d874bf6b00f 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,9 +153,9 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformOther(tree: Tree)(using Context): Tree = tree match { case tree: Export => EmptyTree case tree: NamedArg => transformAllDeep(tree.arg) - case tree @ SplicedExpr(expr, tpt) => + case tree @ Splice(expr, tpt) => assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.SplicedExpr(tree)(transformAllDeep(expr), transformAllDeep(tpt)) + cpy.Splice(tree)(transformAllDeep(expr), transformAllDeep(tpt)) case tree => if (tree.isType) toTypeTree(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 341633e30960..60714d273bf0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -45,11 +45,11 @@ class Inlining extends MacroTransform { new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case _: QuotedExpr => + case _: Quote => traverseChildren(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => traverseChildren(tree)(using StagingLevel.quoteContext) - case _: SplicedExpr => + case _: Splice => traverseChildren(tree)(using StagingLevel.spliceContext) case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) @@ -99,11 +99,11 @@ class Inlining extends MacroTransform { val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 else Inlines.inlineCall(tree1) - case _: QuotedExpr => + case _: Quote => super.transform(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => super.transform(tree)(using StagingLevel.quoteContext) - case _: SplicedExpr => + case _: Splice => super.transform(tree)(using StagingLevel.spliceContext) case _: PackageDef => super.transform(tree) match diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index c020a53e7fdf..c60562baaa7f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -83,9 +83,9 @@ class PickleQuotes extends MacroTransform { override def checkPostCondition(tree: Tree)(using Context): Unit = tree match - case tree: QuotedExpr => + case tree: Quote => assert(Inlines.inInlineMethod) - case tree: SplicedExpr => + case tree: Splice => assert(Inlines.inInlineMethod) case tree: RefTree if !Inlines.inInlineMethod => assert(tree.symbol != defn.QuotedTypeModule_of) @@ -100,7 +100,7 @@ class PickleQuotes extends MacroTransform { protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case Apply(Select(QuotedExpr(expr, tpt), nme.apply), List(quotes)) => + case Apply(Select(Quote(expr, tpt), nme.apply), List(quotes)) => val (contents, codeWithHoles) = makeHoles(expr) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 5cfa74fd5610..eac9a8d70654 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -485,7 +485,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) => superAcc.withInvalidCurrentClass(super.transform(tree)) - case QuotedExpr(expr, _) => + case Quote(expr, _) => ctx.compilationUnit.needsStaging = true super.transform(tree) case tree => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 2e6549bc8ec8..4b6d434d80b0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -44,7 +44,7 @@ object Splicer { * See: `Staging` */ def splice(tree: Tree, splicePos: SrcPos, spliceExpansionPos: SrcPos, classLoader: ClassLoader)(using Context): Tree = tree match { - case QuotedExpr(quotedTree, _) => quotedTree + case Quote(quotedTree, _) => quotedTree case _ => val macroOwner = newSymbol(ctx.owner, nme.MACROkw, Macro | Synthetic, defn.AnyType, coord = tree.span) try @@ -136,7 +136,7 @@ object Splicer { * See: `Staging` */ def checkValidMacroBody(tree: Tree)(using Context): Unit = tree match { - case QuotedExpr(_, _) => // ok + case Quote(_, _) => // ok case _ => type Env = Set[Symbol] @@ -155,10 +155,10 @@ object Splicer { case Block(Nil, expr) => checkIfValidArgument(expr) case Typed(expr, _) => checkIfValidArgument(expr) - case Apply(Select(QuotedExpr(expr, tpt), nme.apply), _) => + case Apply(Select(Quote(expr, tpt), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case SplicedExpr(_, _) => + case Splice(_, _) => report.error("Quoted argument of macros may not have splices", tree.srcPos) case _ => traverseChildren(tree) @@ -203,7 +203,7 @@ object Splicer { case Typed(expr, _) => checkIfValidStaticCall(expr) - case Apply(Select(QuotedExpr(quoted, tpt), nme.apply), _) => + case Apply(Select(Quote(quoted, tpt), nme.apply), _) => // OK, canceled and warning emitted case Call(fn, args) @@ -240,7 +240,7 @@ object Splicer { override protected def interpretTree(tree: Tree)(implicit env: Env): Object = tree match { // Interpret level -1 quoted code `'{...}` (assumed without level 0 splices) - case Apply(Select(QuotedExpr(expr, _), nme.apply), _) => + case Apply(Select(Quote(expr, _), nme.apply), _) => val expr1 = expr match { case expr: Ident if expr.symbol.isAllOf(InlineByNameProxy) => // inline proxy for by-name parameter diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index ca3acb6b8fac..af77588419b5 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -86,7 +86,7 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = assert(level == 0) tree match - case Apply(Select(QuotedExpr(expr, _), nme.apply),List(quotes)) => + case Apply(Select(Quote(expr, _), nme.apply),List(quotes)) => QuoteTransformer().transform(tree) case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of => QuoteTransformer().transform(tree) @@ -111,10 +111,10 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case tree: SplicedExpr => + case tree: Splice => if level > 1 then val expr1 = super.transform(tree.expr)(using spliceContext) - cpy.SplicedExpr(tree)(expr1, tree.tpt) + cpy.Splice(tree)(expr1, tree.tpt) else val holeIdx = numHoles numHoles += 1 @@ -134,7 +134,7 @@ class Splicing extends MacroTransform: typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) - case Apply(Select(QuotedExpr(expr, tpt), nme.apply),List(quotes)) => + case Apply(Select(Quote(expr, tpt), nme.apply),List(quotes)) => super.transform(tree)(using quoteContext) case _: Template => for sym <- tree.symbol.owner.info.decls do @@ -234,10 +234,10 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case SplicedExpr(expr, tpt) => + case Splice(expr, tpt) => val expr1 = transform(expr)(using spliceContext) - cpy.SplicedExpr(tree)(expr1, tpt) - case Apply(sel @ Select(app @ QuotedExpr(expr, tpt), nme.apply), quotesArgs) => + cpy.Splice(tree)(expr1, tpt) + case Apply(sel @ Select(app @ Quote(expr, tpt), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => capturedTerm(expr) @@ -246,7 +246,7 @@ class Splicing extends MacroTransform: if level > 1 then transform(expr)(using quoteContext) else transformLevel0QuoteContent(expr)(using quoteContext) } - cpy.Apply(tree)(cpy.Select(sel)(cpy.QuotedExpr(app)(newExpr, tpt), nme.apply), quotesArgs) + cpy.Apply(tree)(cpy.Select(sel)(cpy.Quote(app)(newExpr, tpt), nme.apply), quotesArgs) case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => val newContent = capturedPartTypes(tpt) @@ -409,10 +409,10 @@ class Splicing extends MacroTransform: body(using ctx.withOwner(meth)).changeOwner(ctx.owner, meth) } }) - SplicedExpr(closure, TypeTree(tpe)) + Splice(closure, TypeTree(tpe)) private def quoted(expr: Tree)(using Context): Tree = - QuotedExpr(expr, TypeTree(expr.tpe.widenTermRefExpr)) + Quote(expr, TypeTree(expr.tpe.widenTermRefExpr)) .select(nme.apply) .appliedTo(quotes.nn) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index f7518ec8b9f5..23c851a33408 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -312,7 +312,7 @@ object SpaceEngine { def isIrrefutableQuotedPattern(unapp: tpd.Tree, implicits: List[tpd.Tree], pt: Type)(using Context): Boolean = { implicits.headOption match // pattern '{ $x: T } - case Some(tpd.Apply(tpd.Select(tpd.QuotedExpr(tpd.TypeApply(fn, List(tpt)), _), nme.apply), _)) + case Some(tpd.Apply(tpd.Select(tpd.Quote(tpd.TypeApply(fn, List(tpt)), _), nme.apply), _)) if unapp.symbol.owner.eq(defn.QuoteMatching_ExprMatchModule) && fn.symbol.eq(defn.QuotedRuntimePatterns_patternHole) => pt <:< defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 77c9eb519db1..79d6501ccb2d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1097,7 +1097,7 @@ trait Applications extends Compatibility { } else { val app = tree.fun match - case _: untpd.SplicedExpr if ctx.mode.is(Mode.QuotedPattern) => typedAppliedSplice(tree, pt) + case _: untpd.Splice if ctx.mode.is(Mode.QuotedPattern) => typedAppliedSplice(tree, pt) case _ => realApply app match { case Apply(fn @ Select(left, _), right :: Nil) if fn.hasType => diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index d47c7abf3fc3..9ec4a8648695 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -31,15 +31,15 @@ trait QuotesAndSplices { import tpd._ - def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = + def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = if tree.tpt.isEmpty then typedQuoteSyntactic(tree, pt) else val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using quoteContext) - assignType(untpd.cpy.QuotedExpr(tree)(expr1, tpt1), tpt1) + assignType(untpd.cpy.Quote(tree)(expr1, tpt1), tpt1) - def typedSplicedExpr(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = + def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = if tree.tpt.isEmpty then typedSpliceSyntactic(tree, pt) else @@ -48,7 +48,7 @@ trait QuotesAndSplices { defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) val expr1 = typed(tree.expr, splicedType)(using spliceContext) - assignType(untpd.cpy.SplicedExpr(tree)(expr1, tpt1), tpt1) + assignType(untpd.cpy.Splice(tree)(expr1, tpt1), tpt1) def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = val tpt = typedType(tree.tpt) @@ -57,10 +57,10 @@ trait QuotesAndSplices { /** Translate `'{ e }` into `scala.quoted.Expr.apply(e)` and `'[T]` into `scala.quoted.Type.apply[T]` * while tracking the quotation level in the context. */ - private def typedQuoteSyntactic(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree = { + private def typedQuoteSyntactic(tree: untpd.Quote, pt: Type)(using Context): Tree = { record("typedQuote") tree.expr match { - case untpd.SplicedExpr(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => + case untpd.Splice(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } @@ -83,7 +83,7 @@ trait QuotesAndSplices { else val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.expr) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match - case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => QuotedExpr(quotedExpr, tpt) + case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) } @@ -93,11 +93,11 @@ trait QuotesAndSplices { } /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ - private def typedSpliceSyntactic(tree: untpd.SplicedExpr, pt: Type)(using Context): Tree = { + private def typedSpliceSyntactic(tree: untpd.Splice, pt: Type)(using Context): Tree = { record("typedSplice") checkSpliceOutsideQuote(tree) tree.expr match { - case untpd.QuotedExpr(innerExpr, _) if innerExpr.isTerm => + case untpd.Quote(innerExpr, _) if innerExpr.isTerm => report.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.srcPos) return typed(innerExpr, pt) case _ => @@ -110,7 +110,7 @@ trait QuotesAndSplices { using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(spliceOwner(ctx))) val baseType = pat.tpe.baseType(defn.QuotedExprClass) val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType - SplicedExpr(pat, TypeTree(argType)).withSpan(tree.span) + Splice(pat, TypeTree(argType)).withSpan(tree.span) } else { report.error(em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) @@ -132,7 +132,7 @@ trait QuotesAndSplices { typedApply(internalSplice, pt)(using spliceContext).withSpan(tree.span) match case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => - cpy.SplicedExpr(tree)(spliced, tpt) + cpy.Splice(tree)(spliced, tpt) case tree => tree } } @@ -146,7 +146,7 @@ trait QuotesAndSplices { */ def typedAppliedSplice(tree: untpd.Apply, pt: Type)(using Context): Tree = { assert(ctx.mode.is(Mode.QuotedPattern)) - val untpd.Apply(splice: untpd.SplicedExpr, args) = tree: @unchecked + val untpd.Apply(splice: untpd.Splice, args) = tree: @unchecked if !isFullyDefined(pt, ForceDegree.flipBottom) then report.error(em"Type must be fully defined.", splice.srcPos) tree.withType(UnspecifiedErrorType) @@ -245,12 +245,12 @@ trait QuotesAndSplices { val freshTypeBindingsBuff = new mutable.ListBuffer[Tree] val typePatBuf = new mutable.ListBuffer[Tree] override def transform(tree: Tree)(using Context) = tree match { - case Typed(SplicedExpr(pat, _), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => + case Typed(Splice(pat, _), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => val tpt1 = transform(tpt) // Transform type bindings val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil) - val newSplice = cpy.SplicedExpr(tree)(pat, tpt1) + val newSplice = cpy.Splice(tree)(pat, tpt1) transform(newSplice) - case Apply(TypeApply(fn, targs), SplicedExpr(pat, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => + case Apply(TypeApply(fn, targs), Splice(pat, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => args match // TODO support these patterns. Possibly using scala.quoted.util.Var case SeqLiteral(args, _) => for arg <- args; if arg.symbol.is(Mutable) do @@ -262,7 +262,7 @@ trait QuotesAndSplices { val pat1 = if (patType eq patType1) pat else pat.withType(patType1) patBuf += pat1 } - case SplicedExpr(pat, _) => + case Splice(pat, _) => try ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) finally { val patType = pat.tpe.widen @@ -401,7 +401,7 @@ trait QuotesAndSplices { * ) => ... * ``` */ - private def typedQuotePattern(tree: untpd.QuotedExpr, pt: Type, qctx: Tree)(using Context): Tree = { + private def typedQuotePattern(tree: untpd.Quote, pt: Type, qctx: Tree)(using Context): Tree = { val quoted = tree.expr if quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then report.error("Quote pattern can only match scrutinees of type scala.quoted.Expr", tree.srcPos) @@ -461,7 +461,7 @@ trait QuotesAndSplices { val quoteClass = if (quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (quoted.isTerm) tpd.QuotedExpr(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) + if (quoted.isTerm) tpd.Quote(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx) val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 9fe679a0583b..fc51af646b17 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -392,13 +392,13 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(using Context): Inlined = tree.withType(avoidingType(expansion, bindings)) - def assignType(tree: untpd.QuotedExpr, tpt: Tree)(using Context): QuotedExpr = + def assignType(tree: untpd.Quote, tpt: Tree)(using Context): Quote = val lambdaType = // Quotes ?=> Expr[T] defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe)) tree.withType(lambdaType) - def assignType(tree: untpd.SplicedExpr, tpt: Tree)(using Context): SplicedExpr = + def assignType(tree: untpd.Splice, tpt: Tree)(using Context): Splice = tree.withType(tpt.tpe) def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(using Context): If = diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index aa0243146241..4bc012b5b226 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3075,8 +3075,8 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree: untpd.ParsedTry => typedTry(tree, pt) case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt) case untpd.EmptyTree => tpd.EmptyTree - case tree: untpd.QuotedExpr => typedQuotedExpr(tree, pt) - case tree: untpd.SplicedExpr => typedSplicedExpr(tree, pt) + case tree: untpd.Quote => typedQuote(tree, pt) + case tree: untpd.Splice => typedSplice(tree, pt) case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here case tree: untpd.Hole => typedHole(tree, pt) case _ => typedUnadapted(desugar(tree, pt), pt, locked) From 76d0daf885f10041bbf9ee8bea89dcb089fa817e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 13:25:50 +0200 Subject: [PATCH 410/657] Add documentation --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 65a0cd6ba710..546e55ee22e3 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -677,11 +677,23 @@ object Trees { override def isType = expansion.isType } + /** A tree representing a quote `'{ expr } + * + * @param expr The tree that was quoted + * @param tpt The type of the tree that was quoted, + * EmptyTree if this tree comes from the parser. + */ case class Quote[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Quote[T] } + /** A tree representing a splice `${ expr }` + * + * @param expr The tree that was spliced + * @param tpt The type of the tree that was spliced, + * EmptyTree if this tree comes from the parser. + */ case class Splice[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] From 75bacdbadf9ecc9db4c39223e05669e0a329b526 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 13:37:10 +0200 Subject: [PATCH 411/657] Backwards compatibility support for quotes and splice in reflection API --- .../quoted/runtime/impl/QuotesImpl.scala | 23 ++++++++++++++++--- tests/run-staging/multi-staging.check | 2 +- tests/run-staging/quote-nested-5.check | 4 ++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 1949304ca287..f63ef3feb041 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -609,11 +609,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end extension end NamedArgMethods - type Apply = tpd.Apply + type Apply = tpd.Apply | tpd.Quote | tpd.Splice object ApplyTypeTest extends TypeTest[Tree, Apply]: def unapply(x: Tree): Option[Apply & x.type] = x match case x: (tpd.Apply & x.type) => Some(x) + case x: (tpd.Quote & x.type) => Some(x) // TODO expose Quote AST in Quotes + case x: (tpd.Splice & x.type) => Some(x) // TODO expose Splice AST in Quotes case _ => None end ApplyTypeTest @@ -630,8 +632,23 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler given ApplyMethods: ApplyMethods with extension (self: Apply) - def fun: Term = self.fun - def args: List[Term] = self.args + def fun: Term = self match + case self: tpd.Apply => self.fun + case self: tpd.Quote => // TODO expose Quote AST in Quotes + import dotty.tools.dotc.ast.tpd.TreeOps + tpd.ref(dotc.core.Symbols.defn.QuotedRuntime_exprQuote) + .appliedToTypeTree(self.tpt) + .withSpan(self.span) + case self: tpd.Splice => // TODO expose Splice AST in Quotes + import dotty.tools.dotc.ast.tpd.TreeOps + tpd.ref(dotc.core.Symbols.defn.QuotedRuntime_exprSplice) + .appliedToTypeTree(self.tpt) + .withSpan(self.span) + + def args: List[Term] = self match + case self: tpd.Apply => self.args + case self: tpd.Quote => List(self.expr) // TODO expose Quote AST in Quotes + case self: tpd.Splice => List(self.expr) // TODO expose Splice AST in Quotes end extension end ApplyMethods diff --git a/tests/run-staging/multi-staging.check b/tests/run-staging/multi-staging.check index 5d12306ba4ef..76adcfec3034 100644 --- a/tests/run-staging/multi-staging.check +++ b/tests/run-staging/multi-staging.check @@ -1,5 +1,5 @@ stage1 code: ((q1: scala.quoted.Quotes) ?=> { val x1: scala.Int = 2 - scala.quoted.runtime.Expr.quote[scala.Int](1.+(scala.quoted.runtime.Expr.nestedSplice[scala.Int](q1)(((evidence$5: scala.quoted.Quotes) ?=> scala.quoted.Expr.apply[scala.Int](x1)(scala.quoted.ToExpr.IntToExpr[scala.Int])(evidence$5))))).apply(using q1) + scala.quoted.runtime.Expr.quote[scala.Int](1.+(scala.quoted.runtime.Expr.splice[scala.Int](((evidence$5: scala.quoted.Quotes) ?=> scala.quoted.Expr.apply[scala.Int](x1)(scala.quoted.ToExpr.IntToExpr[scala.Int])(evidence$5))))).apply(using q1) }) 3 diff --git a/tests/run-staging/quote-nested-5.check b/tests/run-staging/quote-nested-5.check index f29acb3b347a..d5fee708c460 100644 --- a/tests/run-staging/quote-nested-5.check +++ b/tests/run-staging/quote-nested-5.check @@ -1,4 +1,4 @@ ((q: scala.quoted.Quotes) ?=> { val a: scala.quoted.Expr[scala.Int] = scala.quoted.runtime.Expr.quote[scala.Int](4).apply(using q) - ((q2: scala.quoted.Quotes) ?=> ((evidence$3: scala.quoted.Quotes) ?=> a).asInstanceOf[scala.ContextFunction1[scala.quoted.Quotes, scala.quoted.Expr[scala.Int]]].apply(using q2)).apply(using q) -}) + ((q2: scala.quoted.Quotes) ?=> ((evidence$2: scala.quoted.Quotes) ?=> a).asInstanceOf[scala.ContextFunction1[scala.quoted.Quotes, scala.quoted.Expr[scala.Int]]].apply(using q2)) +}.apply(using q)) From 0e6dc2eee80a5aed21876355a62906a84166ffb3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Apr 2023 13:48:25 +0200 Subject: [PATCH 412/657] Add regression test Closes #6991 --- tests/neg-macros/i6991.check | 10 ++++++++++ tests/neg-macros/i6991.scala | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/neg-macros/i6991.check create mode 100644 tests/neg-macros/i6991.scala diff --git a/tests/neg-macros/i6991.check b/tests/neg-macros/i6991.check new file mode 100644 index 000000000000..57d611a09053 --- /dev/null +++ b/tests/neg-macros/i6991.check @@ -0,0 +1,10 @@ +-- [E050] Type Error: tests/neg-macros/i6991.scala:11:14 --------------------------------------------------------------- +11 | case '{($x: Foo)($bar: String)} => '{"Hello World"} // error + | ^^^^^^^ + | expression does not take parameters + | + | longer explanation available when compiling with `-explain` +-- [E008] Not Found Error: tests/neg-macros/i6991.scala:12:23 ---------------------------------------------------------- +12 | case '{($x: Foo).apply($bar: String)} => '{"Hello World"} // error + | ^^^^^^^^^^^^^^^ + | value apply is not a member of macros.Foo diff --git a/tests/neg-macros/i6991.scala b/tests/neg-macros/i6991.scala new file mode 100644 index 000000000000..c6838261ed7a --- /dev/null +++ b/tests/neg-macros/i6991.scala @@ -0,0 +1,16 @@ +import scala.quoted._ + +object macros { + inline def mcr(x: => Any): Any = ${mcrImpl('x)} + + class Foo // { def apply(str: String) = "100" } + class Bar { def apply(str: String) = "100" } + + def mcrImpl(body: Expr[Any])(using ctx: Quotes): Expr[Any] = { + body match { + case '{($x: Foo)($bar: String)} => '{"Hello World"} // error + case '{($x: Foo).apply($bar: String)} => '{"Hello World"} // error + case '{($x: Bar)($bar: String)} => '{"Hello World"} + } + } +} From 709060952c452240201b0def95949dd333a6569b Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 25 Apr 2023 14:04:49 +0200 Subject: [PATCH 413/657] Fix compiler crash in WUnused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 3 ++- tests/neg-custom-args/fatal-warnings/i17335.scala | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i17335.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index ce05e6c125de..32afe9c8a1e7 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -47,7 +47,8 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def isRunnable(using Context): Boolean = ctx.settings.Wunused.value.nonEmpty && - !ctx.isJava + !ctx.isJava && + super.isRunnable // ========== SETUP ============ diff --git a/tests/neg-custom-args/fatal-warnings/i17335.scala b/tests/neg-custom-args/fatal-warnings/i17335.scala new file mode 100644 index 000000000000..6629e2f151c9 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17335.scala @@ -0,0 +1,4 @@ +// scalac: -Wunused:all + +def aMethod() = + doStuff { (x) => x } // error From 8cbde30aa634d52c3e3bfb4104df224a19405e85 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 25 Apr 2023 14:12:56 +0200 Subject: [PATCH 414/657] Change the order of checks --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 32afe9c8a1e7..371df57045b4 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -46,9 +46,9 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def description: String = CheckUnused.description override def isRunnable(using Context): Boolean = + super.isRunnable && ctx.settings.Wunused.value.nonEmpty && - !ctx.isJava && - super.isRunnable + !ctx.isJava // ========== SETUP ============ From aae9ec1dfbe695ae481be5d6740898d9bd54d624 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Fri, 21 Apr 2023 01:35:46 +0200 Subject: [PATCH 415/657] Wunused: Include import selector bounds in unused checks closes lampepfl#17314 --- .../tools/dotc/transform/CheckUnused.scala | 34 ++++++++++++------- .../fatal-warnings/i17314b.scala | 14 ++++++++ tests/pos-special/fatal-warnings/i17314.scala | 33 ++++++++++++++++++ .../pos-special/fatal-warnings/i17314a.scala | 12 +++++++ 4 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i17314b.scala create mode 100644 tests/pos-special/fatal-warnings/i17314.scala create mode 100644 tests/pos-special/fatal-warnings/i17314a.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index ce05e6c125de..baef470c8e88 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -202,8 +202,11 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke override def traverse(tree: tpd.Tree)(using Context): Unit = val newCtx = if tree.symbol.exists then ctx.withOwner(tree.symbol) else ctx tree match - case imp:tpd.Import => + case imp: tpd.Import => unusedDataApply(_.registerImport(imp)) + imp.selectors.filter(_.isGiven).map(_.bound).collect { + case untpd.TypedSplice(tree1) => tree1 + }.foreach(traverse(_)(using newCtx)) traverseChildren(tree)(using newCtx) case ident: Ident => prepareForIdent(ident) @@ -449,13 +452,12 @@ object CheckUnused: val used = usedInScope.pop().toSet // used imports in this scope val imports = impInScope.pop() - val kept = used.filterNot { t => - val (sym, isAccessible, optName, isDerived) = t + val kept = used.filterNot { (sym, isAccessible, optName, isDerived) => // keep the symbol for outer scope, if it matches **no** import // This is the first matching wildcard selector var selWildCard: Option[ImportSelector] = None - val exists = imports.exists { imp => + val matchedExplicitImport = imports.exists { imp => sym.isInImport(imp, isAccessible, optName, isDerived) match case None => false case optSel@Some(sel) if sel.isWildcard => @@ -466,11 +468,11 @@ object CheckUnused: unusedImport -= sel true } - if !exists && selWildCard.isDefined then + if !matchedExplicitImport && selWildCard.isDefined then unusedImport -= selWildCard.get true // a matching import exists so the symbol won't be kept for outer scope else - exists + matchedExplicitImport } // if there's an outer scope @@ -610,12 +612,17 @@ object CheckUnused: * return true */ private def shouldSelectorBeReported(imp: tpd.Import, sel: ImportSelector)(using Context): Boolean = - if ctx.settings.WunusedHas.strictNoImplicitWarn then + ctx.settings.WunusedHas.strictNoImplicitWarn && ( sel.isWildcard || imp.expr.tpe.member(sel.name.toTermName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) || imp.expr.tpe.member(sel.name.toTypeName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) - else - false + ) + + extension (tree: ImportSelector) + def boundTpe: Type = tree.bound match { + case untpd.TypedSplice(tree1) => tree1.tpe + case _ => NoType + } extension (sym: Symbol) /** is accessible without import in current context */ @@ -628,7 +635,7 @@ object CheckUnused: && c.owner.thisType.member(sym.name).alternatives.contains(sym) } - /** Given an import and accessibility, return an option of selector that match import<->symbol */ + /** Given an import and accessibility, return selector that matches import<->symbol */ private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name], isDerived: Boolean)(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) @@ -641,9 +648,12 @@ object CheckUnused: def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None - def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) + def givenSelector = if sym.is(Given) || sym.is(Implicit) + then sels.filter(sel => sel.isGiven && !sel.bound.isEmpty).find(sel => sel.boundTpe =:= sym.info) + else None + def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven && sel.bound.isEmpty) || sym.is(Implicit))) if qualHasSymbol && (!isAccessible || sym.isRenamedSymbol(symName)) && sym.exists then - selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) + selector.orElse(dealiasedSelector).orElse(givenSelector).orElse(wildcard) // selector with name or wildcard (or given) else None diff --git a/tests/neg-custom-args/fatal-warnings/i17314b.scala b/tests/neg-custom-args/fatal-warnings/i17314b.scala new file mode 100644 index 000000000000..384767765cf4 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i17314b.scala @@ -0,0 +1,14 @@ +// scalac: -Wunused:all + +package foo: + class Foo[T] + given Foo[Int] = new Foo[Int] + + +package bar: + import foo.{given foo.Foo[Int]} // error + import foo.Foo + + given Foo[Int] = ??? + + val repro: Foo[Int] = summon[Foo[Int]] diff --git a/tests/pos-special/fatal-warnings/i17314.scala b/tests/pos-special/fatal-warnings/i17314.scala new file mode 100644 index 000000000000..23f988741bed --- /dev/null +++ b/tests/pos-special/fatal-warnings/i17314.scala @@ -0,0 +1,33 @@ +// scalac: "-Wunused:all" + +import java.net.URI + +object circelike { + import scala.compiletime.summonInline + import scala.deriving.Mirror + + type Codec[T] + type Configuration + trait ConfiguredCodec[T] + object ConfiguredCodec: + inline final def derived[A](using conf: Configuration)(using + inline mirror: Mirror.Of[A] + ): ConfiguredCodec[A] = + new ConfiguredCodec[A]: + val codec = summonInline[Codec[URI]] // simplification +} + +object foo { + import circelike.{Codec, Configuration} + + given Configuration = ??? + given Codec[URI] = ??? +} + +object bar { + import circelike.Codec + import circelike.{Configuration, ConfiguredCodec} + import foo.{given Configuration, given Codec[URI]} + + case class Operator(url: URI) derives ConfiguredCodec +} diff --git a/tests/pos-special/fatal-warnings/i17314a.scala b/tests/pos-special/fatal-warnings/i17314a.scala new file mode 100644 index 000000000000..468b956fb04c --- /dev/null +++ b/tests/pos-special/fatal-warnings/i17314a.scala @@ -0,0 +1,12 @@ +// scalac: -Wunused:all + +package foo: + class Foo[T] + given Foo[Int] = new Foo[Int] + + +package bar: + import foo.{given foo.Foo[Int]} + import foo.Foo + + val repro: Foo[Int] = summon[Foo[Int]] From cdd5ffb3f47211a8fb9805f11f76234846c1fba8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 26 Apr 2023 08:30:13 +0200 Subject: [PATCH 416/657] Adapt REPL test output --- compiler/test-resources/repl-macros/i5551 | 2 +- compiler/test-resources/repl/i10355 | 6 ++++-- staging/test-resources/repl-staging/i6007 | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/test-resources/repl-macros/i5551 b/compiler/test-resources/repl-macros/i5551 index d71251e9a824..984551438b51 100644 --- a/compiler/test-resources/repl-macros/i5551 +++ b/compiler/test-resources/repl-macros/i5551 @@ -1,7 +1,7 @@ scala> import scala.quoted._ scala> def assertImpl(expr: Expr[Boolean])(using q: Quotes) = '{ if !($expr) then throw new AssertionError("failed assertion")} def assertImpl - (expr: quoted.Expr[Boolean])(using q: quoted.Quotes): quoted.Expr[Unit] + (expr: quoted.Expr[Boolean])(using q: quoted.Quotes): scala.quoted.Expr[Unit] scala> inline def assert(expr: => Boolean): Unit = ${ assertImpl('{expr}) } def assert(expr: => Boolean): Unit diff --git a/compiler/test-resources/repl/i10355 b/compiler/test-resources/repl/i10355 index bfe3af835c87..294b9d7f1101 100644 --- a/compiler/test-resources/repl/i10355 +++ b/compiler/test-resources/repl/i10355 @@ -1,5 +1,7 @@ scala> import scala.quoted._ scala> def foo(expr: Expr[Any])(using Quotes) = expr match { case '{ $x: t } => '{ $x: Any } } -def foo(expr: quoted.Expr[Any])(using x$2: quoted.Quotes): quoted.Expr[Any] +def foo + (expr: quoted.Expr[Any])(using x$2: quoted.Quotes): scala.quoted.Expr[Any] scala> def bar(expr: Expr[Any])(using Quotes) = expr match { case '{ $x: t } => '{ val a: t = ??? ; ???} } -def bar(expr: quoted.Expr[Any])(using x$2: quoted.Quotes): quoted.Expr[Nothing] +def bar + (expr: quoted.Expr[Any])(using x$2: quoted.Quotes): scala.quoted.Expr[Nothing] diff --git a/staging/test-resources/repl-staging/i6007 b/staging/test-resources/repl-staging/i6007 index be9d5c0f92d6..0d6fbd0cffb1 100644 --- a/staging/test-resources/repl-staging/i6007 +++ b/staging/test-resources/repl-staging/i6007 @@ -3,7 +3,7 @@ scala> import quoted.staging.{Compiler => StagingCompiler, _} scala> implicit def compiler: StagingCompiler = StagingCompiler.make(getClass.getClassLoader) def compiler: quoted.staging.Compiler scala> def v(using Quotes) = '{ (if true then Some(1) else None).map(v => v+1) } -def v(using x$1: quoted.Quotes): quoted.Expr[Option[Int]] +def v(using x$1: quoted.Quotes): scala.quoted.Expr[Option[Int]] scala> scala.quoted.staging.withQuotes(v.show) val res0: String = (if (true) scala.Some.apply[scala.Int](1) else scala.None).map[scala.Int](((v: scala.Int) => v.+(1))) scala> scala.quoted.staging.run(v) From 7c96eed5fc8bd726480e005281e1a02608c5c0fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 26 Apr 2023 16:00:49 +0200 Subject: [PATCH 417/657] Fix #17344: Make implicit references to this above dynamic imports explicit. Implicit references to the `this` of an outer class are made explicit by the typer, and they need to be for `explicitOuter` to do its job correctly. When we desugar a `js.dynamicImport`, we move the code inside a synthetic inner class. If it contains implicit references to an enclosing class, we must make them explicit at that point. --- .../dotc/transform/sjs/PrepJSInterop.scala | 33 ++++++++++++++++++- project/Build.scala | 12 +++++++ .../SJSDynamicImportTestScala3.scala | 29 ++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/sjs-junit/test-require-multi-modules/org/scalajs/testsuite/jsinterop/SJSDynamicImportTestScala3.scala diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala index 8a430991e378..a2f9a0fb45a3 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala @@ -93,6 +93,24 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP } } + private var dynamicImportEnclosingClasses: Set[Symbol] = Set.empty + + private def enterDynamicImportEnclosingClass[A](cls: Symbol)(body: => A): A = { + val saved = dynamicImportEnclosingClasses + dynamicImportEnclosingClasses = saved + cls + try + body + finally + dynamicImportEnclosingClasses = saved + } + + private def hasImplicitThisPrefixToDynamicImportEnclosingClass(tpe: Type)(using Context): Boolean = + tpe match + case tpe: ThisType => dynamicImportEnclosingClasses.contains(tpe.cls) + case TermRef(prefix, _) => hasImplicitThisPrefixToDynamicImportEnclosingClass(prefix) + case _ => false + end hasImplicitThisPrefixToDynamicImportEnclosingClass + /** DefDefs in class templates that export methods to JavaScript */ private val exporters = mutable.Map.empty[Symbol, mutable.ListBuffer[Tree]] @@ -297,10 +315,15 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP assert(currentOwner.isTerm, s"unexpected owner: $currentOwner at ${tree.sourcePos}") + val enclosingClass = currentOwner.enclosingClass + // new DynamicImportThunk { def apply(): Any = body } val dynamicImportThunkAnonClass = AnonClass(currentOwner, List(jsdefn.DynamicImportThunkType), span) { cls => val applySym = newSymbol(cls, nme.apply, Method, MethodType(Nil, Nil, defn.AnyType), coord = span).entered - val newBody = transform(body).changeOwnerAfter(currentOwner, applySym, thisPhase) + val transformedBody = enterDynamicImportEnclosingClass(enclosingClass) { + transform(body) + } + val newBody = transformedBody.changeOwnerAfter(currentOwner, applySym, thisPhase) val applyDefDef = DefDef(applySym, newBody) List(applyDefDef) } @@ -310,6 +333,14 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP .appliedToTypeTree(tpeArg) .appliedTo(dynamicImportThunkAnonClass) + // #17344 Make `ThisType`-based references to enclosing classes of `js.dynamicImport` explicit + case tree: Ident if hasImplicitThisPrefixToDynamicImportEnclosingClass(tree.tpe) => + def rec(tpe: Type): Tree = (tpe: @unchecked) match // exhaustive because of the `if ... =>` + case tpe: ThisType => This(tpe.cls) + case tpe @ TermRef(prefix, _) => rec(prefix).select(tpe.symbol) + + rec(tree.tpe).withSpan(tree.span) + // Compile-time errors and warnings for js.Dynamic.literal case Apply(Apply(fun, nameArgs), args) if fun.symbol == jsdefn.JSDynamicLiteral_applyDynamic || diff --git a/project/Build.scala b/project/Build.scala index 97b4d31f5e02..a1245dfd8460 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1219,6 +1219,18 @@ object Build { org.scalajs.jsenv.Input.Script(f) +: (Test / jsEnvInput).value }, + Test / unmanagedSourceDirectories ++= { + val linkerConfig = scalaJSStage.value match { + case FastOptStage => (Test / fastLinkJS / scalaJSLinkerConfig).value + case FullOptStage => (Test / fullLinkJS / scalaJSLinkerConfig).value + } + + if (linkerConfig.moduleKind != ModuleKind.NoModule && !linkerConfig.closureCompiler) + Seq(baseDirectory.value / "test-require-multi-modules") + else + Nil + }, + (Compile / managedSources) ++= { val dir = fetchScalaJSSource.value ( diff --git a/tests/sjs-junit/test-require-multi-modules/org/scalajs/testsuite/jsinterop/SJSDynamicImportTestScala3.scala b/tests/sjs-junit/test-require-multi-modules/org/scalajs/testsuite/jsinterop/SJSDynamicImportTestScala3.scala new file mode 100644 index 000000000000..b7c9060fcce1 --- /dev/null +++ b/tests/sjs-junit/test-require-multi-modules/org/scalajs/testsuite/jsinterop/SJSDynamicImportTestScala3.scala @@ -0,0 +1,29 @@ +package org.scalajs.testsuite.jsinterop + +import org.junit.Assert.* +import org.junit.Test + +import org.scalajs.junit.async._ + +import scala.scalajs.js +import scala.scalajs.js.annotation.* + +class SJSDynamicImportTestScala3 { + import scala.concurrent.ExecutionContext.Implicits.global + + @Test def implicitThisReferenceInDynamicImport_Issue17344(): AsyncResult = await { + class Foo() { + def foo(): Int = 1 + } + class Bar(foo: Foo) { + def bar(): js.Promise[Int] = js.dynamicImport(foo.foo()) + } + + val bar = new Bar(new Foo()) + val promise = bar.bar() + + promise.toFuture.map { r => + assertEquals(1, r) + } + } +} From d4bd097da79b6322ba8f23b66bfa07fa02dd949d Mon Sep 17 00:00:00 2001 From: Carl Date: Thu, 27 Apr 2023 01:52:12 +0200 Subject: [PATCH 418/657] Add unset vars warning for Wunused:locals, privates --- .../tools/dotc/transform/CheckUnused.scala | 83 ++++--- .../fatal-warnings/i15503b.scala | 58 ++++- .../fatal-warnings/i15503c.scala | 19 +- .../fatal-warnings/i15503i.scala | 10 +- .../fatal-warnings/i16639a.scala | 207 ++++++++++++++++++ tests/pos/i16639false-pos-on-trait.scala | 40 ++++ 6 files changed, 370 insertions(+), 47 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i16639a.scala create mode 100644 tests/pos/i16639false-pos-on-trait.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 69ec9f0d7b2b..4eba6b525b01 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -27,6 +27,7 @@ import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.StdNames.nme import scala.math.Ordering + /** * A compiler phase that checks for unused imports or definitions * @@ -146,6 +147,13 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke if !tree.isInstanceOf[tpd.InferredTypeTree] then typeTraverser(unusedDataApply).traverse(tree.tpe) ctx + override def prepareForAssign(tree: tpd.Assign)(using Context): Context = + unusedDataApply{ ud => + val sym = tree.lhs.symbol + if sym.exists then + ud.registerSetVar(sym) + } + // ========== MiniPhase Transform ========== override def transformBlock(tree: tpd.Block)(using Context): tpd.Tree = @@ -172,6 +180,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke unusedDataApply(_.removeIgnoredUsage(tree.symbol)) tree + // ---------- MiniPhase HELPERS ----------- private def pushInBlockTemplatePackageDef(tree: tpd.Block | tpd.Template | tpd.PackageDef)(using Context): Context = @@ -215,11 +224,11 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke case sel: Select => prepareForSelect(sel) traverseChildren(tree)(using newCtx) - case _: (tpd.Block | tpd.Template | tpd.PackageDef) => + case tree: (tpd.Block | tpd.Template | tpd.PackageDef) => //! DIFFERS FROM MINIPHASE - unusedDataApply { ud => - ud.inNewScope(ScopeType.fromTree(tree))(traverseChildren(tree)(using newCtx)) - } + pushInBlockTemplatePackageDef(tree) + traverseChildren(tree)(using newCtx) + popOutBlockTemplatePackageDef() case t:tpd.ValDef => prepareForValDef(t) traverseChildren(tree)(using newCtx) @@ -235,6 +244,9 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke case t: tpd.Bind => prepareForBind(t) traverseChildren(tree)(using newCtx) + case t:tpd.Assign => + prepareForAssign(t) + traverseChildren(tree) case _: tpd.InferredTypeTree => case t@tpd.TypeTree() => //! DIFFERS FROM MINIPHASE @@ -278,6 +290,10 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke report.warning(s"unused private member", t) case UnusedSymbol(t, _, WarnTypes.PatVars) => report.warning(s"unused pattern variable", t) + case UnusedSymbol(t, _, WarnTypes.UnsetLocals) => + report.warning(s"unset local variable", t) + case UnusedSymbol(t, _, WarnTypes.UnsetPrivates) => + report.warning(s"unset private variable", t) } end CheckUnused @@ -297,6 +313,8 @@ object CheckUnused: case ImplicitParams case PrivateMembers case PatVars + case UnsetLocals + case UnsetPrivates /** * The key used to retrieve the "unused entity" analysis metadata, @@ -343,12 +361,8 @@ object CheckUnused: private val implicitParamInScope = MutSet[tpd.MemberDef]() private val patVarsInScope = MutSet[tpd.Bind]() - /* Unused collection collected at the end */ - private val unusedLocalDef = MutSet[tpd.MemberDef]() - private val unusedPrivateDef = MutSet[tpd.MemberDef]() - private val unusedExplicitParams = MutSet[tpd.MemberDef]() - private val unusedImplicitParams = MutSet[tpd.MemberDef]() - private val unusedPatVars = MutSet[tpd.Bind]() + /** All variables sets*/ + private val setVars = MutSet[Symbol]() /** All used symbols */ private val usedDef = MutSet[Symbol]() @@ -360,15 +374,6 @@ object CheckUnused: private val paramsToSkip = MutSet[Symbol]() - /** - * Push a new Scope of the given type, executes the given Unit and - * pop it back to the original type. - */ - def inNewScope(newScope: ScopeType)(execInNewScope: => Unit)(using Context): Unit = - val prev = currScopeType - pushScope(newScope) - execInNewScope - popScope() def finishAggregation(using Context)(): Unit = val unusedInThisStage = this.getUnused @@ -443,6 +448,9 @@ object CheckUnused: impInScope.push(MutSet()) usedInScope.push(MutSet()) + def registerSetVar(sym: Symbol): Unit = + setVars += sym + /** * leave the current scope and do : * @@ -501,15 +509,19 @@ object CheckUnused: unusedImport.map(d => UnusedSymbol(d.srcPos, d.name, WarnTypes.Imports)).toList else Nil - val sortedLocalDefs = + // Partition to extract unset local variables from usedLocalDefs + val (usedLocalDefs, unusedLocalDefs) = if ctx.settings.WunusedHas.locals then - localDefInScope - .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) - .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.LocalDefs)).toList + localDefInScope.partition(d => d.symbol.usedDefContains) else - Nil + (Nil, Nil) + val sortedLocalDefs = + unusedLocalDefs + .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) + .filterNot(d => containsSyntheticSuffix(d.symbol)) + .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.LocalDefs)).toList + val unsetLocalDefs = unsetVarsFromUsedSym(usedLocalDefs).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.UnsetLocals)).toList + val sortedExplicitParams = if ctx.settings.WunusedHas.explicits then explicitParamInScope @@ -527,14 +539,14 @@ object CheckUnused: .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.ImplicitParams)).toList else Nil - val sortedPrivateDefs = + // Partition to extract unset private variables from usedPrivates + val (usedPrivates, unusedPrivates) = if ctx.settings.WunusedHas.privates then - privateDefInScope - .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol)) - .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PrivateMembers)).toList + privateDefInScope.partition(d => d.symbol.usedDefContains) else - Nil + (Nil, Nil) + val sortedPrivateDefs = unusedPrivates.filterNot(d => containsSyntheticSuffix(d.symbol)).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PrivateMembers)).toList + val unsetPrivateDefs = unsetVarsFromUsedSym(usedPrivates).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.UnsetPrivates)).toList val sortedPatVars = if ctx.settings.WunusedHas.patvars then patVarsInScope @@ -544,7 +556,9 @@ object CheckUnused: .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PatVars)).toList else Nil - val warnings = List(sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, sortedPrivateDefs, sortedPatVars).flatten.sortBy { s => + val warnings = + List(sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, + sortedPrivateDefs, sortedPatVars, unsetLocalDefs, unsetPrivateDefs).flatten.sortBy { s => val pos = s.pos.sourcePos (pos.line, pos.column) } @@ -744,6 +758,9 @@ object CheckUnused: private def isWildcard: Boolean = thisName == StdNames.nme.WILDCARD || thisName.is(WildcardParamName) + def unsetVarsFromUsedSym(usedDefs: Iterable[tpd.MemberDef])(using Context): Iterable[tpd.MemberDef] = + usedDefs.filter(d => d.symbol.is(Mutable) && !setVars(d.symbol)) + end UnusedData private object UnusedData: diff --git a/tests/neg-custom-args/fatal-warnings/i15503b.scala b/tests/neg-custom-args/fatal-warnings/i15503b.scala index 8a4a055150f9..c8a2d6bc2074 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503b.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503b.scala @@ -2,49 +2,91 @@ val a = 1 // OK +var cs = 3 // OK + val b = // OK + var e3 = 2 // error val e1 = 1 // error def e2 = 2 // error 1 val c = // OK - val e1 = 1 // OK + var e1 = 1 // error not set + def e2 = e1 // OK + val e3 = e2 // OK + e3 + +val g = // OK + var e1 = 1 // OK def e2 = e1 // OK - e2 + val e3 = e2 // OK + e1 = e3 // OK + e3 def d = 1 // OK def e = // OK val e1 = 1 // error def e2 = 2 // error + var e3 = 4 // error 1 def f = // OK val f1 = 1 // OK - def f2 = f1 // OK + var f2 = f1 // error not set + def f3 = f2 // OK + f3 + +def h = // OK + val f1 = 1 // OK + var f2 = f1 // OK + def f3 = f2 // OK + f2 = f3 // OK f2 class Foo { + val a = 1 // OK + + var cs = 3 // OK + val b = // OK + var e3 = 2 // error val e1 = 1 // error def e2 = 2 // error 1 val c = // OK - val e1 = 1 // OK + var e1 = 1 // error not set + def e2 = e1 // OK + val e3 = e2 // OK + e3 + + val g = // OK + var e1 = 1 // OK def e2 = e1 // OK - e2 + val e3 = e2 // OK + e1 = e3 // OK + e3 def d = 1 // OK def e = // OK val e1 = 1 // error def e2 = 2 // error + var e3 = 4 // error 1 def f = // OK val f1 = 1 // OK - def f2 = f1 // OK + var f2 = f1 // error not set + def f3 = f2 // OK + f3 + + def h = // OK + val f1 = 1 // OK + var f2 = f1 // OK + def f3 = f2 // OK + f2 = f3 // OK f2 } @@ -68,7 +110,7 @@ package foo.scala2.tests: new a.Inner } def f2 = { - var x = 100 + var x = 100 // error not set x } } @@ -89,7 +131,7 @@ package foo.scala2.tests: } package test.foo.twisted.i16682: - def myPackage = + def myPackage = object IntExtractor: // OK def unapply(s: String): Option[Int] = s.toIntOption diff --git a/tests/neg-custom-args/fatal-warnings/i15503c.scala b/tests/neg-custom-args/fatal-warnings/i15503c.scala index 630846df4e5d..e4e15116bf0d 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503c.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503c.scala @@ -12,12 +12,24 @@ class A: private[this] val f = e // OK private val g = f // OK + private[A] var h = 1 // OK + private[this] var i = h // error not set + private var j = i // error not set + + private[this] var k = 1 // OK + private var l = 2 // OK + private val m = // error + k = l + l = k + l + private def fac(x: Int): Int = // error if x == 0 then 1 else x * fac(x - 1) val x = 1 // OK def y = 2 // OK def z = g // OK + var w = 2 // OK package foo.test.contructors: case class A private (x:Int) // OK @@ -25,7 +37,12 @@ package foo.test.contructors: class C private (private val x: Int) // error class D private (private val x: Int): // OK def y = x - + class E private (private var x: Int): // error not set + def y = x + class F private (private var x: Int): // OK + def y = + x = 3 + x package test.foo.i16682: object myPackage: diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index fefead7f01a3..ea84c176eee4 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -142,8 +142,8 @@ package foo.test.possibleclasses.withvar: private var y: Int // OK )( s: Int, // OK - var t: Int, // OK - private var z: Int // OK + var t: Int, // OK global scope can be set somewhere else + private var z: Int // error not set ) { def a = k + y + s + t + z } @@ -159,11 +159,11 @@ package foo.test.possibleclasses.withvar: class AllUsed( k: Int, // OK - private var y: Int // OK + private var y: Int // error not set )( s: Int, // OK - var t: Int, // OK - private var z: Int // OK + var t: Int, // OK global scope can be set somewhere else + private var z: Int // error not set ) { def a = k + y + s + t + z } diff --git a/tests/neg-custom-args/fatal-warnings/i16639a.scala b/tests/neg-custom-args/fatal-warnings/i16639a.scala new file mode 100644 index 000000000000..c62910b7f566 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16639a.scala @@ -0,0 +1,207 @@ +// scalac: -Wunused:all +// +class Bippy(a: Int, b: Int) { + private def this(c: Int) = this(c, c) // warn /Dotty:NoWarn + private def boop(x: Int) = x+a+b // error + private def bippy(x: Int): Int = bippy(x) // error TODO: could warn + final private val MILLIS1 = 2000 // error no warn, /Dotty:Warn + final private val MILLIS2: Int = 1000 // error + final private val HI_COMPANION: Int = 500 // no warn, accessed from companion + def hi() = Bippy.HI_INSTANCE +} +object Bippy { + def hi(x: Bippy) = x.HI_COMPANION + private val HI_INSTANCE: Int = 500 // no warn, accessed from instance + private val HEY_INSTANCE: Int = 1000 // error warn + private lazy val BOOL: Boolean = true // error warn +} + +class A(val msg: String) +class B1(msg: String) extends A(msg) +class B2(msg0: String) extends A(msg0) +class B3(msg0: String) extends A("msg") // error /Dotty: unused explicit parameter + +trait Bing + +trait Accessors { + private var v1: Int = 0 // error warn + private var v2: Int = 0 // error warn, never set + private var v3: Int = 0 // warn, never got /Dotty: no warn even if not used + private var v4: Int = 0 // no warn + + private[this] var v5 = 0 // error warn, never set + private[this] var v6 = 0 // warn, never got /Dotty: no warn even if not used + private[this] var v7 = 0 // no warn + + def bippy(): Int = { + v3 = 3 + v4 = 4 + v6 = 6 + v7 = 7 + v2 + v4 + v5 + v7 + } +} + +class StableAccessors { + private var s1: Int = 0 // error warn + private var s2: Int = 0 // error warn, never set + private var s3: Int = 0 // warn, never got /Dotty: no warn even if not usued + private var s4: Int = 0 // no warn + + private[this] var s5 = 0 // error warn, never set + private[this] var s6 = 0 // no warn, limitation /Dotty: Why limitation ? + private[this] var s7 = 0 // no warn + + def bippy(): Int = { + s3 = 3 + s4 = 4 + s6 = 6 + s7 = 7 + s2 + s4 + s5 + s7 + } +} + +trait DefaultArgs { + // warn about default getters for x2 and x3 + private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3 // no more warn warn since #17061 + + def boppy() = bippy(5, 100, 200) +} + + +class Outer { + class Inner +} + +trait Locals { + def f0 = { + var x = 1 // error warn + var y = 2 + y = 3 + y + y + } + def f1 = { + val a = new Outer // no warn + val b = new Outer // error warn + new a.Inner + } + def f2 = { + var x = 100 // error warn about it being a var, var not set + x + } +} + +object Types { + private object Dongo { def f = this } // no more warn since #17061 + private class Bar1 // error warn + private class Bar2 // no warn + private type Alias1 = String // error warn + private type Alias2 = String // no warn + def bippo = (new Bar2).toString + + def f(x: Alias2) = x.length + + def l1() = { + object HiObject { def f = this } // no more warn since #17061 + class Hi { // error warn + def f1: Hi = new Hi + def f2(x: Hi) = x + } + class DingDongDoobie // error warn + class Bippy // no warn + type Something = Bippy // no warn + type OtherThing = String // error warn + (new Bippy): Something + } +} + +trait Underwarn { + def f(): Seq[Int] + + def g() = { + val Seq(_, _) = f() // no warn + true + } +} + +class OtherNames { + private def x_=(i: Int): Unit = () // no more warn since #17061 + private def x: Int = 42 // error Dotty triggers unused private member : To investigate + private def y_=(i: Int): Unit = () // // no more warn since #17061 + private def y: Int = 42 + + def f = y +} + + +trait Forever { + def f = { + val t = Option((17, 42)) + for { + ns <- t + (i, j) = ns // no warn + } yield (i + j) + } + def g = { + val t = Option((17, 42)) + for { + ns <- t + (i, j) = ns // no warn + } yield 42 // val emitted only if needed, hence nothing unused + } +} + +trait Ignorance { + private val readResolve = 42 // error ignore /dotty triggers unused private member/ why should we ignore ? +} + +trait CaseyKasem { + def f = 42 match { + case x if x < 25 => "no warn" + case y if toString.nonEmpty => "no warn" + y + case z => "warn" + } +} +trait CaseyAtTheBat { + def f = Option(42) match { + case Some(x) if x < 25 => "no warn" + case Some(y @ _) if toString.nonEmpty => "no warn" + case Some(z) => "warn" + case None => "no warn" + } +} + +class `not even using companion privates` + +object `not even using companion privates` { + private implicit class `for your eyes only`(i: Int) { // no more warn since #17061 + def f = i + } +} + +class `no warn in patmat anonfun isDefinedAt` { + def f(pf: PartialFunction[String, Int]) = pf("42") + def g = f { + case s => s.length // no warn (used to warn case s => true in isDefinedAt) + } +} + +// this is the ordinary case, as AnyRef is an alias of Object +class `nonprivate alias is enclosing` { + class C + type C2 = C + private class D extends C2 // error warn +} + +object `classof something` { + private class intrinsically + def f = classOf[intrinsically].toString() +} + +trait `short comings` { + def f: Int = { + val x = 42 // error /Dotty only triggers in dotty + 17 + } +} + diff --git a/tests/pos/i16639false-pos-on-trait.scala b/tests/pos/i16639false-pos-on-trait.scala new file mode 100644 index 000000000000..67e304f556e1 --- /dev/null +++ b/tests/pos/i16639false-pos-on-trait.scala @@ -0,0 +1,40 @@ +// scalac -Wunsued:all +//Avoid warning on setter in trait Regression test : issue10154 scala + +trait T { + private var x: String = _ + + def y: String = { + if (x eq null) x = "hello, world" + x + } +} + +/* +➜ skalac -version +Scala compiler version 2.13.10-20220920-001308-98972e5 -- Copyright 2002-2022, LAMP/EPFL and Lightbend, Inc. + +➜ skalac -d /tmp -Wunused -Vprint:typer t12646.scala +t12646.scala:3: warning: parameter value x_= in variable x is never used + private var x: String = _ + ^ +[[syntax trees at end of typer]] // t12646.scala +package { + abstract trait T extends scala.AnyRef { + def /*T*/$init$(): Unit = { + () + }; + private val x: String = _; + private def x_=(x$1: String): Unit; + def y: String = { + if (T.this.x.eq(null)) + T.this.x_=("hello, world") + else + (); + T.this.x + } + } +} + +1 warning +*/ From 04f020f8a824f3a73f47de6e74a52becfb26410a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 27 Apr 2023 08:18:36 +0200 Subject: [PATCH 419/657] Remove `Splice.isInBraces` --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 1 - compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 546e55ee22e3..d748d531845f 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -697,7 +697,6 @@ object Trees { case class Splice[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] - def isInBraces: Boolean = span.end != expr.span.end } /** A type tree that represents an existing or inferred type */ diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 9ec4a8648695..ed026d331b60 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -147,10 +147,11 @@ trait QuotesAndSplices { def typedAppliedSplice(tree: untpd.Apply, pt: Type)(using Context): Tree = { assert(ctx.mode.is(Mode.QuotedPattern)) val untpd.Apply(splice: untpd.Splice, args) = tree: @unchecked + def isInBraces: Boolean = splice.span.end != splice.expr.span.end if !isFullyDefined(pt, ForceDegree.flipBottom) then report.error(em"Type must be fully defined.", splice.srcPos) tree.withType(UnspecifiedErrorType) - else if splice.isInBraces then // ${x}(...) match an application + else if isInBraces then // ${x}(...) match an application val typedArgs = args.map(arg => typedExpr(arg)) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) val splice1 = typedSpliceSyntactic(splice, defn.FunctionOf(argTypes, pt)) From 35db4a1c7015781556962e0a07f4f5e099f85495 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 27 Apr 2023 09:16:29 +0200 Subject: [PATCH 420/657] Move Quote/Splice retyping into ReTyper --- .../tools/dotc/typer/QuotesAndSplices.scala | 37 +++++-------------- .../src/dotty/tools/dotc/typer/ReTyper.scala | 14 +++++++ 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index ed026d331b60..f81c0c2de56a 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -31,34 +31,12 @@ trait QuotesAndSplices { import tpd._ - def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = - if tree.tpt.isEmpty then - typedQuoteSyntactic(tree, pt) - else - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using quoteContext) - assignType(untpd.cpy.Quote(tree)(expr1, tpt1), tpt1) - - def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = - if tree.tpt.isEmpty then - typedSpliceSyntactic(tree, pt) - else - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val splicedType = // Quotes ?=> Expr[T] - defn.FunctionType(1, isContextual = true) - .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val expr1 = typed(tree.expr, splicedType)(using spliceContext) - assignType(untpd.cpy.Splice(tree)(expr1, tpt1), tpt1) - - def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = - val tpt = typedType(tree.tpt) - assignType(tree, tpt) - /** Translate `'{ e }` into `scala.quoted.Expr.apply(e)` and `'[T]` into `scala.quoted.Type.apply[T]` * while tracking the quotation level in the context. */ - private def typedQuoteSyntactic(tree: untpd.Quote, pt: Type)(using Context): Tree = { + def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = { record("typedQuote") + assert(tree.tpt.isEmpty) tree.expr match { case untpd.Splice(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) @@ -93,8 +71,9 @@ trait QuotesAndSplices { } /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ - private def typedSpliceSyntactic(tree: untpd.Splice, pt: Type)(using Context): Tree = { + def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = { record("typedSplice") + assert(tree.tpt.isEmpty) checkSpliceOutsideQuote(tree) tree.expr match { case untpd.Quote(innerExpr, _) if innerExpr.isTerm => @@ -137,6 +116,10 @@ trait QuotesAndSplices { } } + def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = + val tpt = typedType(tree.tpt) + assignType(tree, tpt) + /** Types a splice applied to some arguments `$f(arg1, ..., argn)` in a quote pattern. * * The tree is desugared into `$f.apply(arg1, ..., argn)` where the expression `$f` @@ -154,7 +137,7 @@ trait QuotesAndSplices { else if isInBraces then // ${x}(...) match an application val typedArgs = args.map(arg => typedExpr(arg)) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) - val splice1 = typedSpliceSyntactic(splice, defn.FunctionOf(argTypes, pt)) + val splice1 = typedSplice(splice, defn.FunctionOf(argTypes, pt)) Apply(splice1.select(nme.apply), typedArgs).withType(pt).withSpan(tree.span) else // $x(...) higher-order quasipattern val typedArgs = args.map { @@ -167,7 +150,7 @@ trait QuotesAndSplices { if args.isEmpty then report.error("Missing arguments for open pattern", tree.srcPos) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) - val typedPat = typedSpliceSyntactic(splice, defn.FunctionOf(argTypes, pt)) + val typedPat = typedSplice(splice, defn.FunctionOf(argTypes, pt)) ref(defn.QuotedRuntimePatterns_patternHigherOrderHole).appliedToType(pt).appliedTo(typedPat, SeqLiteral(typedArgs, TypeTree(defn.AnyType))) } diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index c64f541fd811..9efe3b31a356 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -12,6 +12,7 @@ import ast.{tpd, untpd} import scala.util.control.NonFatal import util.Spans.Span import Nullables._ +import staging.StagingLevel.* /** A version of Typer that keeps all symbols defined and referenced in a * previously typed tree. @@ -94,6 +95,19 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking override def typedUnApply(tree: untpd.Apply, selType: Type)(using Context): Tree = typedApply(tree, selType) + override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using quoteContext) + assignType(untpd.cpy.Quote(tree)(expr1, tpt1), tpt1) + + override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = + val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) + val splicedType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) + val expr1 = typed(tree.expr, splicedType)(using spliceContext) + assignType(untpd.cpy.Splice(tree)(expr1, tpt1), tpt1) + override def localDummy(cls: ClassSymbol, impl: untpd.Template)(using Context): Symbol = impl.symbol override def retrieveSym(tree: untpd.Tree)(using Context): Symbol = tree.symbol From 1ef7f59095b901201074f662fb9af6b4f4ad0c76 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 27 Apr 2023 09:55:45 +0200 Subject: [PATCH 421/657] Add documentation --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index d748d531845f..3427146a6baa 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -677,11 +677,20 @@ object Trees { override def isType = expansion.isType } - /** A tree representing a quote `'{ expr } + /** A tree representing a quote `'{ expr }` + * `Quote`s are created by the `Parser` with an empty `tpt`. In typer + * they can be typed as a `Quote` with a known `tpt` or desugared and + * typed as a quote pattern. + * + * `Quotes` are checked transformed in the `staging`, `splicing` and `pickleQuotes` + * phases. After `pickleQuotes` phase, the only quotes that exist are in `inline` + * methods. These are dropped when we remove the inline method implementations. + * + * The `tpt` will be transformed in `staging` and used in `pickleQuotes` to create the + * pickled representation of the quote. * * @param expr The tree that was quoted - * @param tpt The type of the tree that was quoted, - * EmptyTree if this tree comes from the parser. + * @param tpt The type of the tree that was quoted */ case class Quote[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { @@ -689,11 +698,20 @@ object Trees { } /** A tree representing a splice `${ expr }` - * - * @param expr The tree that was spliced - * @param tpt The type of the tree that was spliced, - * EmptyTree if this tree comes from the parser. - */ + * + * `Splice`s are created by the `Parser` with an empty `tpt`. In typer + * they can be typed as a `Splice` with a known `tpt` or desugared and + * typed as a quote pattern holes. + * + * `Splice` are checked transformed in the `staging` and `splicing` phases. + * After `splicing` phase, the only quotes that exist are in `inline` + * methods. These are dropped when we remove the inline method implementations. + * + * The `tpt` will be transformed in `staging` and used in `splicing` to create `Hole`s. + * + * @param expr The tree that was spliced + * @param tpt The type of the spliced tree + */ case class Splice[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] From 687d06ea78087cdca1ef4161427c166ae02df9e1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 27 Apr 2023 10:13:47 +0200 Subject: [PATCH 422/657] Transform Quote/Splice in MiniPhase To transform quotes and splices in inline methods. --- .../tools/dotc/transform/ElimByName.scala | 6 --- .../tools/dotc/transform/FirstTransform.scala | 3 -- .../tools/dotc/transform/MegaPhase.scala | 51 +++++++++++++++++++ 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 976df4f436f7..151e841f0e48 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -159,12 +159,6 @@ class ElimByName extends MiniPhase, InfoTransformer: else tree } - override def transformOther(tree: Tree)(using Context): Tree = tree match - case tree @ Splice(spliced, tpt) => - assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.Splice(tree)(transformAllDeep(spliced), tpt) - case tree => tree - object ElimByName: val name: String = "elimByName" val description: String = "map by-name parameters to functions" diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 9d874bf6b00f..a7e0795ce195 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -153,9 +153,6 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase => override def transformOther(tree: Tree)(using Context): Tree = tree match { case tree: Export => EmptyTree case tree: NamedArg => transformAllDeep(tree.arg) - case tree @ Splice(expr, tpt) => - assert(dotty.tools.dotc.inlines.Inlines.inInlineMethod) - cpy.Splice(tree)(transformAllDeep(expr), transformAllDeep(tpt)) case tree => if (tree.isType) toTypeTree(tree) else tree } diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index d4dd911241d3..77d6b2f3ac61 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -5,6 +5,7 @@ package transform import core._ import Contexts._, Phases._, Symbols._, Decorators._ import Flags.PackageVal +import staging.StagingLevel.* /** A MegaPhase combines a number of mini-phases which are all executed in * a single tree traversal. @@ -66,6 +67,8 @@ object MegaPhase { def prepareForTry(tree: Try)(using Context): Context = ctx def prepareForSeqLiteral(tree: SeqLiteral)(using Context): Context = ctx def prepareForInlined(tree: Inlined)(using Context): Context = ctx + def prepareForQuote(tree: Quote)(using Context): Context = ctx + def prepareForSplice(tree: Splice)(using Context): Context = ctx def prepareForTypeTree(tree: TypeTree)(using Context): Context = ctx def prepareForBind(tree: Bind)(using Context): Context = ctx def prepareForAlternative(tree: Alternative)(using Context): Context = ctx @@ -100,6 +103,8 @@ object MegaPhase { def transformTry(tree: Try)(using Context): Tree = tree def transformSeqLiteral(tree: SeqLiteral)(using Context): Tree = tree def transformInlined(tree: Inlined)(using Context): Tree = tree + def transformQuote(tree: Quote)(using Context): Tree = tree + def transformSplice(tree: Splice)(using Context): Tree = tree def transformTypeTree(tree: TypeTree)(using Context): Tree = tree def transformBind(tree: Bind)(using Context): Tree = tree def transformAlternative(tree: Alternative)(using Context): Tree = tree @@ -394,6 +399,18 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { val expansion = transformTree(tree.expansion, start)(using inlineContext(tree.call)) goInlined(cpy.Inlined(tree)(tree.call, bindings, expansion), start) } + case tree: Quote => + inContext(prepQuote(tree, start)(using outerCtx)) { + val expr = transformTree(tree.expr, start)(using quoteContext) + val tpt = transformTree(tree.tpt, start) + goQuote(cpy.Quote(tree)(expr, tpt), start) + } + case tree: Splice => + inContext(prepSplice(tree, start)(using outerCtx)) { + val expr = transformTree(tree.expr, start)(using spliceContext) + val tpt = transformTree(tree.tpt, start) + goSplice(cpy.Splice(tree)(expr, tpt), start) + } case tree: Return => inContext(prepReturn(tree, start)(using outerCtx)) { val expr = transformTree(tree.expr, start) @@ -546,6 +563,10 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { private val nxSeqLiteralTransPhase = init("transformSeqLiteral") private val nxInlinedPrepPhase = init("prepareForInlined") private val nxInlinedTransPhase = init("transformInlined") + private val nxQuotePrepPhase = init("prepareForQuote") + private val nxQuoteTransPhase = init("transformQuote") + private val nxSplicePrepPhase = init("prepareForPrep") + private val nxSpliceTransPhase = init("transformSplice") private val nxTypeTreePrepPhase = init("prepareForTypeTree") private val nxTypeTreeTransPhase = init("transformTypeTree") private val nxBindPrepPhase = init("prepareForBind") @@ -893,6 +914,36 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { } } + def prepQuote(tree: Quote, start: Int)(using Context): Context = { + val phase = nxQuotePrepPhase(start) + if (phase == null) ctx + else prepQuote(tree, phase.idxInGroup + 1)(using phase.prepareForQuote(tree)) + } + + def goQuote(tree: Quote, start: Int)(using Context): Tree = { + val phase = nxQuoteTransPhase(start) + if (phase == null) tree + else phase.transformQuote(tree) match { + case tree1: Quote => goQuote(tree1, phase.idxInGroup + 1) + case tree1 => transformNode(tree1, phase.idxInGroup + 1) + } + } + + def prepSplice(tree: Splice, start: Int)(using Context): Context = { + val phase = nxSplicePrepPhase(start) + if (phase == null) ctx + else prepSplice(tree, phase.idxInGroup + 1)(using phase.prepareForSplice(tree)) + } + + def goSplice(tree: Splice, start: Int)(using Context): Tree = { + val phase = nxSpliceTransPhase(start) + if (phase == null) tree + else phase.transformSplice(tree) match { + case tree1: Splice => goSplice(tree1, phase.idxInGroup + 1) + case tree1 => transformNode(tree1, phase.idxInGroup + 1) + } + } + def prepTypeTree(tree: TypeTree, start: Int)(using Context): Context = { val phase = nxTypeTreePrepPhase(start) if (phase == null) ctx From 76ec7050d323b06a453656b16203fb02346b046a Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Wed, 26 Apr 2023 12:43:54 +0200 Subject: [PATCH 423/657] Assert that symbols created using reflect API have correct privateWithin symbols closes lampepfl#17351 --- compiler/src/dotty/tools/dotc/core/Symbols.scala | 2 +- .../scala/quoted/runtime/impl/QuotesImpl.scala | 3 +++ tests/neg/i17351/Macro_1.scala | 16 ++++++++++++++++ tests/neg/i17351/Test_2.scala | 1 + 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i17351/Macro_1.scala create mode 100644 tests/neg/i17351/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index aa3ae0c3c513..37adaf3ab008 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -170,7 +170,7 @@ object Symbols { asInstanceOf[TermSymbol] } final def asType(using Context): TypeSymbol = { - assert(isType, s"isType called on not-a-Type $this"); + assert(isType, s"asType called on not-a-Type $this"); asInstanceOf[TypeSymbol] } diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 1949304ca287..cf967242a715 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2504,6 +2504,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def newModule(owner: Symbol, name: String, modFlags: Flags, clsFlags: Flags, parents: List[TypeRepr], decls: Symbol => List[Symbol], privateWithin: Symbol): Symbol = assert(parents.nonEmpty && !parents.head.typeSymbol.is(dotc.core.Flags.Trait), "First parent must be a class") + assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`") val mod = dotc.core.Symbols.newNormalizedModuleSymbol( owner, name.toTermName, @@ -2520,8 +2521,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def newMethod(owner: Symbol, name: String, tpe: TypeRepr): Symbol = newMethod(owner, name, tpe, Flags.EmptyFlags, noSymbol) def newMethod(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol = + assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`") dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin) def newVal(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol = + assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`") dotc.core.Symbols.newSymbol(owner, name.toTermName, flags, tpe, privateWithin) def newBind(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol = dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | Case, tpe) diff --git a/tests/neg/i17351/Macro_1.scala b/tests/neg/i17351/Macro_1.scala new file mode 100644 index 000000000000..b80999e1bce5 --- /dev/null +++ b/tests/neg/i17351/Macro_1.scala @@ -0,0 +1,16 @@ +import scala.quoted.* + +inline def gen: Unit = ${ genImpl } + +def genImpl(using Quotes): Expr[Unit] = { + import quotes.reflect.* + + val valDefSymbol = Symbol.newVal(Symbol.spliceOwner, "bar", TypeRepr.of[Unit], Flags.EmptyFlags, Symbol.spliceOwner) + + val valDef = ValDef(valDefSymbol, Some('{ () }.asTerm)) + + Block( + List(valDef), + '{ () }.asTerm + ).asExprOf[Unit] +} diff --git a/tests/neg/i17351/Test_2.scala b/tests/neg/i17351/Test_2.scala new file mode 100644 index 000000000000..209c23204ad3 --- /dev/null +++ b/tests/neg/i17351/Test_2.scala @@ -0,0 +1 @@ +val foo = gen // error From 1ca9e8f96f08eec8a837a691c8afb418fceae5da Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 14:19:46 +0200 Subject: [PATCH 424/657] Apply delayed-init.md --- docs/_spec/05-classes-and-objects.md | 10 ---------- .../dropped-features/delayed-init.md | 0 2 files changed, 10 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/delayed-init.md (100%) diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index c40bd49cae16..c6cb1dc113de 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -96,16 +96,6 @@ If this is not a template of a trait, then its _evaluation_ consists of the foll Mixin-evaluation happens in reverse order of occurrence in the linearization. - Finally, the statement sequence ´\mathit{stats}\,´ is evaluated. -###### Delayed Initialization -This statement sequence constitutes the initialization code for an object or class after the superclass constructor invocation and the mixin-evaluation of the template's base classes as described above. -Normally, this code is passed to a special hook, inaccessible to user code, which simply executes it. - -However, in objects and classes (but not traits) which extend `scala.DelayedInit`, the initialization code is passed to a `delayedInit` method which can be overridden to implement arbitrary semantics. - -```scala -def delayedInit(body: => Unit): Unit -``` - ### Constructor Invocations ```ebnf diff --git a/docs/_spec/TODOreference/dropped-features/delayed-init.md b/docs/_spec/APPLIEDreference/dropped-features/delayed-init.md similarity index 100% rename from docs/_spec/TODOreference/dropped-features/delayed-init.md rename to docs/_spec/APPLIEDreference/dropped-features/delayed-init.md From eb2a02133eff6c54699c33b35870815c5c5170cd Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 14:25:31 +0200 Subject: [PATCH 425/657] Apply do-while.md --- docs/_spec/06-expressions.md | 10 ---------- .../dropped-features/do-while.md | 0 2 files changed, 10 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/do-while.md (100%) diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index 92697e64f0ac..c2c4d1f56afa 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -12,7 +12,6 @@ Expr ::= (Bindings | id | ‘_’) ‘=>’ Expr Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] | ‘while’ ‘(’ Expr ‘)’ {nl} Expr | ‘try’ Expr [‘catch’ Expr] [‘finally’ Expr] - | ‘do’ Expr [semi] ‘while’ ‘(’ Expr ‘)’ | ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr | ‘throw’ Expr | ‘return’ [Expr] @@ -717,15 +716,6 @@ def whileLoop(cond: => Boolean)(body: => Unit): Unit = if (cond) { body ; whileLoop(cond)(body) } else {} ``` -## Do Loop Expressions - -```ebnf -Expr1 ::= ‘do’ Expr [semi] ‘while’ ‘(’ Expr ‘)’ -``` - -The _do loop expression_ `do ´e_1´ while (´e_2´)` is typed and evaluated as if it was the expression `(´e_1´ ; while (´e_2´) ´e_1´)`. -A semicolon preceding the `while` symbol of a do loop expression is ignored. - ## For Comprehensions and For Loops ```ebnf diff --git a/docs/_spec/TODOreference/dropped-features/do-while.md b/docs/_spec/APPLIEDreference/dropped-features/do-while.md similarity index 100% rename from docs/_spec/TODOreference/dropped-features/do-while.md rename to docs/_spec/APPLIEDreference/dropped-features/do-while.md From 3ab8ae822f845d04ff9b45e977471a3715921e74 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 14:25:59 +0200 Subject: [PATCH 426/657] Cleanup --- .../dropped-features/dropped-features.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/dropped-features.md (100%) diff --git a/docs/_spec/TODOreference/dropped-features/dropped-features.md b/docs/_spec/APPLIEDreference/dropped-features/dropped-features.md similarity index 100% rename from docs/_spec/TODOreference/dropped-features/dropped-features.md rename to docs/_spec/APPLIEDreference/dropped-features/dropped-features.md From 1ab9d9bfda87492f0ac642b120276dbd975c8d9b Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 14:33:47 +0200 Subject: [PATCH 427/657] Apply early-initializers.md --- docs/_spec/05-classes-and-objects.md | 53 +------------------ .../dropped-features/early-initializers.md | 0 2 files changed, 1 insertion(+), 52 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/early-initializers.md (100%) diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index c6cb1dc113de..b73a4aa56071 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -295,57 +295,6 @@ It is a static error if the inheritance closure of a class type consists of an i [^kennedy]: Kennedy, Pierce. [On Decidability of Nominal Subtyping with Variance.]( https://research.microsoft.com/pubs/64041/fool2007.pdf) in FOOL 2007 -### Early Definitions - -```ebnf -EarlyDefs ::= ‘{’ [EarlyDef {semi EarlyDef}] ‘}’ ‘with’ -EarlyDef ::= {Annotation} {Modifier} PatVarDef -``` - -A template may start with an _early field definition_ clause, which serves to define certain field values before the supertype constructor is called. -In a template - -```scala -{ val ´p_1´: ´T_1´ = ´e_1´ - ... - val ´p_n´: ´T_n´ = ´e_n´ -} with ´sc´ with ´mt_1´ with ´mt_n´ { ´\mathit{stats}´ } -``` - -The initial pattern definitions of ´p_1 , \ldots , p_n´ are called _early definitions_. -They define fields which form part of the template. -Every early definition must define at least one variable. - -An early definition is type-checked and evaluated in the scope which is in effect just before the template being defined, augmented by any type parameters of the enclosing class and by any early definitions preceding the one being defined. -In particular, any reference to `this` in an early definition refers to the identity of `this` just outside the template. -Consequently, it is impossible for an early definition to refer to the object being constructed by the template, or to refer to one of its fields and methods, except for any other preceding early definition in the same section. -Furthermore, references to preceding early definitions always refer to the value that's defined there and do not take into account overriding definitions. -In other words, a block of early definitions is evaluated exactly as if it were a local block containing a number of value definitions. - -Early definitions are evaluated before the superclass constructor of the template is called, in the order they are defined. - -###### Example -Early definitions are particularly useful for traits, which do not have normal constructor parameters. -Example: - -```scala -trait Greeting { - val name: String - val msg = "How are you, "+name -} -class C extends { - val name = "Bob" -} with Greeting { - println(msg) -} -``` - -In the code above, the field `name` is initialized before the constructor of `Greeting` is called. -Therefore, field `msg` in class `Greeting` is properly initialized to `"How are you, Bob"`. - -If `name` had been initialized instead in `C`'s normal class body, it would be initialized after the constructor of `Greeting`. -In that case, `msg` would be initialized to `"How are you, "`. - ## Modifiers ```ebnf @@ -592,7 +541,7 @@ A constructor expression is either a self constructor invocation `this(´\mathit The self constructor invocation must construct a generic instance of the class. I.e. if the class in question has name ´C´ and type parameters `[´\mathit{tps}\,´]`, then a self constructor invocation must generate an instance of `´C´[´\mathit{tps}\,´]`; it is not permitted to instantiate formal type parameters. -The signature and the self constructor invocation of a constructor definition are type-checked and evaluated in the scope which is in effect at the point of the enclosing class definition, augmented by any type parameters of the enclosing class and by any [early definitions](#early-definitions) of the enclosing template. +The signature and the self constructor invocation of a constructor definition are type-checked and evaluated in the scope which is in effect at the point of the enclosing class definition, augmented by any type parameters of the enclosing class. The rest of the constructor expression is type-checked and evaluated as a method body in the current class. If there are auxiliary constructors of a class ´C´, they form together with ´C´'s primary [constructor](#class-definitions) an overloaded constructor definition. diff --git a/docs/_spec/TODOreference/dropped-features/early-initializers.md b/docs/_spec/APPLIEDreference/dropped-features/early-initializers.md similarity index 100% rename from docs/_spec/TODOreference/dropped-features/early-initializers.md rename to docs/_spec/APPLIEDreference/dropped-features/early-initializers.md From 1578032e99e0fb6133a80913072ef26f25c8d163 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 15:22:55 +0200 Subject: [PATCH 428/657] Apply existential-types.md --- docs/_spec/03-types.md | 109 +----------------- docs/_spec/06-expressions.md | 21 +--- docs/_spec/07-implicits.md | 3 +- docs/_spec/A2-scala-2-compatibility.md | 19 +++ .../dropped-features/existential-types.md | 0 5 files changed, 29 insertions(+), 123 deletions(-) create mode 100644 docs/_spec/A2-scala-2-compatibility.md rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/existential-types.md (100%) diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index 42157b176888..29427bafbfce 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -349,50 +349,12 @@ trait Function´_n´[-´T_1´, ..., -´T_n´, +´U´] { Hence, function types are [covariant](04-basic-declarations-and-definitions.html#variance-annotations) in their result type and contravariant in their argument types. -### Existential Types - -```ebnf -Type ::= InfixType ExistentialClauses -ExistentialClauses ::= ‘forSome’ ‘{’ ExistentialDcl - {semi ExistentialDcl} ‘}’ -ExistentialDcl ::= ‘type’ TypeDcl - | ‘val’ ValDcl -``` - -An _existential type_ has the form `´T´ forSome { ´Q´ }` where ´Q´ is a sequence of [type declarations](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases). - -Let ´t_1[\mathit{tps}\_1] >: L_1 <: U_1 , \ldots , t_n[\mathit{tps}\_n] >: L_n <: U_n´ be the types declared in ´Q´ (any of the type parameter sections `[ ´\mathit{tps}_i´ ]` might be missing). -The scope of each type ´t_i´ includes the type ´T´ and the existential clause ´Q´. -The type variables ´t_i´ are said to be _bound_ in the type `´T´ forSome { ´Q´ }`. -Type variables which occur in a type ´T´ but which are not bound in ´T´ are said to be _free_ in ´T´. - -A _type instance_ of `´T´ forSome { ´Q´ }` is a type ´\sigma T´ where ´\sigma´ is a substitution over ´t_1 , \ldots , t_n´ such that, for each ´i´, ´\sigma L_i <: \sigma t_i <: \sigma U_i´. -The set of values denoted by the existential type `´T´ forSome {´\,Q\,´}` is the union of the set of values of all its type instances. - -A _skolemization_ of `´T´ forSome { ´Q´ }` is a type instance ´\sigma T´, where ´\sigma´ is the substitution ´[t_1'/t_1 , \ldots , t_n'/t_n]´ and each ´t_i'´ is a fresh abstract type with lower bound ´\sigma L_i´ and upper bound ´\sigma U_i´. - -#### Simplification Rules - -Existential types obey the following four equivalences: - -1. Multiple for-clauses in an existential type can be merged. E.g., `´T´ forSome { ´Q´ } forSome { ´Q'´ }` is equivalent to `´T´ forSome { ´Q´ ; ´Q'´}`. -1. Unused quantifications can be dropped. -E.g., `´T´ forSome { ´Q´ ; ´Q'´}` where none of the types defined in ´Q'´ are referred to by ´T´ or ´Q´, is equivalent to `´T´ forSome {´ Q ´}`. -1. An empty quantification can be dropped. E.g., `´T´ forSome { }` is equivalent to ´T´. -1. An existential type `´T´ forSome { ´Q´ }` where ´Q´ contains a clause `type ´t[\mathit{tps}] >: L <: U´` is equivalent to the type `´T'´ forSome { ´Q´ }` where ´T'´ results from ´T´ by replacing every [covariant occurrence](04-basic-declarations-and-definitions.html#variance-annotations) of ´t´ in ´T´ by ´U´ and by replacing every contravariant occurrence of ´t´ in ´T´ by ´L´. - -#### Existential Quantification over Values - -As a syntactic convenience, the bindings clause in an existential type may also contain value declarations `val ´x´: ´T´`. -An existential type `´T´ forSome { ´Q´; val ´x´: ´S\,´;´\,Q'´ }` is treated as a shorthand for the type `´T'´ forSome { ´Q´; type ´t´ <: ´S´ with Singleton; ´Q'´ }`, where ´t´ is a fresh type name and ´T'´ results from ´T´ by replacing every occurrence of `´x´.type` with ´t´. - -#### Placeholder Syntax for Existential Types +#### Wildcard Types ```ebnf WildcardType ::= ‘_’ TypeBounds ``` - -Scala supports a placeholder syntax for existential types. + A _wildcard type_ is of the form `_´\;´>:´\,L\,´<:´\,U´`. Both bound clauses may be omitted. If a lower bound clause `>:´\,L´` is missing, `>:´\,´scala.Nothing` is assumed. @@ -411,60 +373,6 @@ where ´t´ is some fresh type variable. Wildcard types may also appear as parts of [infix types](#infix-types), [function types](#function-types), or [tuple types](#tuple-types). Their expansion is then the expansion in the equivalent parameterized type. -###### Example - -Assume the class definitions - -```scala -class Ref[T] -abstract class Outer { type T } -``` - -Here are some examples of existential types: - -```scala -Ref[T] forSome { type T <: java.lang.Number } -Ref[x.T] forSome { val x: Outer } -Ref[x_type # T] forSome { type x_type <: Outer with Singleton } -``` - -The last two types in this list are equivalent. -An alternative formulation of the first type above using wildcard syntax is: - -```scala -Ref[_ <: java.lang.Number] -``` - -###### Example - -The type `List[List[_]]` is equivalent to the existential type - -```scala -List[List[t] forSome { type t }] -``` - -###### Example - -Assume a covariant type - -```scala -class List[+T] -``` - -The type - -```scala -List[T] forSome { type T <: java.lang.Number } -``` - -is equivalent (by simplification rule 4 above) to - -```scala -List[java.lang.Number] forSome { type T <: java.lang.Number } -``` - -which is in turn equivalent (by simplification rules 2 and 3 above) to `List[java.lang.Number]`. - ## Non-Value Types The types explained in the following do not denote sets of values, nor do they appear explicitly in programs. @@ -599,18 +507,15 @@ These notions are defined mutually recursively as follows. If ´T´ is an alias or abstract type, the previous clauses apply. Otherwise, ´T´ must be a (possibly parameterized) class type, which is defined in some class ´B´. Then the base types of `´S´#´T´` are the base types of ´T´ in ´B´ seen from the prefix type ´S´. - - The base types of an existential type `´T´ forSome { ´Q´ }` are all types `´S´ forSome { ´Q´ }` where ´S´ is a base type of ´T´. 1. The notion of a type ´T´ _in class ´C´ seen from some prefix type ´S´_ makes sense only if the prefix type ´S´ has a type instance of class ´C´ as a base type, say `´S'´#´C´[´T_1, ..., T_n´]`. Then we define as follows. - If `´S´ = ´\epsilon´.type`, then ´T´ in ´C´ seen from ´S´ is ´T´ itself. - - Otherwise, if ´S´ is an existential type `´S'´ forSome { ´Q´ }`, and ´T´ in ´C´ seen from ´S'´ is ´T'´, then ´T´ in ´C´ seen from ´S´ is `´T'´ forSome {´\,Q\,´}`. - Otherwise, if ´T´ is the ´i´'th type parameter of some class ´D´, then - If ´S´ has a base type `´D´[´U_1, ..., U_n´]`, for some type parameters `[´U_1, ..., U_n´]`, then ´T´ in ´C´ seen from ´S´ is ´U_i´. - Otherwise, if ´C´ is defined in a class ´C'´, then ´T´ in ´C´ seen from ´S´ is the same as ´T´ in ´C'´ seen from ´S'´. - Otherwise, if ´C´ is not defined in another class, then ´T´ in ´C´ seen from ´S´ is ´T´ itself. - - Otherwise, if ´T´ is the singleton type `´D´.this.type` for some class ´D´ - then + - Otherwise, if ´T´ is the singleton type `´D´.this.type` for some class ´D´ then - If ´D´ is a subclass of ´C´ and ´S´ has a type instance of class ´D´ among its base types, then ´T´ in ´C´ seen from ´S´ is ´S´. - Otherwise, if ´C´ is defined in a class ´C'´, then ´T´ in ´C´ seen from ´S´ is the same as ´T´ in ´C'´ seen from ´S'´. - Otherwise, if ´C´ is not defined in another class, then ´T´ in ´C´ seen from ´S´ is ´T´ itself. @@ -676,11 +581,10 @@ The conformance relation ´(<:)´ is the smallest transitive relation that satis 1. If the ´i´'th type parameter of ´T´ is declared neither covariant nor contravariant, then ´U_i \equiv T_i´. - A compound type `´T_1´ with ... with ´T_n´ {´R\,´}` conforms to each of its component types ´T_i´. - If ´T <: U_i´ for ´i \in \{ 1, ..., n \}´ and for every binding ´d´ of a type or value ´x´ in ´R´ there exists a member binding of ´x´ in ´T´ which subsumes ´d´, then ´T´ conforms to the compound type `´U_1´ with ... with ´U_n´ {´R\,´}`. -- The existential type `´T´ forSome {´\,Q\,´}` conforms to ´U´ if its [skolemization](#existential-types) conforms to ´U´. -- The type ´T´ conforms to the existential type `´U´ forSome {´\,Q\,´}` if ´T´ conforms to one of the [type instances](#existential-types) of `´U´ forSome {´\,Q\,´}`. - If ´T_i \equiv T_i'´ for ´i \in \{ 1, ..., n\}´ and ´U´ conforms to ´U'´ then the method type ´(p_1:T_1, ..., p_n:T_n) U´ conforms to ´(p_1':T_1', ..., p_n':T_n') U'´. - The polymorphic type ´[a_1 >: L_1 <: U_1, ..., a_n >: L_n <: U_n] T´ conforms to the polymorphic type ´[a_1 >: L_1' <: U_1', ..., a_n >: L_n' <: U_n'] T'´ if, assuming ´L_1' <: a_1 <: U_1', ..., L_n' <: a_n <: U_n'´ one has ´T <: T'´ and ´L_i <: L_i'´ and ´U_i' <: U_i´ for ´i \in \{ 1, ..., n \}´. -- Type constructors ´T´ and ´T'´ follow a similar discipline. We characterize ´T´ and ´T'´ by their type parameter clauses ´[a_1, ..., a_n]´ and ´[a_1', ..., a_n']´, where an ´a_i´ or ´a_i'´ may include a variance annotation, a higher-order type parameter clause, and bounds. +- Type constructors ´T´ and ´T'´ follow a similar discipline. +We characterize ´T´ and ´T'´ by their type parameter clauses ´[a_1, ..., a_n]´ and ´[a_1', ..., a_n']´, where an ´a_i´ or ´a_i'´ may include a variance annotation, a higher-order type parameter clause, and bounds. Then, ´T´ conforms to ´T'´ if any list ´[t_1, ..., t_n]´ -- with declared variances, bounds and higher-order type parameter clauses -- of valid type arguments for ´T'´ is also a valid list of type arguments for ´T´ and ´T[t_1, ..., t_n] <: T'[t_1, ..., t_n]´. Note that this entails that: - The bounds on ´a_i´ must be weaker than the corresponding bounds declared for ´a'_i´. @@ -790,8 +694,6 @@ A type designator is volatile if it is an alias of a volatile type, or if it des A singleton type `´p´.type` is volatile, if the underlying type of path ´p´ is volatile. -An existential type `´T´ forSome {´\,Q\,´}` is volatile if ´T´ is volatile. - ## Type Erasure A type is called _generic_ if it contains type arguments or type variables. @@ -806,7 +708,6 @@ The erasure mapping is defined as follows. - The erasure of a singleton type `´p´.type` is the erasure of the type of ´p´. - The erasure of a type projection `´T´#´x´` is `|´T´|#´x´`. - The erasure of a compound type `´T_1´ with ... with ´T_n´ {´R\,´}` is the erasure of the intersection dominator of ´T_1, ..., T_n´. -- The erasure of an existential type `´T´ forSome {´\,Q\,´}` is ´|T|´. The _intersection dominator_ of a list of types ´T_1, ..., T_n´ is computed as follows. Let ´T_{i_1}, ..., T_{i_m}´ be the subsequence of types ´T_i´ which are not supertypes of some other type ´T_j´. diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index c2c4d1f56afa..3b39dec25829 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -59,6 +59,7 @@ When we write "expression ´e´ is expected to conform to type ´T´", we mean: The following skolemization rule is applied universally for every expression: If the type of an expression would be an existential type ´T´, then the type of the expression is assumed instead to be a [skolemization](03-types.html#existential-types) of ´T´. + Skolemization is reversed by type packing. Assume an expression ´e´ of type ´T´ and let ´t_1[\mathit{tps}\_1] >: L_1 <: U_1, ..., t_n[\mathit{tps}\_n] >: L_n <: U_n´ be all the type variables created by skolemization of some part of ´e´ which are free in ´T´. Then the _packed type_ of ´e´ is @@ -454,6 +455,7 @@ The final expression can be omitted, in which case the unit value `()` is assume The expected type of the final expression ´e´ is the expected type of the block. The expected type of all preceding statements is undefined. + The type of a block `´s_1´; ...; ´s_n´; ´e´` is `´T´ forSome {´\,Q\,´}`, where ´T´ is the type of ´e´ and ´Q´ contains [existential clauses](03-types.html#existential-types) for every value or type name which is free in ´T´ and which is defined locally in one of the statements ´s_1, ..., s_n´. We say the existential clause _binds_ the occurrence of the value or type name. Specifically, @@ -467,23 +469,7 @@ It is an error if ´c´ carries type parameters. Evaluation of the block entails evaluation of its statement sequence, followed by an evaluation of the final expression ´e´, which defines the result of the block. -A block expression `{´c_1´; ...; ´c_n´}` where ´s_1, ..., s_n´ are case clauses forms a [pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions). - -###### Example -Assuming a class `Ref[T](x: T)`, the block - -```scala -{ class C extends B {´\ldots´} ; new Ref(new C) } -``` - -has the type `Ref[_1] forSome { type _1 <: B }`. -The block - -```scala -{ class C extends B {´\ldots´} ; new C } -``` - -simply has type `B`, because with the rules [here](03-types.html#simplification-rules) the existentially quantified type `_1 forSome { type _1 <: B }` can be simplified to `B`. +A block expression `{´c_1´; ...; ´c_n´}` where ´c_1, ..., c_n´ are case clauses forms a [pattern matching anonymous function](08-pattern-matching.html#pattern-matching-anonymous-functions). ## Prefix, Infix, and Postfix Operations @@ -1190,6 +1176,7 @@ question: given - A parameterized method ´m´ of type `(´p_1:T_1, ..., p_n:T_n´)´U´` is _as specific as_ some other member ´m'´ of type ´S´ if ´m'´ is [applicable](#method-applications) to arguments `(´p_1, ..., p_n´)` of types ´T_1, ..., T_n´. - A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is as specific as some other member of type ´S´ if ´T´ is as specific as ´S´ under the assumption that for ´i = 1, ..., n´ each ´a_i´ is an abstract type name bounded from below by ´L_i´ and from above by ´U_i´. - A member of any other type is always as specific as a parameterized method or a polymorphic method. + - Given two members of types ´T´ and ´U´ which are neither parameterized nor polymorphic method types, the member of type ´T´ is as specific as the member of type ´U´ if the existential dual of ´T´ conforms to the existential dual of ´U´. Here, the existential dual of a polymorphic type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is `´T´ forSome { type ´a_1´ >: ´L_1´ <: ´U_1´, ..., type ´a_n´ >: ´L_n´ <: ´U_n´}`. The existential dual of every other type is the type itself. diff --git a/docs/_spec/07-implicits.md b/docs/_spec/07-implicits.md index a0db9dd9b418..85aaadda87e1 100644 --- a/docs/_spec/07-implicits.md +++ b/docs/_spec/07-implicits.md @@ -54,7 +54,7 @@ If there are no eligible identifiers under this rule, then, second, eligible are The _implicit scope_ of a type ´T´ consists of all [companion modules](05-classes-and-objects.html#object-definitions) of classes that are associated with the implicit parameter's type. Here, we say a class ´C´ is _associated_ with a type ´T´ if it is a [base class](05-classes-and-objects.html#class-linearization) of some part of ´T´. - + The _parts_ of a type ´T´ are: - if ´T´ is a compound type `´T_1´ with ... with ´T_n´`, the union of the parts of ´T_1, ..., T_n´, as well as ´T´ itself; @@ -64,7 +64,6 @@ The _parts_ of a type ´T´ are: - if ´T´ is a type alias, the parts of its expansion; - if ´T´ is an abstract type, the parts of its upper bound; - if ´T´ denotes an implicit conversion to a type with a method with argument types ´T_1, ..., T_n´ and result type ´U´, the union of the parts of ´T_1, ..., T_n´ and ´U´; -- the parts of quantified (existential or universal) and annotated types are defined as the parts of the underlying types (e.g., the parts of `T forSome { ... }` are the parts of `T`); - in all other cases, just ´T´ itself. Note that packages are internally represented as classes with companion modules to hold the package members. diff --git a/docs/_spec/A2-scala-2-compatibility.md b/docs/_spec/A2-scala-2-compatibility.md new file mode 100644 index 000000000000..f4a4970cb81a --- /dev/null +++ b/docs/_spec/A2-scala-2-compatibility.md @@ -0,0 +1,19 @@ + +### Existential Types + +Existential types using `forSome` (as in [SLS §3.2.12](https://www.scala-lang.org/files/archive/spec/2.13/03-types.html#existential-types)) are not available in Scala 3. +Therefore when reading an existential type from Scala 2, the following happens: + +Existential types that can be expressed using only wildcards (but not +`forSome`) are treated as refined types. +For instance, the type +```scala +Map[_ <: AnyRef, Int] +``` +is treated as the type `Map`, where the first type parameter +is upper-bounded by `AnyRef` and the second type parameter is an alias +of `Int`. + +When reading class files compiled with Scala 2, Scala 3 will do a best +effort to approximate existential types with its own types. It will +issue a warning that a precise emulation is not possible. \ No newline at end of file diff --git a/docs/_spec/TODOreference/dropped-features/existential-types.md b/docs/_spec/APPLIEDreference/dropped-features/existential-types.md similarity index 100% rename from docs/_spec/TODOreference/dropped-features/existential-types.md rename to docs/_spec/APPLIEDreference/dropped-features/existential-types.md From 4ef7f6047a65179d0b6baa7c65969048c385c478 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 12 Oct 2022 17:11:27 +0200 Subject: [PATCH 429/657] Add TODOs for later, Cleanup --- docs/_spec/03-types.md | 16 ++------ .../04-basic-declarations-and-definitions.md | 40 +------------------ 2 files changed, 4 insertions(+), 52 deletions(-) diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index 29427bafbfce..95da81d19850 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -63,7 +63,7 @@ StableId ::= id | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id ClassQualifier ::= ‘[’ id ‘]’ ``` - + Paths are not types themselves, but they can be a part of named types and in that function form a central role in Scala's type system. A path is one of the following. @@ -388,6 +388,7 @@ Method types associate to the right: ´(\mathit{Ps}\_1)(\mathit{Ps}\_2)U´ is tr A special case are types of methods without any parameters. They are written here `=> T`. Parameterless methods name expressions that are re-evaluated each time the parameterless method name is referenced. + Method types do not exist as types of values. If a method name is used as a value, its type is [implicitly converted](06-expressions.html#implicit-conversions) to a corresponding function type. @@ -642,20 +643,9 @@ A _weak least upper bound_ is a least upper bound with respect to weak conforman A type ´T´ is _compatible_ to a type ´U´ if ´T´ (or its corresponding function type) [weakly conforms](#weak-conformance) to ´U´ after applying [eta-expansion](06-expressions.html#eta-expansion). If ´T´ is a method type, it's converted to the corresponding function type. If the types do not weakly conform, the following alternatives are checked in order: -- [view application](07-implicits.html#views): there's an implicit view from ´T´ to ´U´; - dropping by-name modifiers: if ´U´ is of the shape `´=> U'´` (and ´T´ is not), `´T <:_w U'´`; - SAM conversion: if ´T´ corresponds to a function type, and ´U´ declares a single abstract method whose type [corresponds](06-expressions.html#sam-conversion) to the function type ´U'´, `´T <:_w U'´`. - - +- [implicit conversion](07-implicits.html#views): there's an implicit conversion from ´T´ to ´U´ in scope; #### Examples diff --git a/docs/_spec/04-basic-declarations-and-definitions.md b/docs/_spec/04-basic-declarations-and-definitions.md index af98eef2a37d..a4f5b6d307c2 100644 --- a/docs/_spec/04-basic-declarations-and-definitions.md +++ b/docs/_spec/04-basic-declarations-and-definitions.md @@ -40,53 +40,15 @@ by commas. These are expanded according to the following scheme: \VAL;x, y: T = e && \VAL; x: T = e \\ && \VAL; y: T = x \\[0.5em] -\LET;x, y: T = e && \LET; x: T = e \\ - && \VAL; y: T = x \\[0.5em] - -\DEF;x, y (ps): T = e &\tab\mbox{expands to}\tab& \DEF; x(ps): T = e \\ - && \DEF; y(ps): T = x(ps)\\[0.5em] - \VAR;x, y: T := e && \VAR;x: T := e\\ && \VAR;y: T := x\\[0.5em] - -\TYPE;t,u = T && \TYPE; t = T\\ - && \TYPE; u = t\\[0.5em] \eda -All definitions have a ``repeated form`` where the initial -definition keyword is followed by several constituent definitions -which are separated by commas. A repeated definition is -always interpreted as a sequence formed from the -constituent definitions. E.g. the function definition -`def f(x) = x, g(y) = y` expands to -`def f(x) = x; def g(y) = y` and -the type definition -`type T, U <: B` expands to -`type T; type U <: B`. -} -\comment{ -If an element in such a sequence introduces only the defined name, -possibly with some type or value parameters, but leaves out any -additional parts in the definition, then those parts are implicitly -copied from the next subsequent sequence element which consists of -more than just a defined name and parameters. Examples: - -- [] The variable declaration `var x, y: Int` expands to `var x: Int; var y: Int`. -- [] + The value definition `val x, y: Int = 1` expands to `val x: Int = 1; val y: Int = 1`. -- [] -The class definition `case class X(), Y(n: Int) extends Z` expands to -`case class X extends Z; case class Y(n: Int) extends Z`. -- The object definition `case object Red, Green, Blue extends Color`~ -expands to -```scala -case object Red extends Color -case object Green extends Color -case object Blue extends Color -``` --> ## Value Declarations and Definitions From d5e2a69cc3eaa17dc04e3937d3ba072d2f02e0aa Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Wed, 19 Oct 2022 14:55:57 +0200 Subject: [PATCH 430/657] Apply limit22.md --- docs/_spec/03-types.md | 38 +++++++++++-------- docs/_spec/06-expressions.md | 13 ++++++- docs/_spec/08-pattern-matching.md | 10 ++++- docs/_spec/12-the-scala-standard-library.md | 36 ++++++++++++++---- .../dropped-features/limit22.md | 0 5 files changed, 70 insertions(+), 27 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/limit22.md (100%) diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index 95da81d19850..acb3a37faf14 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -217,22 +217,27 @@ G[S, Int] // illegal: S constrains its parameter to SimpleType ::= ‘(’ Types ‘)’ ``` -A _tuple type_ ´(T_1 , ... , T_n)´ is an alias for the class `scala.Tuple´n´[´T_1´, ... , ´T_n´]`, where ´n \geq 2´. +A _tuple type_ ´(T_1, ..., T_n)´ where ´n \geq 2´ is an alias for the type `´T_1´ *: ... *: ´T_n´ *: scala.EmptyTuple`. + +Note: +`(´T´)` is just the type ´T´, and not `´T´ *: scala.EmptyTuple`. +`()` is not a valid type, and not `scala.EmptyTuple`. + +If ´n \leq 22´, the type `´T_1´ *: ... *: ´T_n´ *: scala.EmptyTuple` is both a subtype and a supertype of tuple class `scala.Tuple´_n´[´T_1´, ..., ´T_n´]`. Tuple classes are case classes whose fields can be accessed using selectors `_1`, ..., `_n`. -Their functionality is abstracted in a corresponding `Product` trait. +Their functionality is abstracted in the corresponding `scala.Product_´n´` trait. The _n_-ary tuple class and product trait are defined at least as follows in the standard Scala library (they might also add other methods and implement other traits). ```scala case class Tuple´_n´[+´T_1´, ..., +´T_n´](_1: ´T_1´, ..., _n: ´T_n´) extends Product´_n´[´T_1´, ..., ´T_n´] -trait Product´_n´[+´T_1´, ..., +´T_n´] { +trait Product´_n´[+´T_1´, ..., +´T_n´] extends Product: override def productArity = ´n´ def _1: ´T_1´ ... def _n: ´T_n´ -} ``` ### Annotated Types @@ -329,25 +334,26 @@ FunctionArgs ::= InfixType | ‘(’ [ ParamType {‘,’ ParamType } ] ‘)’ ``` -The type ´(T_1, ..., T_n) \Rightarrow U´ represents the set of function values that take arguments of types ´T_1, ..., Tn´ and yield results of type ´U´. -In the case of exactly one argument type ´T \Rightarrow U´ is a shorthand for ´(T) \Rightarrow U´. -An argument type of the form ´\Rightarrow T´ represents a [call-by-name parameter](04-basic-declarations-and-definitions.html#by-name-parameters) of type ´T´. +The type ´(T_1, ..., T_n) \Rightarrow R´ represents the set of function values that take arguments of types ´T_1, ..., Tn´ and yield results of type ´R´. +The case of exactly one argument type ´T \Rightarrow R´ is a shorthand for ´(T) \Rightarrow R´. +An argument type of the form ´\Rightarrow T´ represents a [call-by-name parameter](04-basic-declarations-and-definitions.md#by-name-parameters) of type ´T´. -Function types associate to the right, e.g. ´S \Rightarrow T \Rightarrow U´ is the same as ´S \Rightarrow (T \Rightarrow U)´. +Function types associate to the right, e.g. ´S \Rightarrow T \Rightarrow R´ is the same as ´S \Rightarrow (T \Rightarrow R)´. + +Function types are [covariant](04-basic-declarations-and-definitions.md#variance-annotations) in their result type and [contravariant](04-basic-declarations-and-definitions.md#variance-annotations) in their argument types. Function types are shorthands for class types that define an `apply` method. -Specifically, the ´n´-ary function type ´(T_1 , \ldots , T_n) \Rightarrow U´ is a shorthand for the class type `Function´_n´[´T_1´ , … , ´T_n´, ´U´]`. -Such class types are defined in the Scala library for ´n´ between 0 and 22 as follows. +Specifically, the ´n´-ary function type ´(T_1, ..., T_n) \Rightarrow R´ is a shorthand for the class type `Function´_n´[´T_1´, ..., ´T_n´, ´R´]`. +In particular ´() \Rightarrow R´ is a shorthand for class type `Function´_0´[´R´]`. + +Such class types behave as if they were instances of the following trait: ```scala -package scala -trait Function´_n´[-´T_1´, ..., -´T_n´, +´U´] { - def apply(´x_1´: ´T_1´, ..., ´x_n´: ´T_n´): ´U´ - override def toString = "" -} +trait Function´_n´[-´T_1´, ..., -´T_n´, +´R´]: + def apply(´x_1´: ´T_1´, ..., ´x_n´: ´T_n´): ´R´ ``` -Hence, function types are [covariant](04-basic-declarations-and-definitions.html#variance-annotations) in their result type and contravariant in their argument types. +Their exact supertype and implementation can be consulted in the [function classes section](./12-the-scala-standard-library.md#the-function-classes) of the standard library page in this document. #### Wildcard Types diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index 3b39dec25829..8db2c1e66c98 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -381,9 +381,15 @@ Type applications can be omitted if [local type inference](#local-type-inference ```ebnf SimpleExpr ::= ‘(’ [Exprs] ‘)’ ``` +A _tuple expression_ `(´e_1´, ..., ´e_n´)` where ´n \geq 2´ is equivalent to the expression `´e_1´ *: ... *: ´e_n´ *: scala.EmptyTuple`. -A _tuple expression_ `(´e_1´, ..., ´e_n´)` is an alias for the class instance creation `scala.Tuple´n´(´e_1´, ..., ´e_n´)`, where ´n \geq 2´. -The empty tuple `()` is the unique value of type `scala.Unit`. +Note: +As calls to `*:` are slow, a more efficient translation is free to be implemented. +For example, `(´e_1´, ´e_2´)` could be translated to `scala.Tuple2(´e_1´, ´e_2´)`, which is indeed equivalent to `´e_1´ *: ´e_2´ *: scala.EmptyTuple`. + +Note: +The expression `(´e_1´)` is not equivalent to `´e_1´ *: scala.EmptyTuple`, but instead a regular parenthesized expression. +The expression `()` is not an alias for `scala.EmptyTuple`, but instead the unique value of type `scala.Unit`. ## Instance Creation Expressions @@ -909,6 +915,9 @@ Binding ::= (id | ‘_’) [‘:’ Type] The anonymous function of arity ´n´, `(´x_1´: ´T_1, ..., x_n´: ´T_n´) => e` maps parameters ´x_i´ of types ´T_i´ to a result given by expression ´e´. The scope of each formal parameter ´x_i´ is ´e´. Formal parameters must have pairwise distinct names. +Type bindings can be omitted, in which case the compiler will attempt to infer valid bindings. + +Note: `() => ´e´` defines a nullary function (´n´ = 0), and not for example `(_: Unit) => ´e´`. In the case of a single untyped formal parameter, `(´x\,´) => ´e´` can be abbreviated to `´x´ => ´e´`. If an anonymous function `(´x´: ´T\,´) => ´e´` with a single typed parameter appears as the result expression of a block, it can be abbreviated to `´x´: ´T´ => e`. diff --git a/docs/_spec/08-pattern-matching.md b/docs/_spec/08-pattern-matching.md index 97fb73d58b06..df1c396631ea 100644 --- a/docs/_spec/08-pattern-matching.md +++ b/docs/_spec/08-pattern-matching.md @@ -185,7 +185,15 @@ This is further discussed [here](#pattern-sequences). SimplePattern ::= ‘(’ [Patterns] ‘)’ ``` -A _tuple pattern_ `(´p_1´, ..., ´p_n´)` is an alias for the constructor pattern `scala.Tuple´n´(´p_1´, ..., ´p_n´)`, where ´n \geq 2´. The empty tuple `()` is the unique value of type `scala.Unit`. +A _tuple pattern_ `(´p_1´, ..., ´p_n´)` where ´n \geq 2´ is equivalent to `´p_1´ *: ... *: ´p_n´ *: scala.EmptyTuple`. + +Note: +`()` is equivalent to `_: scala.Unit`, and not `scala.EmptyTuple`. +`(´pat´)` is a pattern matching ´pat´, and not `´pat´ *: scala.EmptyTuple`. + +Note: +As such patterns with `*:` are slow, a more efficient translation is free to be implemented. +For example, `(´p_1´, ´p_2´)` could be translated to `scala.Tuple2(´p_1´, ´p_2´)`, which is indeed equivalent to `´p_1´ *: ´p_2´ *: scala.EmptyTuple`. ### Extractor Patterns diff --git a/docs/_spec/12-the-scala-standard-library.md b/docs/_spec/12-the-scala-standard-library.md index 401735286a51..bea9c9c574d4 100644 --- a/docs/_spec/12-the-scala-standard-library.md +++ b/docs/_spec/12-the-scala-standard-library.md @@ -288,6 +288,7 @@ def + (that: Any): String which concatenates its left operand with the textual representation of its right operand. + ### The `Function` Classes -Scala defines function classes `Function´n´` for ´n = 1 , \ldots , 22´. -These are defined as follows. +For each class type `Function´n´` where ´n = 0, ..., 22´, Scala defines the following function class: ```scala package scala -trait Function´n´[-T_1, ..., -T_´n´, +R] { - def apply(x_1: T_1, ..., x_´n´: T_´n´): R - def toString = "" -} +trait Function´_n´[-´T_1´, ..., -´T_n´, +´R´]: + def apply(´x_1´: ´T_1´, ..., ´x_n´: ´T_n´): ´R´ + override def toString = "" + def curried: ´T_1´ => ... => ´T_n´ => R = ... + def tupled: ((´T_1´, ..., ´T_n´)) => R = ... +``` + +For function types `Function´n´` where ´n > 22´, Scala defines a unique function class: + +```scala +package scala +trait FunctionXXL: + def apply(xs: IArray[Object]): Object + override def toString = "" ``` +There is no loss of type safety, as the internal representation is still `Function´n´` for all ´n´. +However this means methods `curried` and `tupled` are not available on functions with more than 22 parameters. + +The implicitly imported [`Predef`](#the-predef-object) object defines the name +`Function` as an alias of `Function1`. + + The `PartialFunction` subclass of `Function1` represents functions that (indirectly) specify their domain. Use the `isDefined` method to query whether the partial function is defined for a given input (i.e., whether the input is part of the function's domain). @@ -322,7 +339,10 @@ class PartialFunction[-A, +B] extends Function1[A, B] { } ``` -The implicitly imported [`Predef`](#the-predef-object) object defines the name `Function` as an alias of `Function1`. +### Trait `Product` + + +All case classes automatically extend the `Product` trait (and generate synthetic methods to conform to it) (but not `Product´n´`), and define a `_´n´` method for each of their arguments. ### Class `Array` diff --git a/docs/_spec/TODOreference/dropped-features/limit22.md b/docs/_spec/APPLIEDreference/dropped-features/limit22.md similarity index 100% rename from docs/_spec/TODOreference/dropped-features/limit22.md rename to docs/_spec/APPLIEDreference/dropped-features/limit22.md From 3b27b5793c615bda71e69fc4a947bc218f9723d4 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 14 Oct 2022 14:00:21 +0200 Subject: [PATCH 431/657] Small fixes --- docs/_spec/06-expressions.md | 2 +- docs/_spec/12-the-scala-standard-library.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index 8db2c1e66c98..16f2f527a9dc 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -967,7 +967,7 @@ x => x // The identity function f => g => x => f(g(x)) // Curried function composition -(x: Int,y: Int) => x + y // A summation function +(x: Int, y: Int) => x + y // A summation function () => { count += 1; count } // The function which takes an // empty parameter list ´()´, diff --git a/docs/_spec/12-the-scala-standard-library.md b/docs/_spec/12-the-scala-standard-library.md index bea9c9c574d4..631596e0d272 100644 --- a/docs/_spec/12-the-scala-standard-library.md +++ b/docs/_spec/12-the-scala-standard-library.md @@ -621,7 +621,7 @@ object Predef { def assume(assumption: Boolean, message: => Any) { if (!assumption) - throw new IllegalArgumentException(message.toString) + throw new IllegalArgumentException("assumption failed: " + message.toString) } def require(requirement: Boolean) { From 7cd5073b7b8251e76354c56b94fe982a7c5a86c1 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 14 Oct 2022 14:00:39 +0200 Subject: [PATCH 432/657] Add TODO: Update as Implicit Conversion --- docs/_spec/07-implicits.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/_spec/07-implicits.md b/docs/_spec/07-implicits.md index 85aaadda87e1..2cd80f227cd4 100644 --- a/docs/_spec/07-implicits.md +++ b/docs/_spec/07-implicits.md @@ -270,6 +270,7 @@ Throwable => Ordered[Throwable], Since the second type in the sequence is equal to the first, the compiler will issue an error signalling a divergent implicit expansion. + ## Views Implicit parameters and methods can also define implicit conversions called views. From d88f3b86d48bdf88f592f249cb352e18e24d462a Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 20 Oct 2022 14:07:36 +0200 Subject: [PATCH 433/657] Apply macros.md As Scala 2 macros were an experimental feature, they were not included in the spec, and the macro keyword is still a keyword there is thus nothing to do (Scala 3 macros will get their own commits through the corresponding pages) --- .../dropped-features/macros.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/macros.md (100%) diff --git a/docs/_spec/TODOreference/dropped-features/macros.md b/docs/_spec/APPLIEDreference/dropped-features/macros.md similarity index 100% rename from docs/_spec/TODOreference/dropped-features/macros.md rename to docs/_spec/APPLIEDreference/dropped-features/macros.md From 04ceaefa9b49d2f19883b5d34435809d70eeb82c Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 20 Oct 2022 15:38:52 +0200 Subject: [PATCH 434/657] Apply procedure-syntax.md --- .../04-basic-declarations-and-definitions.md | 37 ------------------- docs/_spec/A2-scala-2-compatibility.md | 18 ++++++++- .../dropped-features/procedure-syntax.md | 0 3 files changed, 17 insertions(+), 38 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/procedure-syntax.md (100%) diff --git a/docs/_spec/04-basic-declarations-and-definitions.md b/docs/_spec/04-basic-declarations-and-definitions.md index a4f5b6d307c2..4145f2b8c2e0 100644 --- a/docs/_spec/04-basic-declarations-and-definitions.md +++ b/docs/_spec/04-basic-declarations-and-definitions.md @@ -609,43 +609,6 @@ By contrast, the following application is well formed and yields again the resul sum(xs: _*) ``` -### Procedures - -```ebnf -FunDcl ::= FunSig -FunDef ::= FunSig [nl] ‘{’ Block ‘}’ -``` - -Special syntax exists for procedures, i.e. methods that return the `Unit` value `()`. -A _procedure declaration_ is a method declaration where the result type is omitted. -The result type is then implicitly completed to the `Unit` type. E.g., `def ´f´(´\mathit{ps}´)` is equivalent to `def ´f´(´\mathit{ps}´): Unit`. - -A _procedure definition_ is a method definition where the result type and the equals sign are omitted; its defining expression must be a block. -E.g., `def ´f´(´\mathit{ps}´) {´\mathit{stats}´}` is equivalent to `def ´f´(´\mathit{ps}´): Unit = {´\mathit{stats}´}`. - -###### Example -Here is a declaration and a definition of a procedure named `write`: - -```scala -trait Writer { - def write(str: String) -} -object Terminal extends Writer { - def write(str: String) { System.out.println(str) } -} -``` - -The code above is implicitly completed to the following code: - -```scala -trait Writer { - def write(str: String): Unit -} -object Terminal extends Writer { - def write(str: String): Unit = { System.out.println(str) } -} -``` - ### Method Return Type Inference A class member definition ´m´ that overrides some other method ´m'´ in a base class of ´C´ may leave out the return type, even if it is recursive. diff --git a/docs/_spec/A2-scala-2-compatibility.md b/docs/_spec/A2-scala-2-compatibility.md index f4a4970cb81a..3621887581a6 100644 --- a/docs/_spec/A2-scala-2-compatibility.md +++ b/docs/_spec/A2-scala-2-compatibility.md @@ -16,4 +16,20 @@ of `Int`. When reading class files compiled with Scala 2, Scala 3 will do a best effort to approximate existential types with its own types. It will -issue a warning that a precise emulation is not possible. \ No newline at end of file +issue a warning that a precise emulation is not possible. + +### Procedure Syntax + +Procedure syntax +```scala +def f() { ... } +``` +has been dropped. You need to write one of the following instead: +```scala +def f() = { ... } +def f(): Unit = { ... } +``` +Scala 3 accepts the old syntax under the `-source:3.0-migration` option. +If the `-migration` option is set, it can even rewrite old syntax to new. +The [Scalafix](https://scalacenter.github.io/scalafix/) tool also +can rewrite procedure syntax to make it Scala 3 compatible. \ No newline at end of file diff --git a/docs/_spec/TODOreference/dropped-features/procedure-syntax.md b/docs/_spec/APPLIEDreference/dropped-features/procedure-syntax.md similarity index 100% rename from docs/_spec/TODOreference/dropped-features/procedure-syntax.md rename to docs/_spec/APPLIEDreference/dropped-features/procedure-syntax.md From d76177002f03b14dea11721f939a6e6b05eed119 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 20 Oct 2022 16:07:09 +0200 Subject: [PATCH 435/657] Update return type inference Scala 3 does not infer more precise types for the return type of overriding methods. This can be seen in the following example: ```scala type T type U <: T abstract class A{ def m: T } class B extends A{ def m = ??? : U } val res: U = (new B).m ``` --- docs/_spec/04-basic-declarations-and-definitions.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/_spec/04-basic-declarations-and-definitions.md b/docs/_spec/04-basic-declarations-and-definitions.md index 4145f2b8c2e0..1d50fd649efd 100644 --- a/docs/_spec/04-basic-declarations-and-definitions.md +++ b/docs/_spec/04-basic-declarations-and-definitions.md @@ -612,9 +612,7 @@ sum(xs: _*) ### Method Return Type Inference A class member definition ´m´ that overrides some other method ´m'´ in a base class of ´C´ may leave out the return type, even if it is recursive. -In this case, the return type ´R'´ of the overridden method ´m'´, seen as a member of ´C´, is taken as the return type of ´m´ for each recursive invocation of ´m´. -That way, a type ´R´ for the right-hand side of ´m´ can be determined, which is then taken as the return type of ´m´. -Note that ´R´ may be different from ´R'´, as long as ´R´ conforms to ´R'´. +In this case, whether or not `m` is recursive, its return type will be the return type of ´m'´. ###### Example Assume the following definitions: From b61f22bf8af0128b47b1dcbd22d560ee1ba7f7fb Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Thu, 20 Oct 2022 16:20:30 +0200 Subject: [PATCH 436/657] Apply symlits.md --- docs/_spec/01-lexical-syntax.md | 13 +----------- docs/_spec/A1-deprecated.md | 21 +++++++++++++++++++ .../dropped-features/symlits.md | 0 3 files changed, 22 insertions(+), 12 deletions(-) create mode 100644 docs/_spec/A1-deprecated.md rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/symlits.md (100%) diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md index 862e85a014be..e2470e27d2e0 100644 --- a/docs/_spec/01-lexical-syntax.md +++ b/docs/_spec/01-lexical-syntax.md @@ -259,7 +259,7 @@ A single new line token is accepted ## Literals -There are literals for integer numbers, floating point numbers, characters, booleans, symbols, strings. +There are literals for integer numbers, floating point numbers, characters, booleans, strings. The syntax of these literals is in each case as in Java. ## Root Classes The root of this hierarchy is formed by class `Any`. From 60f528abc5ebe7fd8116b14461503ead44aeec5c Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Mon, 24 Oct 2022 16:03:41 +0200 Subject: [PATCH 438/657] Apply this-qualifier.md As the feature is not actually deprecated, no changes to the spec are needed --- docs/_spec/A3-to-be-deprecated.md | 4 ++++ .../dropped-features/this-qualifier.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 docs/_spec/A3-to-be-deprecated.md rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/this-qualifier.md (93%) diff --git a/docs/_spec/A3-to-be-deprecated.md b/docs/_spec/A3-to-be-deprecated.md new file mode 100644 index 000000000000..adc37111520c --- /dev/null +++ b/docs/_spec/A3-to-be-deprecated.md @@ -0,0 +1,4 @@ +This is a simple list of feature that are not deprecated yet, but will be in the future. +They should emit warnings or errors only when using the `-source:future` compiler flag. + +- [private[this] and protected[this]](../_docs/reference/dropped-features/this-qualifier.md) \ No newline at end of file diff --git a/docs/_spec/TODOreference/dropped-features/this-qualifier.md b/docs/_spec/APPLIEDreference/dropped-features/this-qualifier.md similarity index 93% rename from docs/_spec/TODOreference/dropped-features/this-qualifier.md rename to docs/_spec/APPLIEDreference/dropped-features/this-qualifier.md index 4fcadff8fae3..3fcaefb7e0d8 100644 --- a/docs/_spec/TODOreference/dropped-features/this-qualifier.md +++ b/docs/_spec/APPLIEDreference/dropped-features/this-qualifier.md @@ -4,7 +4,7 @@ title: "Dropped: private[this] and protected[this]" nightlyOf: https://docs.scala-lang.org/scala3/reference/dropped-features/this-qualifier.html --- -The `private[this]` and `protected[this]` access modifiers are deprecated and will be phased out. +The `private[this]` and `protected[this]` access modifiers will be deprecated and phased out. Previously, these modifiers were needed for From d546f59f56021dabf7c7cb954d6700210d51ce14 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Mon, 24 Oct 2022 18:08:07 +0200 Subject: [PATCH 439/657] TOFIXUP Disable indented blocks --- docs/_spec/_config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_spec/_config.yml b/docs/_spec/_config.yml index bd1f691c65d0..3cb9c8f0d440 100644 --- a/docs/_spec/_config.yml +++ b/docs/_spec/_config.yml @@ -8,4 +8,4 @@ highlighter: false markdown: redcarpet encoding: utf-8 redcarpet: - extensions: ["no_intra_emphasis", "fenced_code_blocks", "autolink", "tables", "with_toc_data", "strikethrough", "lax_spacing", "space_after_headers", "superscript", "footnotes"] + extensions: ["no_intra_emphasis", "fenced_code_blocks", "autolink", "tables", "with_toc_data", "strikethrough", "lax_spacing", "space_after_headers", "superscript", "footnotes", "disable_indented_code_blocks"] From 0ed921586903da9be28ccb732a77088b29442b11 Mon Sep 17 00:00:00 2001 From: Quentin Bernet Date: Fri, 3 Feb 2023 16:46:54 +0100 Subject: [PATCH 440/657] Reword section Method Application Will make it easier to explain later what happens when you apply a using method to explicit parameters --- docs/_spec/06-expressions.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index 16f2f527a9dc..662f460af0e1 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -201,8 +201,8 @@ ArgumentExprs ::= ‘(’ [Exprs] ‘)’ Exprs ::= Expr {‘,’ Expr} ``` -An application `´f(e_1, ..., e_m)´` applies the expression `´f´` to the argument expressions `´e_1, ..., e_m´`. -For the overal expression to be well-typed, ´f´ must be *applicable* to its arguments, which is defined next by case analysis on ´f´'s type. +An application `´f(e_1, ..., e_m)´` applies the method `´f´` to the argument expressions `´e_1, ..., e_m´`. +For this expression to be well-typed, the method must be *applicable* to its arguments: If ´f´ has a method type `(´p_1´:´T_1, ..., p_n´:´T_n´)´U´`, each argument expression ´e_i´ is typed with the corresponding parameter type ´T_i´ as expected type. Let ´S_i´ be the type of argument ´e_i´ ´(i = 1, ..., m)´. @@ -214,12 +214,12 @@ Once the types ´S_i´ have been determined, the method ´f´ of the above metho - for every positional argument ´e_i´ the type ´S_i´ is [compatible](03-types.html#compatibility) with ´T_i´; - if the expected type is defined, the result type ´U´ is [compatible](03-types.html#compatibility) to it. -If ´f´ is a polymorphic method, [local type inference](#local-type-inference) is used to instantiate ´f´'s type parameters. -The polymorphic method is applicable if type inference can determine type arguments so that the instantiated method is applicable. - -If ´f´ has some value type, the application is taken to be equivalent to `´f´.apply(´e_1, ..., e_m´)`, i.e. the application of an `apply` method defined by ´f´. -The value `´f´` is applicable to the given arguments if `´f´.apply` is applicable. +If ´f´ is instead of some value type, the application is taken to be equivalent to `´f´.apply(´e_1, ..., e_m´)`, i.e. the application of an `apply` method defined by ´f´. +Value `´f´` is applicable to the given arguments if `´f´.apply` is applicable. +Note: +In the case where ´f´ or `´f´.apply` is a polymorphic method, this is taken as an [ommitted type application](#type-applications). +`´f´` is applicable to the given arguments if the result of this type application is applicable. The application `´f´(´e_1, ..., e_n´)` evaluates ´f´ and then each argument ´e_1, ..., e_n´ from left to right, except for arguments that correspond to a by-name parameter (see below). Each argument expression is converted to the type of its corresponding formal parameter. From 1f62049a67d8119102269c2f0ed26d1742354505 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 10:37:27 +0200 Subject: [PATCH 441/657] Add TODOs and fix typos --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 6 +++--- compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 3427146a6baa..215998353816 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -682,7 +682,7 @@ object Trees { * they can be typed as a `Quote` with a known `tpt` or desugared and * typed as a quote pattern. * - * `Quotes` are checked transformed in the `staging`, `splicing` and `pickleQuotes` + * `Quotes` are checked and transformed in the `staging`, `splicing` and `pickleQuotes` * phases. After `pickleQuotes` phase, the only quotes that exist are in `inline` * methods. These are dropped when we remove the inline method implementations. * @@ -703,8 +703,8 @@ object Trees { * they can be typed as a `Splice` with a known `tpt` or desugared and * typed as a quote pattern holes. * - * `Splice` are checked transformed in the `staging` and `splicing` phases. - * After `splicing` phase, the only quotes that exist are in `inline` + * `Splice` are checked and transformed in the `staging` and `splicing` phases. + * After `splicing` phase, the only splices that exist are in `inline` * methods. These are dropped when we remove the inline method implementations. * * The `tpt` will be transformed in `staging` and used in `splicing` to create `Hole`s. diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index f81c0c2de56a..749fbe423dd3 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -59,6 +59,7 @@ trait QuotesAndSplices { report.error(msg, tree.srcPos) EmptyTree else + // TODO typecheck directly (without `exprQuote`) val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.expr) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt) @@ -106,9 +107,9 @@ trait QuotesAndSplices { markAsMacro(ctx) } + // TODO typecheck directly (without `exprSplice`) val internalSplice = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) - typedApply(internalSplice, pt)(using spliceContext).withSpan(tree.span) match case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => cpy.Splice(tree)(spliced, tpt) From 100f7bf48093644717b3363144722ee39938b6f4 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Fri, 28 Apr 2023 11:36:10 +0200 Subject: [PATCH 442/657] fixup formatting --- docs/_spec/03-types.md | 6 +++--- docs/_spec/06-expressions.md | 16 +++++++--------- docs/_spec/08-pattern-matching.md | 11 ++++------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index acb3a37faf14..5e1da82f1f11 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -219,9 +219,9 @@ SimpleType ::= ‘(’ Types ‘)’ A _tuple type_ ´(T_1, ..., T_n)´ where ´n \geq 2´ is an alias for the type `´T_1´ *: ... *: ´T_n´ *: scala.EmptyTuple`. -Note: -`(´T´)` is just the type ´T´, and not `´T´ *: scala.EmptyTuple`. -`()` is not a valid type, and not `scala.EmptyTuple`. +Notes: +- `(´T´)` is just the type ´T´, and not `´T´ *: scala.EmptyTuple`. +- `()` is not a valid type, and not `scala.EmptyTuple`. If ´n \leq 22´, the type `´T_1´ *: ... *: ´T_n´ *: scala.EmptyTuple` is both a subtype and a supertype of tuple class `scala.Tuple´_n´[´T_1´, ..., ´T_n´]`. diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index 662f460af0e1..bb90c2b071a4 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -217,9 +217,9 @@ Once the types ´S_i´ have been determined, the method ´f´ of the above metho If ´f´ is instead of some value type, the application is taken to be equivalent to `´f´.apply(´e_1, ..., e_m´)`, i.e. the application of an `apply` method defined by ´f´. Value `´f´` is applicable to the given arguments if `´f´.apply` is applicable. -Note: -In the case where ´f´ or `´f´.apply` is a polymorphic method, this is taken as an [ommitted type application](#type-applications). -`´f´` is applicable to the given arguments if the result of this type application is applicable. +Notes: +- In the case where ´f´ or `´f´.apply` is a polymorphic method, this is taken as an [ommitted type application](#type-applications). +- `´f´` is applicable to the given arguments if the result of this type application is applicable. The application `´f´(´e_1, ..., e_n´)` evaluates ´f´ and then each argument ´e_1, ..., e_n´ from left to right, except for arguments that correspond to a by-name parameter (see below). Each argument expression is converted to the type of its corresponding formal parameter. @@ -383,13 +383,11 @@ SimpleExpr ::= ‘(’ [Exprs] ‘)’ ``` A _tuple expression_ `(´e_1´, ..., ´e_n´)` where ´n \geq 2´ is equivalent to the expression `´e_1´ *: ... *: ´e_n´ *: scala.EmptyTuple`. -Note: -As calls to `*:` are slow, a more efficient translation is free to be implemented. -For example, `(´e_1´, ´e_2´)` could be translated to `scala.Tuple2(´e_1´, ´e_2´)`, which is indeed equivalent to `´e_1´ *: ´e_2´ *: scala.EmptyTuple`. +Note: as calls to `*:` are slow, a more efficient translation is free to be implemented. For example, `(´e_1´, ´e_2´)` could be translated to `scala.Tuple2(´e_1´, ´e_2´)`, which is indeed equivalent to `´e_1´ *: ´e_2´ *: scala.EmptyTuple`. -Note: -The expression `(´e_1´)` is not equivalent to `´e_1´ *: scala.EmptyTuple`, but instead a regular parenthesized expression. -The expression `()` is not an alias for `scala.EmptyTuple`, but instead the unique value of type `scala.Unit`. +Notes: +- The expression `(´e_1´)` is not equivalent to `´e_1´ *: scala.EmptyTuple`, but instead a regular parenthesized expression. +- The expression `()` is not an alias for `scala.EmptyTuple`, but instead the unique value of type `scala.Unit`. ## Instance Creation Expressions diff --git a/docs/_spec/08-pattern-matching.md b/docs/_spec/08-pattern-matching.md index df1c396631ea..f16f4c3dfa88 100644 --- a/docs/_spec/08-pattern-matching.md +++ b/docs/_spec/08-pattern-matching.md @@ -187,13 +187,10 @@ This is further discussed [here](#pattern-sequences). A _tuple pattern_ `(´p_1´, ..., ´p_n´)` where ´n \geq 2´ is equivalent to `´p_1´ *: ... *: ´p_n´ *: scala.EmptyTuple`. -Note: -`()` is equivalent to `_: scala.Unit`, and not `scala.EmptyTuple`. -`(´pat´)` is a pattern matching ´pat´, and not `´pat´ *: scala.EmptyTuple`. - -Note: -As such patterns with `*:` are slow, a more efficient translation is free to be implemented. -For example, `(´p_1´, ´p_2´)` could be translated to `scala.Tuple2(´p_1´, ´p_2´)`, which is indeed equivalent to `´p_1´ *: ´p_2´ *: scala.EmptyTuple`. +Notes: +- `()` is equivalent to `_: scala.Unit`, and not `scala.EmptyTuple`. +- `(´pat´)` is a pattern matching ´pat´, and not `´pat´ *: scala.EmptyTuple`. +- As such patterns with `*:` are slow, a more efficient translation is free to be implemented. For example, `(´p_1´, ´p_2´)` could be translated to `scala.Tuple2(´p_1´, ´p_2´)`, which is indeed equivalent to `´p_1´ *: ´p_2´ *: scala.EmptyTuple`. ### Extractor Patterns From 1cd9bd263e2fcd557c6779e29ad10f6d8299cf99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Fri, 28 Apr 2023 13:29:31 +0200 Subject: [PATCH 443/657] Set reference version to 3.3.0-RC5 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index a1245dfd8460..c5586c68f981 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -80,7 +80,7 @@ object DottyJSPlugin extends AutoPlugin { object Build { import ScaladocConfigs._ - val referenceVersion = "3.3.0-RC4" + val referenceVersion = "3.3.0-RC5" val baseVersion = "3.3.1-RC1" From 5ae7861eb246461bc0f970d0e446c2b080440d18 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 12:32:34 +0200 Subject: [PATCH 444/657] Remove `tpt` from `Quote` --- .../dotty/tools/dotc/CompilationUnit.scala | 2 +- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/ast/Trees.scala | 40 ++++++++++++------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 6 +-- .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- .../dotty/tools/dotc/inlines/Inliner.scala | 4 +- .../dotty/tools/dotc/parsing/Parsers.scala | 4 +- .../tools/dotc/printing/RefinedPrinter.scala | 6 +-- .../tools/dotc/staging/CrossStageSafety.scala | 4 +- .../dotc/staging/TreeMapWithStages.scala | 6 +-- .../tools/dotc/transform/MegaPhase.scala | 3 +- .../tools/dotc/transform/PickleQuotes.scala | 6 +-- .../tools/dotc/transform/PostTyper.scala | 2 +- .../dotty/tools/dotc/transform/Splicer.scala | 10 ++--- .../dotty/tools/dotc/transform/Splicing.scala | 10 ++--- .../tools/dotc/transform/patmat/Space.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 7 ++-- .../src/dotty/tools/dotc/typer/ReTyper.scala | 6 +-- .../dotty/tools/dotc/typer/TypeAssigner.scala | 6 --- .../quoted/runtime/impl/QuotesImpl.scala | 2 +- 22 files changed, 69 insertions(+), 67 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 9a41f685f6c3..8e4989f13c3d 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -157,7 +157,7 @@ object CompilationUnit { if tree.symbol.is(Flags.Inline) then containsInline = true tree match - case tpd.Quote(_, _) => + case tpd.Quote(_) => containsQuote = true case tree: tpd.Apply if tree.symbol == defn.QuotedTypeModule_of => containsQuote = true diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 5c2410d3fbdc..60962f0d1996 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1986,7 +1986,7 @@ object desugar { trees foreach collect case Block(Nil, expr) => collect(expr) - case Quote(expr, _) => + case Quote(expr) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { case Splice(expr, _) => collect(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 215998353816..e626a06829dd 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -678,23 +678,33 @@ object Trees { } /** A tree representing a quote `'{ expr }` - * `Quote`s are created by the `Parser` with an empty `tpt`. In typer - * they can be typed as a `Quote` with a known `tpt` or desugared and - * typed as a quote pattern. + * `Quote`s are created by the `Parser`. In typer they can be typed as a + * `Quote` with a known `tpt` or desugared and typed as a quote pattern. * * `Quotes` are checked and transformed in the `staging`, `splicing` and `pickleQuotes` * phases. After `pickleQuotes` phase, the only quotes that exist are in `inline` * methods. These are dropped when we remove the inline method implementations. * - * The `tpt` will be transformed in `staging` and used in `pickleQuotes` to create the - * pickled representation of the quote. - * * @param expr The tree that was quoted - * @param tpt The type of the tree that was quoted */ - case class Quote[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Quote[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Quote[T] + + /** Type of the quoted expression as seen from outside the quote */ + def exprType(using Context): Type = + val quoteType = typeOpt // Quotes ?=> Expr[T] + val exprType = quoteType.argInfos.last // Expr[T] + exprType.argInfos.head // T + + /** Set the type of the quoted expression as seen from outside the quote */ + def withExprType(tpe: Type)(using Context): Quote[Type] = + val exprType = // Expr[T] + defn.QuotedExprClass.typeRef.appliedTo(tpe) + val quoteType = // Quotes ?=> Expr[T] + defn.FunctionType(1, isContextual = true) + .appliedTo(defn.QuotesClass.typeRef, exprType) + withType(quoteType) } /** A tree representing a splice `${ expr }` @@ -1299,9 +1309,9 @@ object Trees { case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree))) } - def Quote(tree: Tree)(expr: Tree, tpt: Tree)(using Context): Quote = tree match { - case tree: Quote if (expr eq tree.expr) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.Quote(expr, tpt)(sourceFile(tree))) + def Quote(tree: Tree)(expr: Tree)(using Context): Quote = tree match { + case tree: Quote if (expr eq tree.expr) => tree + case _ => finalize(tree, untpd.Quote(expr)(sourceFile(tree))) } def Splice(tree: Tree)(expr: Tree, tpt: Tree)(using Context): Splice = tree match { case tree: Splice if (expr eq tree.expr) && (tpt eq tree.tpt) => tree @@ -1546,8 +1556,8 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case tree @ Quote(expr, tpt) => - cpy.Quote(tree)(transform(expr), transform(tpt)) + case tree @ Quote(expr) => + cpy.Quote(tree)(transform(expr)) case tree @ Splice(expr, tpt) => cpy.Splice(tree)(transform(expr), transform(tpt)) case tree @ Hole(_, _, args, content, tpt) => @@ -1691,8 +1701,8 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) - case Quote(expr, tpt) => - this(this(x, expr), tpt) + case Quote(expr) => + this(x, expr) case Splice(expr, tpt) => this(this(x, expr), tpt) case Hole(_, _, args, content, tpt) => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 66c0bedc12c3..78de609850db 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,8 +170,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - def Quote(expr: Tree, tpt: Tree)(using Context): Quote = - ta.assignType(untpd.Quote(expr, tpt), tpt) + def Quote(expr: Tree, tpe: Type)(using Context): Quote = + untpd.Quote(expr).withExprType(tpe) def Splice(expr: Tree, tpt: Tree)(using Context): Splice = ta.assignType(untpd.Splice(expr, tpt), tpt) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 45b2fcd49595..4fdd9115f2c9 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -397,7 +397,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): SeqLiteral = new SeqLiteral(elems, elemtpt) def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) - def Quote(expr: Tree, tpt: Tree)(implicit src: SourceFile): Quote = new Quote(expr, tpt) + def Quote(expr: Tree)(implicit src: SourceFile): Quote = new Quote(expr) def Splice(expr: Tree, tpt: Tree)(implicit src: SourceFile): Splice = new Splice(expr, tpt) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 864004a3e5ec..6346c2f3dd03 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,11 +665,11 @@ class TreePickler(pickler: TastyPickler) { pickleTree(hi) pickleTree(alias) } - case Quote(expr, tpt) => + case tree @ Quote(expr) => pickleTree( - // scala.quoted.runtime.Expr.quoted[]() + // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) - .appliedToTypeTree(tpt) + .appliedToType(tree.exprType) .appliedTo(expr) .withSpan(tree.span) ) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 79d2008eb698..02e79854c666 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1269,7 +1269,7 @@ class TreeUnpickler(reader: TastyReader, def quotedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - Quote(args.head, targs.head) + Quote(args.head, targs.head.tpe) def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index e2fd4c6688c1..8f3c4df237a6 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -827,7 +827,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = super.typedQuote(tree, pt) match - case Quote(Splice(inner, _), _) => inner + case Quote(Splice(inner, _)) => inner case tree1 => ctx.compilationUnit.needsStaging = true tree1 @@ -1070,7 +1070,7 @@ class Inliner(val call: tpd.Tree)(using Context): else tree match { case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) - case Quote(body, _) => + case Quote(body) => level += 1 try apply(syms, body) finally level -= 1 diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index c2e2e4466d98..d98f407edc73 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1262,7 +1262,7 @@ object Parsers { } } in.nextToken() - Quote(t, EmptyTree) + Quote(t) } else if !in.featureEnabled(Feature.symbolLiterals) then @@ -2497,7 +2497,7 @@ object Parsers { val expr = if (in.token == LBRACKET) inBrackets(typ()) else stagedBlock() - Quote(expr, EmptyTree) + Quote(expr) } } case NEW => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index b2b9196b0a13..8cf9ab00b091 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -716,9 +716,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) - case Quote(expr, tpt) => - val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) - keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") + case tree @ Quote(expr) => + val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.exprType) ~ keywordStr("]")).provided(printDebug) + keywordStr("'") ~ exprTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Splice(expr, tpt) => val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 748b053d7eaa..36b2b9caa050 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -103,8 +103,8 @@ class CrossStageSafety extends TreeMapWithStages { val transformedBody = transformQuoteBody(body, quote.span) val stripAnnotsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val tpt1 = TypeTree(healType(quote.tpt.srcPos)(stripAnnotsDeep(quote.tpt.tpe))) - cpy.Quote(quote)(transformedBody, tpt1) + val exprType1 = healType(quote.srcPos)(stripAnnotsDeep(quote.exprType)) + cpy.Quote(quote)(transformedBody).withExprType(exprType1) } override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index a292b4b079be..0a85ef5e9e84 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -26,7 +26,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` */ protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = - cpy.Quote(quote)(body, quote.tpt) + cpy.Quote(quote)(body) /** Transform the quote `quote` which contains the quoted `body`. * @@ -62,7 +62,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { try transformQuotedType(quotedTree, tree) finally inQuoteOrSplice = old - case tree @ Quote(quotedTree, _) => + case tree @ Quote(quotedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { @@ -79,7 +79,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { - case Quote(t, _) => + case Quote(t) => // Optimization: `${ 'x }` --> `x` transform(t) case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index 77d6b2f3ac61..86393cf2d466 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -402,8 +402,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { case tree: Quote => inContext(prepQuote(tree, start)(using outerCtx)) { val expr = transformTree(tree.expr, start)(using quoteContext) - val tpt = transformTree(tree.tpt, start) - goQuote(cpy.Quote(tree)(expr, tpt), start) + goQuote(cpy.Quote(tree)(expr), start) } case tree: Splice => inContext(prepSplice(tree, start)(using outerCtx)) { diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index c60562baaa7f..f23558e8fe9a 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -100,11 +100,11 @@ class PickleQuotes extends MacroTransform { protected def newTransformer(using Context): Transformer = new Transformer { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case Apply(Select(Quote(expr, tpt), nme.apply), List(quotes)) => - val (contents, codeWithHoles) = makeHoles(expr) + case Apply(Select(quote: Quote, nme.apply), List(quotes)) => + val (contents, codeWithHoles) = makeHoles(quote.expr) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles) - val pickled = PickleQuotes(quotes, codeWithHoles2, contents, tpt.tpe, false) + val pickled = PickleQuotes(quotes, codeWithHoles2, contents, quote.exprType, false) transform(pickled) // pickle quotes that are in the contents case Apply(TypeApply(_, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of => tpt match diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index eac9a8d70654..6861084b1dcb 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -485,7 +485,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) => superAcc.withInvalidCurrentClass(super.transform(tree)) - case Quote(expr, _) => + case Quote(expr) => ctx.compilationUnit.needsStaging = true super.transform(tree) case tree => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 4b6d434d80b0..d65823ff9407 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -44,7 +44,7 @@ object Splicer { * See: `Staging` */ def splice(tree: Tree, splicePos: SrcPos, spliceExpansionPos: SrcPos, classLoader: ClassLoader)(using Context): Tree = tree match { - case Quote(quotedTree, _) => quotedTree + case Quote(quotedTree) => quotedTree case _ => val macroOwner = newSymbol(ctx.owner, nme.MACROkw, Macro | Synthetic, defn.AnyType, coord = tree.span) try @@ -136,7 +136,7 @@ object Splicer { * See: `Staging` */ def checkValidMacroBody(tree: Tree)(using Context): Unit = tree match { - case Quote(_, _) => // ok + case Quote(_) => // ok case _ => type Env = Set[Symbol] @@ -155,7 +155,7 @@ object Splicer { case Block(Nil, expr) => checkIfValidArgument(expr) case Typed(expr, _) => checkIfValidArgument(expr) - case Apply(Select(Quote(expr, tpt), nme.apply), _) => + case Apply(Select(Quote(expr), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match case Splice(_, _) => @@ -203,7 +203,7 @@ object Splicer { case Typed(expr, _) => checkIfValidStaticCall(expr) - case Apply(Select(Quote(quoted, tpt), nme.apply), _) => + case Apply(Select(Quote(quoted), nme.apply), _) => // OK, canceled and warning emitted case Call(fn, args) @@ -240,7 +240,7 @@ object Splicer { override protected def interpretTree(tree: Tree)(implicit env: Env): Object = tree match { // Interpret level -1 quoted code `'{...}` (assumed without level 0 splices) - case Apply(Select(Quote(expr, _), nme.apply), _) => + case Apply(Select(Quote(expr), nme.apply), _) => val expr1 = expr match { case expr: Ident if expr.symbol.isAllOf(InlineByNameProxy) => // inline proxy for by-name parameter diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index af77588419b5..1b3013bdcfff 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -86,7 +86,7 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = assert(level == 0) tree match - case Apply(Select(Quote(expr, _), nme.apply),List(quotes)) => + case Apply(Select(Quote(expr), nme.apply),List(quotes)) => QuoteTransformer().transform(tree) case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of => QuoteTransformer().transform(tree) @@ -134,7 +134,7 @@ class Splicing extends MacroTransform: typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) - case Apply(Select(Quote(expr, tpt), nme.apply),List(quotes)) => + case Apply(Select(Quote(expr), nme.apply),List(quotes)) => super.transform(tree)(using quoteContext) case _: Template => for sym <- tree.symbol.owner.info.decls do @@ -237,7 +237,7 @@ class Splicing extends MacroTransform: case Splice(expr, tpt) => val expr1 = transform(expr)(using spliceContext) cpy.Splice(tree)(expr1, tpt) - case Apply(sel @ Select(app @ Quote(expr, tpt), nme.apply), quotesArgs) => + case Apply(sel @ Select(app @ Quote(expr), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => capturedTerm(expr) @@ -246,7 +246,7 @@ class Splicing extends MacroTransform: if level > 1 then transform(expr)(using quoteContext) else transformLevel0QuoteContent(expr)(using quoteContext) } - cpy.Apply(tree)(cpy.Select(sel)(cpy.Quote(app)(newExpr, tpt), nme.apply), quotesArgs) + cpy.Apply(tree)(cpy.Select(sel)(cpy.Quote(app)(newExpr), nme.apply), quotesArgs) case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => val newContent = capturedPartTypes(tpt) @@ -412,7 +412,7 @@ class Splicing extends MacroTransform: Splice(closure, TypeTree(tpe)) private def quoted(expr: Tree)(using Context): Tree = - Quote(expr, TypeTree(expr.tpe.widenTermRefExpr)) + Quote(expr, expr.tpe.widenTermRefExpr) .select(nme.apply) .appliedTo(quotes.nn) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 23c851a33408..2a2c15f95774 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -312,7 +312,7 @@ object SpaceEngine { def isIrrefutableQuotedPattern(unapp: tpd.Tree, implicits: List[tpd.Tree], pt: Type)(using Context): Boolean = { implicits.headOption match // pattern '{ $x: T } - case Some(tpd.Apply(tpd.Select(tpd.Quote(tpd.TypeApply(fn, List(tpt)), _), nme.apply), _)) + case Some(tpd.Apply(tpd.Select(tpd.Quote(tpd.TypeApply(fn, List(tpt))), nme.apply), _)) if unapp.symbol.owner.eq(defn.QuoteMatching_ExprMatchModule) && fn.symbol.eq(defn.QuotedRuntimePatterns_patternHole) => pt <:< defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 749fbe423dd3..01b4a4384e9f 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -36,7 +36,6 @@ trait QuotesAndSplices { */ def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = { record("typedQuote") - assert(tree.tpt.isEmpty) tree.expr match { case untpd.Splice(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) @@ -62,7 +61,7 @@ trait QuotesAndSplices { // TODO typecheck directly (without `exprQuote`) val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.expr) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match - case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt) + case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt.tpe) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) } @@ -77,7 +76,7 @@ trait QuotesAndSplices { assert(tree.tpt.isEmpty) checkSpliceOutsideQuote(tree) tree.expr match { - case untpd.Quote(innerExpr, _) if innerExpr.isTerm => + case untpd.Quote(innerExpr) if innerExpr.isTerm => report.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.srcPos) return typed(innerExpr, pt) case _ => @@ -446,7 +445,7 @@ trait QuotesAndSplices { val quoteClass = if (quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (quoted.isTerm) tpd.Quote(shape, TypeTree(defn.AnyType)).select(nme.apply).appliedTo(qctx) + if (quoted.isTerm) tpd.Quote(shape, defn.AnyType).select(nme.apply).appliedTo(qctx) else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx) val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 9efe3b31a356..c96dbad2d01a 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -96,9 +96,9 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking typedApply(tree, selType) override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val expr1 = typed(tree.expr, tpt1.tpe.widenSkolem)(using quoteContext) - assignType(untpd.cpy.Quote(tree)(expr1, tpt1), tpt1) + assertTyped(tree) + val expr1 = typed(tree.expr, tree.exprType)(using quoteContext) + untpd.cpy.Quote(tree)(expr1).withType(tree.typeOpt) override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index fc51af646b17..c22d1eb72aa6 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -392,12 +392,6 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(using Context): Inlined = tree.withType(avoidingType(expansion, bindings)) - def assignType(tree: untpd.Quote, tpt: Tree)(using Context): Quote = - val lambdaType = // Quotes ?=> Expr[T] - defn.FunctionType(1, isContextual = true) - .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe)) - tree.withType(lambdaType) - def assignType(tree: untpd.Splice, tpt: Tree)(using Context): Splice = tree.withType(tpt.tpe) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index f63ef3feb041..dc72962401cc 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -637,7 +637,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case self: tpd.Quote => // TODO expose Quote AST in Quotes import dotty.tools.dotc.ast.tpd.TreeOps tpd.ref(dotc.core.Symbols.defn.QuotedRuntime_exprQuote) - .appliedToTypeTree(self.tpt) + .appliedToType(self.exprType) .withSpan(self.span) case self: tpd.Splice => // TODO expose Splice AST in Quotes import dotty.tools.dotc.ast.tpd.TreeOps From 3976d062314e12eda6af23b57537064228e4f9b1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 14:20:29 +0200 Subject: [PATCH 445/657] Remove `tpt` from `Splice` --- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/ast/Trees.scala | 26 +++++++------------ compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 8 +++--- .../tools/dotc/core/tasty/TreeUnpickler.scala | 4 +-- .../dotty/tools/dotc/inlines/Inliner.scala | 6 ++--- .../dotc/inlines/PrepareInlineable.scala | 2 +- .../dotty/tools/dotc/parsing/Parsers.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 8 +++--- .../tools/dotc/staging/CrossStageSafety.scala | 11 +++----- .../dotc/staging/TreeMapWithStages.scala | 4 +-- .../tools/dotc/transform/MegaPhase.scala | 3 +-- .../dotty/tools/dotc/transform/Splicer.scala | 2 +- .../dotty/tools/dotc/transform/Splicing.scala | 8 +++--- .../tools/dotc/typer/QuotesAndSplices.scala | 19 ++++++-------- .../src/dotty/tools/dotc/typer/ReTyper.scala | 12 +++++---- .../dotty/tools/dotc/typer/TypeAssigner.scala | 3 --- .../quoted/runtime/impl/QuotesImpl.scala | 2 +- 19 files changed, 57 insertions(+), 71 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 60962f0d1996..c1dd78451bae 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1989,7 +1989,7 @@ object desugar { case Quote(expr) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { - case Splice(expr, _) => collect(expr) + case Splice(expr) => collect(expr) case _ => traverseChildren(tree) } }.traverse(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index e626a06829dd..e5b7f32a674d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -709,20 +709,16 @@ object Trees { /** A tree representing a splice `${ expr }` * - * `Splice`s are created by the `Parser` with an empty `tpt`. In typer - * they can be typed as a `Splice` with a known `tpt` or desugared and - * typed as a quote pattern holes. + * `Splice`s are created by the `Parser`. In typer they can be typed as a + * `Splice` with a known `tpt` or desugared and typed as a quote pattern holes. * * `Splice` are checked and transformed in the `staging` and `splicing` phases. * After `splicing` phase, the only splices that exist are in `inline` * methods. These are dropped when we remove the inline method implementations. * - * The `tpt` will be transformed in `staging` and used in `splicing` to create `Hole`s. - * * @param expr The tree that was spliced - * @param tpt The type of the spliced tree */ - case class Splice[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Splice[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] } @@ -1313,9 +1309,9 @@ object Trees { case tree: Quote if (expr eq tree.expr) => tree case _ => finalize(tree, untpd.Quote(expr)(sourceFile(tree))) } - def Splice(tree: Tree)(expr: Tree, tpt: Tree)(using Context): Splice = tree match { - case tree: Splice if (expr eq tree.expr) && (tpt eq tree.tpt) => tree - case _ => finalize(tree, untpd.Splice(expr, tpt)(sourceFile(tree))) + def Splice(tree: Tree)(expr: Tree)(using Context): Splice = tree match { + case tree: Splice if (expr eq tree.expr) => tree + case _ => finalize(tree, untpd.Splice(expr)(sourceFile(tree))) } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree @@ -1422,8 +1418,6 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) - def Splice(tree: Splice)(expr: Tree = tree.expr, tpt: Tree = tree.tpt)(using Context): Splice = - Splice(tree: Tree)(expr, tpt) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, args, content, tpt) @@ -1558,8 +1552,8 @@ object Trees { if (trees1 eq trees) tree else Thicket(trees1) case tree @ Quote(expr) => cpy.Quote(tree)(transform(expr)) - case tree @ Splice(expr, tpt) => - cpy.Splice(tree)(transform(expr), transform(tpt)) + case tree @ Splice(expr) => + cpy.Splice(tree)(transform(expr)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => @@ -1703,8 +1697,8 @@ object Trees { this(x, ts) case Quote(expr) => this(x, expr) - case Splice(expr, tpt) => - this(this(x, expr), tpt) + case Splice(expr) => + this(x, expr) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 78de609850db..ce9bd3ea0595 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -173,8 +173,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Quote(expr: Tree, tpe: Type)(using Context): Quote = untpd.Quote(expr).withExprType(tpe) - def Splice(expr: Tree, tpt: Tree)(using Context): Splice = - ta.assignType(untpd.Splice(expr, tpt), tpt) + def Splice(expr: Tree, tpe: Type)(using Context): Splice = + untpd.Splice(expr).withType(tpe) def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 4fdd9115f2c9..ec85ffa689b6 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -398,7 +398,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) def Quote(expr: Tree)(implicit src: SourceFile): Quote = new Quote(expr) - def Splice(expr: Tree, tpt: Tree)(implicit src: SourceFile): Splice = new Splice(expr, tpt) + def Splice(expr: Tree)(implicit src: SourceFile): Splice = new Splice(expr) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 6346c2f3dd03..7682e042304a 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -667,17 +667,17 @@ class TreePickler(pickler: TastyPickler) { } case tree @ Quote(expr) => pickleTree( - // scala.quoted.runtime.Expr.quoted[]() + // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) .appliedToType(tree.exprType) .appliedTo(expr) .withSpan(tree.span) ) - case Splice(expr, tpt) => + case Splice(expr) => pickleTree( - // scala.quoted.runtime.Expr.splice[]() + // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) - .appliedToTypeTree(tpt) + .appliedToType(tree.tpe) .appliedTo(expr) .withSpan(tree.span) ) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 02e79854c666..4f81e1477ea7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1273,12 +1273,12 @@ class TreeUnpickler(reader: TastyReader, def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - Splice(args.head, targs.head) + Splice(args.head, targs.head.tpe) def nestedSpliceExpr(fn: Tree, args: List[Tree]): Tree = fn match case Apply(TypeApply(_, targs), _ :: Nil) => // nestedSplice[T](quotes)(expr) - Splice(args.head, targs.head) + Splice(args.head, targs.head.tpe) case _ => // nestedSplice[T](quotes) tpd.Apply(fn, args) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 8f3c4df237a6..9179e92527e6 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -827,14 +827,14 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = super.typedQuote(tree, pt) match - case Quote(Splice(inner, _)) => inner + case Quote(Splice(inner)) => inner case tree1 => ctx.compilationUnit.needsStaging = true tree1 override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = super.typedSplice(tree, pt) match - case tree1 @ Splice(expr, tpt) + case tree1 @ Splice(expr) if StagingLevel.level == 0 && !hasInliningErrors => val expanded = expandMacro(expr, tree1.srcPos) @@ -1078,7 +1078,7 @@ class Inliner(val call: tpd.Tree)(using Context): level += 1 try apply(syms, body) finally level -= 1 - case Splice(body, _) => + case Splice(body) => level -= 1 try apply(syms, body) finally level += 1 diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index 11d4d9881230..c771eff9e840 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -291,7 +291,7 @@ object PrepareInlineable { if (inlined.is(Macro) && !ctx.isAfterTyper) { def checkMacro(tree: Tree): Unit = tree match { - case Splice(code, _) => + case Splice(code) => if (code.symbol.flags.is(Inline)) report.error("Macro cannot be implemented with an `inline` method", code.srcPos) Splicer.checkValidMacroBody(code) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index d98f407edc73..9e169d334bd5 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1764,7 +1764,7 @@ object Parsers { syntaxError(em"$msg\n\nHint: $hint", Span(start, in.lastOffset)) Ident(nme.ERROR.toTypeName) else - Splice(expr, EmptyTree) + Splice(expr) } /** SimpleType ::= SimpleLiteral diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 8cf9ab00b091..7ed8ede8aaf9 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -717,11 +717,11 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) case tree @ Quote(expr) => - val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.exprType) ~ keywordStr("]")).provided(printDebug) + val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.exprType) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) keywordStr("'") ~ exprTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") - case Splice(expr, tpt) => - val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(!tpt.isEmpty && printDebug) - keywordStr("$") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") + case Splice(expr) => + val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) + keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") case Hole(isTermHole, idx, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 36b2b9caa050..32b54ffa7ff8 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -144,13 +144,10 @@ class CrossStageSafety extends TreeMapWithStages { */ protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree = { val body1 = transform(body)(using spliceContext) - val tpt1 = - if level == 0 then - transform(splice.tpt) - else - val tp = healType(splice.srcPos)(splice.tpe.widenTermRefExpr) - TypeTree(tp).withSpan(splice.tpt.span) - cpy.Splice(splice)(body1, tpt1) + val tpe1 = + if level == 0 then splice.tpe + else healType(splice.srcPos)(splice.tpe.widenTermRefExpr) + untpd.cpy.Splice(splice)(body1).withType(tpe1) } protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 0a85ef5e9e84..ddf2017af368 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -66,7 +66,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(quotedTree) match { - case Splice(t, _) => + case Splice(t) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` transform(t).asInstance(tree.tpe) @@ -75,7 +75,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } finally inQuoteOrSplice = old - case tree @ Splice(splicedTree, _) => + case tree @ Splice(splicedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true try dropEmptyBlocks(splicedTree) match { diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index 86393cf2d466..cf34c60330fa 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -407,8 +407,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { case tree: Splice => inContext(prepSplice(tree, start)(using outerCtx)) { val expr = transformTree(tree.expr, start)(using spliceContext) - val tpt = transformTree(tree.tpt, start) - goSplice(cpy.Splice(tree)(expr, tpt), start) + goSplice(cpy.Splice(tree)(expr), start) } case tree: Return => inContext(prepReturn(tree, start)(using outerCtx)) { diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index d65823ff9407..38f1005cd813 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -158,7 +158,7 @@ object Splicer { case Apply(Select(Quote(expr), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case Splice(_, _) => + case Splice(_) => report.error("Quoted argument of macros may not have splices", tree.srcPos) case _ => traverseChildren(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 1b3013bdcfff..fd195e8f068f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -114,7 +114,7 @@ class Splicing extends MacroTransform: case tree: Splice => if level > 1 then val expr1 = super.transform(tree.expr)(using spliceContext) - cpy.Splice(tree)(expr1, tree.tpt) + cpy.Splice(tree)(expr1) else val holeIdx = numHoles numHoles += 1 @@ -234,9 +234,9 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case Splice(expr, tpt) => + case Splice(expr) => val expr1 = transform(expr)(using spliceContext) - cpy.Splice(tree)(expr1, tpt) + cpy.Splice(tree)(expr1) case Apply(sel @ Select(app @ Quote(expr), nme.apply), quotesArgs) => expr match case expr: RefTree if isCaptured(expr.symbol) => @@ -409,7 +409,7 @@ class Splicing extends MacroTransform: body(using ctx.withOwner(meth)).changeOwner(ctx.owner, meth) } }) - Splice(closure, TypeTree(tpe)) + Splice(closure, tpe) private def quoted(expr: Tree)(using Context): Tree = Quote(expr, expr.tpe.widenTermRefExpr) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 01b4a4384e9f..4642bdbbb9ba 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -37,7 +37,7 @@ trait QuotesAndSplices { def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = { record("typedQuote") tree.expr match { - case untpd.Splice(innerExpr, _) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => + case untpd.Splice(innerExpr) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } @@ -73,7 +73,6 @@ trait QuotesAndSplices { /** Translate `${ t: Expr[T] }` into expression `t.splice` while tracking the quotation level in the context */ def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = { record("typedSplice") - assert(tree.tpt.isEmpty) checkSpliceOutsideQuote(tree) tree.expr match { case untpd.Quote(innerExpr) if innerExpr.isTerm => @@ -89,7 +88,7 @@ trait QuotesAndSplices { using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(spliceOwner(ctx))) val baseType = pat.tpe.baseType(defn.QuotedExprClass) val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType - Splice(pat, TypeTree(argType)).withSpan(tree.span) + Splice(pat, argType).withSpan(tree.span) } else { report.error(em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) @@ -111,7 +110,7 @@ trait QuotesAndSplices { untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) typedApply(internalSplice, pt)(using spliceContext).withSpan(tree.span) match case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => - cpy.Splice(tree)(spliced, tpt) + cpy.Splice(tree)(spliced) case tree => tree } } @@ -229,12 +228,10 @@ trait QuotesAndSplices { val freshTypeBindingsBuff = new mutable.ListBuffer[Tree] val typePatBuf = new mutable.ListBuffer[Tree] override def transform(tree: Tree)(using Context) = tree match { - case Typed(Splice(pat, _), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => - val tpt1 = transform(tpt) // Transform type bindings - val exprTpt = AppliedTypeTree(TypeTree(defn.QuotedExprClass.typeRef), tpt1 :: Nil) - val newSplice = cpy.Splice(tree)(pat, tpt1) - transform(newSplice) - case Apply(TypeApply(fn, targs), Splice(pat, _) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => + case Typed(splice: Splice, tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => + transform(tpt) // Collect type bindings + transform(splice) + case Apply(TypeApply(fn, targs), Splice(pat) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => args match // TODO support these patterns. Possibly using scala.quoted.util.Var case SeqLiteral(args, _) => for arg <- args; if arg.symbol.is(Mutable) do @@ -246,7 +243,7 @@ trait QuotesAndSplices { val pat1 = if (patType eq patType1) pat else pat.withType(patType1) patBuf += pat1 } - case Splice(pat, _) => + case Splice(pat) => try ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) finally { val patType = pat.tpe.widen diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index c96dbad2d01a..b43722d54cc7 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -101,12 +101,14 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking untpd.cpy.Quote(tree)(expr1).withType(tree.typeOpt) override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = - val tpt1 = checkSimpleKinded(typedType(tree.tpt, mapPatternBounds = true)) - val splicedType = // Quotes ?=> Expr[T] + assertTyped(tree) + val exprType = // Expr[T] + defn.QuotedExprClass.typeRef.appliedTo(tree.typeOpt) + val quoteType = // Quotes ?=> Expr[T] defn.FunctionType(1, isContextual = true) - .appliedTo(defn.QuotesClass.typeRef, defn.QuotedExprClass.typeRef.appliedTo(tpt1.tpe.widenSkolem)) - val expr1 = typed(tree.expr, splicedType)(using spliceContext) - assignType(untpd.cpy.Splice(tree)(expr1, tpt1), tpt1) + .appliedTo(defn.QuotesClass.typeRef, exprType) + val expr1 = typed(tree.expr, quoteType)(using spliceContext) + untpd.cpy.Splice(tree)(expr1).withType(tree.typeOpt) override def localDummy(cls: ClassSymbol, impl: untpd.Template)(using Context): Symbol = impl.symbol diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index c22d1eb72aa6..6ac45cbcf04d 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -392,9 +392,6 @@ trait TypeAssigner { def assignType(tree: untpd.Inlined, bindings: List[Tree], expansion: Tree)(using Context): Inlined = tree.withType(avoidingType(expansion, bindings)) - def assignType(tree: untpd.Splice, tpt: Tree)(using Context): Splice = - tree.withType(tpt.tpe) - def assignType(tree: untpd.If, thenp: Tree, elsep: Tree)(using Context): If = tree.withType(thenp.tpe | elsep.tpe) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index dc72962401cc..c0b778756d41 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -642,7 +642,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case self: tpd.Splice => // TODO expose Splice AST in Quotes import dotty.tools.dotc.ast.tpd.TreeOps tpd.ref(dotc.core.Symbols.defn.QuotedRuntime_exprSplice) - .appliedToTypeTree(self.tpt) + .appliedToType(self.tpe) .withSpan(self.span) def args: List[Term] = self match From d4b2f0933f7b38c30ce1fb16ddbfaa74834282b0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 16:19:20 +0200 Subject: [PATCH 446/657] Rename `Quote.{expr=>body}` Quotes can contain terms `'{..}` or types `'[..]`. --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 4 ++-- compiler/src/dotty/tools/dotc/ast/Trees.scala | 16 ++++++++-------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 ++-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 6 +++--- .../src/dotty/tools/dotc/parsing/Parsers.scala | 4 ++-- .../tools/dotc/printing/RefinedPrinter.scala | 4 ++-- .../dotty/tools/dotc/transform/MegaPhase.scala | 4 ++-- .../tools/dotc/transform/PickleQuotes.scala | 2 +- .../dotty/tools/dotc/transform/PostTyper.scala | 2 +- .../src/dotty/tools/dotc/transform/Splicer.scala | 14 +++++++------- .../dotty/tools/dotc/transform/Splicing.scala | 4 ++-- .../tools/dotc/typer/QuotesAndSplices.scala | 10 +++++----- .../src/dotty/tools/dotc/typer/ReTyper.scala | 4 ++-- .../scala/quoted/runtime/impl/QuotesImpl.scala | 2 +- 15 files changed, 41 insertions(+), 41 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index c1dd78451bae..d6f9e0d24c91 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1986,13 +1986,13 @@ object desugar { trees foreach collect case Block(Nil, expr) => collect(expr) - case Quote(expr) => + case Quote(body) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { case Splice(expr) => collect(expr) case _ => traverseChildren(tree) } - }.traverse(expr) + }.traverse(body) case CapturingTypeTree(refs, parent) => collect(parent) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index e5b7f32a674d..0bcc28d360e9 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -687,7 +687,7 @@ object Trees { * * @param expr The tree that was quoted */ - case class Quote[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Quote[+T <: Untyped] private[ast] (body: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Quote[T] @@ -1305,9 +1305,9 @@ object Trees { case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree))) } - def Quote(tree: Tree)(expr: Tree)(using Context): Quote = tree match { - case tree: Quote if (expr eq tree.expr) => tree - case _ => finalize(tree, untpd.Quote(expr)(sourceFile(tree))) + def Quote(tree: Tree)(body: Tree)(using Context): Quote = tree match { + case tree: Quote if (body eq tree.body) => tree + case _ => finalize(tree, untpd.Quote(body)(sourceFile(tree))) } def Splice(tree: Tree)(expr: Tree)(using Context): Splice = tree match { case tree: Splice if (expr eq tree.expr) => tree @@ -1550,8 +1550,8 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case tree @ Quote(expr) => - cpy.Quote(tree)(transform(expr)) + case tree @ Quote(body) => + cpy.Quote(tree)(transform(body)) case tree @ Splice(expr) => cpy.Splice(tree)(transform(expr)) case tree @ Hole(_, _, args, content, tpt) => @@ -1695,8 +1695,8 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) - case Quote(expr) => - this(x, expr) + case Quote(body) => + this(x, body) case Splice(expr) => this(x, expr) case Hole(_, _, args, content, tpt) => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index ce9bd3ea0595..a44472fed658 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,8 +170,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - def Quote(expr: Tree, tpe: Type)(using Context): Quote = - untpd.Quote(expr).withExprType(tpe) + def Quote(body: Tree, tpe: Type)(using Context): Quote = + untpd.Quote(body).withExprType(tpe) def Splice(expr: Tree, tpe: Type)(using Context): Splice = untpd.Splice(expr).withType(tpe) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index ec85ffa689b6..30f58fba44ec 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -397,7 +397,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): SeqLiteral = new SeqLiteral(elems, elemtpt) def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) - def Quote(expr: Tree)(implicit src: SourceFile): Quote = new Quote(expr) + def Quote(body: Tree)(implicit src: SourceFile): Quote = new Quote(body) def Splice(expr: Tree)(implicit src: SourceFile): Splice = new Splice(expr) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 7682e042304a..97bfe4d28854 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,12 +665,12 @@ class TreePickler(pickler: TastyPickler) { pickleTree(hi) pickleTree(alias) } - case tree @ Quote(expr) => + case tree @ Quote(body) => pickleTree( - // scala.quoted.runtime.Expr.quoted[]() + // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) .appliedToType(tree.exprType) - .appliedTo(expr) + .appliedTo(body) .withSpan(tree.span) ) case Splice(expr) => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9e169d334bd5..324c3037d8ce 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2494,10 +2494,10 @@ object Parsers { case QUOTE => atSpan(in.skipToken()) { withinStaged(StageKind.Quoted | (if (location.inPattern) StageKind.QuotedPattern else 0)) { - val expr = + val body = if (in.token == LBRACKET) inBrackets(typ()) else stagedBlock() - Quote(expr) + Quote(body) } } case NEW => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 7ed8ede8aaf9..d93cf1d87e02 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -716,9 +716,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) - case tree @ Quote(expr) => + case tree @ Quote(body) => val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.exprType) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) - keywordStr("'") ~ exprTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") + keywordStr("'") ~ exprTypeText ~ keywordStr("{") ~ toTextGlobal(body) ~ keywordStr("}") case Splice(expr) => val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index cf34c60330fa..7ad109b67751 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -401,8 +401,8 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { } case tree: Quote => inContext(prepQuote(tree, start)(using outerCtx)) { - val expr = transformTree(tree.expr, start)(using quoteContext) - goQuote(cpy.Quote(tree)(expr), start) + val body = transformTree(tree.body, start)(using quoteContext) + goQuote(cpy.Quote(tree)(body), start) } case tree: Splice => inContext(prepSplice(tree, start)(using outerCtx)) { diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index f23558e8fe9a..a0ea03f238df 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -101,7 +101,7 @@ class PickleQuotes extends MacroTransform { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match case Apply(Select(quote: Quote, nme.apply), List(quotes)) => - val (contents, codeWithHoles) = makeHoles(quote.expr) + val (contents, codeWithHoles) = makeHoles(quote.body) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles) val pickled = PickleQuotes(quotes, codeWithHoles2, contents, quote.exprType, false) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 6861084b1dcb..d74392f201ba 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -485,7 +485,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase ) case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) => superAcc.withInvalidCurrentClass(super.transform(tree)) - case Quote(expr) => + case _: Quote => ctx.compilationUnit.needsStaging = true super.transform(tree) case tree => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 38f1005cd813..8f45fd94d205 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -155,7 +155,7 @@ object Splicer { case Block(Nil, expr) => checkIfValidArgument(expr) case Typed(expr, _) => checkIfValidArgument(expr) - case Apply(Select(Quote(expr), nme.apply), _) => + case Apply(Select(Quote(body), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match case Splice(_) => @@ -163,7 +163,7 @@ object Splicer { case _ => traverseChildren(tree) } - noSpliceChecker.traverse(expr) + noSpliceChecker.traverse(body) case Apply(TypeApply(fn, List(quoted)), _)if fn.symbol == defn.QuotedTypeModule_of => // OK @@ -240,15 +240,15 @@ object Splicer { override protected def interpretTree(tree: Tree)(implicit env: Env): Object = tree match { // Interpret level -1 quoted code `'{...}` (assumed without level 0 splices) - case Apply(Select(Quote(expr), nme.apply), _) => - val expr1 = expr match { + case Apply(Select(Quote(body), nme.apply), _) => + val body1 = body match { case expr: Ident if expr.symbol.isAllOf(InlineByNameProxy) => // inline proxy for by-name parameter expr.symbol.defTree.asInstanceOf[DefDef].rhs - case Inlined(EmptyTree, _, expr1) => expr1 - case _ => expr + case Inlined(EmptyTree, _, body1) => body1 + case _ => body } - new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(expr1, ctx.owner)).withSpan(expr1.span), SpliceScope.getCurrent) + new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(body1, ctx.owner)).withSpan(body1.span), SpliceScope.getCurrent) // Interpret level -1 `Type.of[T]` case Apply(TypeApply(fn, quoted :: Nil), _) if fn.symbol == defn.QuotedTypeModule_of => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index fd195e8f068f..a0f105fa9745 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -86,7 +86,7 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = assert(level == 0) tree match - case Apply(Select(Quote(expr), nme.apply),List(quotes)) => + case Apply(Select(_: Quote, nme.apply), _) => QuoteTransformer().transform(tree) case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of => QuoteTransformer().transform(tree) @@ -134,7 +134,7 @@ class Splicing extends MacroTransform: typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) - case Apply(Select(Quote(expr), nme.apply),List(quotes)) => + case Apply(Select(_: Quote, nme.apply),List(quotes)) => super.transform(tree)(using quoteContext) case _: Template => for sym <- tree.symbol.owner.info.decls do diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 4642bdbbb9ba..ebff31414d68 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -36,8 +36,8 @@ trait QuotesAndSplices { */ def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = { record("typedQuote") - tree.expr match { - case untpd.Splice(innerExpr) if tree.isTerm && !ctx.mode.is(Mode.Pattern) => + tree.body match { + case _: untpd.Splice if tree.isTerm && !ctx.mode.is(Mode.Pattern) => report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos) case _ => } @@ -50,7 +50,7 @@ trait QuotesAndSplices { if ctx.mode.is(Mode.Pattern) then typedQuotePattern(tree, pt, quotes).withSpan(tree.span) - else if tree.expr.isType then + else if tree.body.isType then val msg = em"""Quoted types `'[..]` can only be used in patterns. | |Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead. @@ -59,7 +59,7 @@ trait QuotesAndSplices { EmptyTree else // TODO typecheck directly (without `exprQuote`) - val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.expr) + val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.body) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt.tpe) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) @@ -383,7 +383,7 @@ trait QuotesAndSplices { * ``` */ private def typedQuotePattern(tree: untpd.Quote, pt: Type, qctx: Tree)(using Context): Tree = { - val quoted = tree.expr + val quoted = tree.body if quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then report.error("Quote pattern can only match scrutinees of type scala.quoted.Expr", tree.srcPos) else if quoted.isType && !pt.derivesFrom(defn.QuotedTypeClass) then diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index b43722d54cc7..d8e9b115ea49 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -97,8 +97,8 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = assertTyped(tree) - val expr1 = typed(tree.expr, tree.exprType)(using quoteContext) - untpd.cpy.Quote(tree)(expr1).withType(tree.typeOpt) + val body1 = typed(tree.body, tree.exprType)(using quoteContext) + untpd.cpy.Quote(tree)(body1).withType(tree.typeOpt) override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = assertTyped(tree) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index c0b778756d41..1df2abcf0453 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -647,7 +647,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def args: List[Term] = self match case self: tpd.Apply => self.args - case self: tpd.Quote => List(self.expr) // TODO expose Quote AST in Quotes + case self: tpd.Quote => List(self.body) // TODO expose Quote AST in Quotes case self: tpd.Splice => List(self.expr) // TODO expose Splice AST in Quotes end extension end ApplyMethods From 35408ec652effb8ecd03cb6b76128cd561ba2245 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 16:29:24 +0200 Subject: [PATCH 447/657] Fix staging level tracking in Transformer --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 5 +++-- .../dotty/tools/dotc/transform/Splicing.scala | 20 +++++++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 0bcc28d360e9..c9840cd36cdf 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -17,6 +17,7 @@ import annotation.unchecked.uncheckedVariance import annotation.constructorOnly import compiletime.uninitialized import Decorators._ +import staging.StagingLevel.* object Trees { @@ -1551,9 +1552,9 @@ object Trees { val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) case tree @ Quote(body) => - cpy.Quote(tree)(transform(body)) + cpy.Quote(tree)(transform(body)(using quoteContext)) case tree @ Splice(expr) => - cpy.Splice(tree)(transform(expr)) + cpy.Splice(tree)(transform(expr)(using spliceContext)) case tree @ Hole(_, _, args, content, tpt) => cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index a0f105fa9745..0b50343905f8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -111,17 +111,13 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case tree: Splice => - if level > 1 then - val expr1 = super.transform(tree.expr)(using spliceContext) - cpy.Splice(tree)(expr1) - else - val holeIdx = numHoles - numHoles += 1 - val splicer = SpliceTransformer(ctx.owner, quotedDefs.contains) - val newSplicedCode1 = splicer.transformSplice(tree.expr, tree.tpe, holeIdx)(using spliceContext) - val newSplicedCode2 = Level0QuoteTransformer.transform(newSplicedCode1)(using spliceContext) - newSplicedCode2 + case tree: Splice if level == 1 => + val holeIdx = numHoles + numHoles += 1 + val splicer = SpliceTransformer(ctx.owner, quotedDefs.contains) + val newSplicedCode1 = splicer.transformSplice(tree.expr, tree.tpe, holeIdx)(using spliceContext) + val newSplicedCode2 = Level0QuoteTransformer.transform(newSplicedCode1)(using spliceContext) + newSplicedCode2 case tree: TypeDef if tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => val tp @ TypeRef(qual: TermRef, _) = tree.rhs.tpe.hiBound: @unchecked quotedDefs += tree.symbol @@ -134,8 +130,6 @@ class Splicing extends MacroTransform: typeHoles.put(qual, hole) hole cpy.TypeDef(tree)(rhs = hole) - case Apply(Select(_: Quote, nme.apply),List(quotes)) => - super.transform(tree)(using quoteContext) case _: Template => for sym <- tree.symbol.owner.info.decls do quotedDefs += sym From e73eab74b8d681bbf9ce61d9a8df50addfe00e62 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 28 Apr 2023 16:40:01 +0200 Subject: [PATCH 448/657] Cleanup unnecessary staging level updates --- .../dotc/inlines/PrepareInlineable.scala | 2 -- .../dotty/tools/dotc/transform/Inlining.scala | 8 -------- .../dotty/tools/dotc/transform/Splicing.scala | 20 +++++++------------ 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index c771eff9e840..ed8683bf210e 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -91,9 +91,7 @@ object PrepareInlineable { } private def stagingContext(tree: Tree)(using Context): Context = tree match - case tree: Quote => StagingLevel.quoteContext case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext - case tree: Splice => StagingLevel.spliceContext case _ => ctx } diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index 60714d273bf0..f5b72759b57e 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -45,12 +45,8 @@ class Inlining extends MacroTransform { new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case _: Quote => - traverseChildren(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => traverseChildren(tree)(using StagingLevel.quoteContext) - case _: Splice => - traverseChildren(tree)(using StagingLevel.spliceContext) case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) case _ => @@ -99,12 +95,8 @@ class Inlining extends MacroTransform { val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 else Inlines.inlineCall(tree1) - case _: Quote => - super.transform(tree)(using StagingLevel.quoteContext) case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => super.transform(tree)(using StagingLevel.quoteContext) - case _: Splice => - super.transform(tree)(using StagingLevel.spliceContext) case _: PackageDef => super.transform(tree) match case tree1: PackageDef => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 0b50343905f8..e8102a5db224 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -228,19 +228,10 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case Splice(expr) => - val expr1 = transform(expr)(using spliceContext) - cpy.Splice(tree)(expr1) - case Apply(sel @ Select(app @ Quote(expr), nme.apply), quotesArgs) => - expr match - case expr: RefTree if isCaptured(expr.symbol) => - capturedTerm(expr) - case _ => - val newExpr = withCurrentQuote(quotesArgs.head) { - if level > 1 then transform(expr)(using quoteContext) - else transformLevel0QuoteContent(expr)(using quoteContext) - } - cpy.Apply(tree)(cpy.Select(sel)(cpy.Quote(app)(newExpr), nme.apply), quotesArgs) + case Apply(sel @ Select(app @ Quote(body), nme.apply), quotesArgs) => + body match + case body: RefTree if isCaptured(body.symbol) => capturedTerm(body) + case _ => withCurrentQuote(quotesArgs.head) { super.transform(tree) } case Apply(TypeApply(typeof, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => val newContent = capturedPartTypes(tpt) @@ -253,6 +244,9 @@ class Splicing extends MacroTransform: ref(capturedType(newContent))(using ctx.withSource(tree.source)).withSpan(tree.span) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) + case Quote(expr) if level == 0 => + val newExpr = transformLevel0QuoteContent(expr)(using quoteContext) + cpy.Quote(tree)(newExpr) case _ => super.transform(tree) From 48a610c326a97e3ee4efd56176d5e02322cd694c Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Fri, 28 Apr 2023 14:40:34 +0200 Subject: [PATCH 449/657] Fix #17187: allow patches with same span --- .../dotty/tools/dotc/parsing/Parsers.scala | 9 ++-- .../dotty/tools/dotc/rewrites/Rewrites.scala | 5 +-- .../dotty/tools/dotc/CompilationTests.scala | 1 + tests/rewrites/i12340.check | 9 ++++ tests/rewrites/i12340.scala | 7 +++ tests/rewrites/i17187.check | 44 +++++++++++++++++++ tests/rewrites/i17187.scala | 33 ++++++++++++++ 7 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 tests/rewrites/i17187.check create mode 100644 tests/rewrites/i17187.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 15a639743c15..f773e10bc80d 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -715,7 +715,11 @@ object Parsers { val t = enclosed(INDENT, body) if needsBraces(t) then patch(source, Span(startOpening, endOpening), " {") - patch(source, Span(closingOffset(source.nextLine(in.lastOffset))), indentWidth.toPrefix ++ "}\n") + val next = in.next + def closedByEndMarker = + next.token == END && (next.offset - next.lineOffset) == indentWidth.toPrefix.size + if closedByEndMarker then patch(source, Span(next.offset), "} // ") + else patch(source, Span(closingOffset(source.nextLine(in.lastOffset))), indentWidth.toPrefix ++ "}\n") t end indentedToBraces @@ -1422,9 +1426,6 @@ object Parsers { val start = in.skipToken() if stats.isEmpty || !matchesAndSetEnd(stats.last) then syntaxError(em"misaligned end marker", Span(start, in.lastCharOffset)) - else if overlapsPatch(source, Span(start, start)) then - patch(source, Span(start, start), "") - patch(source, Span(start, in.lastCharOffset), s"} // end $endName") in.token = IDENTIFIER // Leaving it as the original token can confuse newline insertion in.nextToken() end checkEndMarker diff --git a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala index 96e88e5c68ae..f2dfac88d464 100644 --- a/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala +++ b/compiler/src/dotty/tools/dotc/rewrites/Rewrites.scala @@ -23,10 +23,7 @@ object Rewrites { private[Rewrites] val pbuf = new mutable.ListBuffer[Patch]() def addPatch(span: Span, replacement: String): Unit = - pbuf.indexWhere(p => p.span.start == span.start && p.span.end == span.end) match { - case i if i >= 0 => pbuf.update(i, Patch(span, replacement)) - case _ => pbuf += Patch(span, replacement) - } + pbuf += Patch(span, replacement) def apply(cs: Array[Char]): Array[Char] = { val delta = pbuf.map(_.delta).sum diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index ed531aa404c2..d75afc330f5f 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -81,6 +81,7 @@ class CompilationTests { compileFile("tests/rewrites/i9632.scala", defaultOptions.and("-indent", "-rewrite")), compileFile("tests/rewrites/i11895.scala", defaultOptions.and("-indent", "-rewrite")), compileFile("tests/rewrites/i12340.scala", unindentOptions.and("-rewrite")), + compileFile("tests/rewrites/i17187.scala", unindentOptions.and("-rewrite")), ).checkRewrites() } diff --git a/tests/rewrites/i12340.check b/tests/rewrites/i12340.check index c6cb9af8bb57..3ee2867f3e62 100644 --- a/tests/rewrites/i12340.check +++ b/tests/rewrites/i12340.check @@ -3,6 +3,15 @@ class C { def f = 42 } // end C +class A { + class B { + class C { + def foo = 42 + } + + } // end B +} + def f(i: Int) = { if i < 42 then println(i) diff --git a/tests/rewrites/i12340.scala b/tests/rewrites/i12340.scala index bf907ef9f276..10fcdd256f5b 100644 --- a/tests/rewrites/i12340.scala +++ b/tests/rewrites/i12340.scala @@ -3,6 +3,13 @@ class C: def f = 42 end C +class A: + class B: + class C: + def foo = 42 + + end B + def f(i: Int) = if i < 42 then println(i) diff --git a/tests/rewrites/i17187.check b/tests/rewrites/i17187.check new file mode 100644 index 000000000000..1e6a07c79738 --- /dev/null +++ b/tests/rewrites/i17187.check @@ -0,0 +1,44 @@ + +object A { + object B { + def a = 2 + } +} + +def m1 = { + def b = { + def c = 2 + } +} + +def m2 = + if true then { + val x = 3 + if (false) + x + else { + val y = 4 + y + } + } + +def m3 = + try { + val n2 = 21 + val n1 = 4 + n2 / n1 + } + catch { + case _ => 4 + } + +def m4 = { + val n2 = 21 + try { + val n1 = 4 + n2 / n1 + } + catch { + case _ => 4 + } +} diff --git a/tests/rewrites/i17187.scala b/tests/rewrites/i17187.scala new file mode 100644 index 000000000000..e6a55e00b39a --- /dev/null +++ b/tests/rewrites/i17187.scala @@ -0,0 +1,33 @@ + +object A: + object B: + def a = 2 + +def m1 = + def b = + def c = 2 + +def m2 = + if true then + val x = 3 + if (false) + x + else + val y = 4 + y + +def m3 = + try + val n2 = 21 + val n1 = 4 + n2 / n1 + catch + case _ => 4 + +def m4 = + val n2 = 21 + try + val n1 = 4 + n2 / n1 + catch + case _ => 4 From d6696d87ec0a9e9e3eb5a6e4b01b59a18e54f4ab Mon Sep 17 00:00:00 2001 From: Carl Date: Sat, 29 Apr 2023 13:31:14 +0200 Subject: [PATCH 450/657] Refactor unsetVarsFromUsedSym to .isUnsetVarDef --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 4eba6b525b01..bd521c8679d0 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -520,7 +520,7 @@ object CheckUnused: .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.LocalDefs)).toList - val unsetLocalDefs = unsetVarsFromUsedSym(usedLocalDefs).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.UnsetLocals)).toList + val unsetLocalDefs = usedLocalDefs.filter(isUnsetVarDef).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.UnsetLocals)).toList val sortedExplicitParams = if ctx.settings.WunusedHas.explicits then @@ -546,7 +546,7 @@ object CheckUnused: else (Nil, Nil) val sortedPrivateDefs = unusedPrivates.filterNot(d => containsSyntheticSuffix(d.symbol)).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PrivateMembers)).toList - val unsetPrivateDefs = unsetVarsFromUsedSym(usedPrivates).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.UnsetPrivates)).toList + val unsetPrivateDefs = usedPrivates.filter(isUnsetVarDef).map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.UnsetPrivates)).toList val sortedPatVars = if ctx.settings.WunusedHas.patvars then patVarsInScope @@ -745,10 +745,13 @@ object CheckUnused: !isSyntheticMainParam(sym) && !sym.shouldNotReportParamOwner - private def shouldReportPrivateDef(using Context): Boolean = currScopeType.top == ScopeType.Template && !memDef.symbol.isConstructor && memDef.symbol.is(Private, butNot = SelfName | Synthetic | CaseAccessor) + private def isUnsetVarDef(using Context): Boolean = + val sym = memDef.symbol + sym.is(Mutable) && !setVars(sym) + extension (imp: tpd.Import) /** Enum generate an import for its cases (but outside them), which should be ignored */ def isGeneratedByEnum(using Context): Boolean = @@ -758,9 +761,6 @@ object CheckUnused: private def isWildcard: Boolean = thisName == StdNames.nme.WILDCARD || thisName.is(WildcardParamName) - def unsetVarsFromUsedSym(usedDefs: Iterable[tpd.MemberDef])(using Context): Iterable[tpd.MemberDef] = - usedDefs.filter(d => d.symbol.is(Mutable) && !setVars(d.symbol)) - end UnusedData private object UnusedData: From 9d3f270b674204535c45ec6b44fe9a7ec4596d32 Mon Sep 17 00:00:00 2001 From: Adrien Piquerez Date: Mon, 1 May 2023 09:43:44 +0200 Subject: [PATCH 451/657] Revert exact match in overlaps As suggested by @som-snytt in https://github.com/lampepfl/dotty/pull/17366#pullrequestreview-1406509043 --- compiler/src/dotty/tools/dotc/util/Spans.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/util/Spans.scala b/compiler/src/dotty/tools/dotc/util/Spans.scala index ba537e9aec01..e1487408f36b 100644 --- a/compiler/src/dotty/tools/dotc/util/Spans.scala +++ b/compiler/src/dotty/tools/dotc/util/Spans.scala @@ -86,7 +86,6 @@ object Spans { || containsInner(this, that.end) || containsInner(that, this.start) || containsInner(that, this.end) - || this.start == that.start && this.end == that.end // exact match in one point ) } From e06b0f95809f22c6dfb5c1e6283584d88bfa102d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 10:19:46 +0200 Subject: [PATCH 452/657] Use `Quote to encode `Type.of` in the `staging phase` --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 28 +++++++++++------ compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +-- .../tools/dotc/core/tasty/TreePickler.scala | 10 ++++-- .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 6 ++-- .../tools/dotc/staging/CrossStageSafety.scala | 11 ++++--- .../dotc/staging/TreeMapWithStages.scala | 4 --- .../tools/dotc/transform/PickleQuotes.scala | 24 ++++++-------- .../tools/dotc/transform/ReifiedReflect.scala | 4 +-- .../dotty/tools/dotc/transform/Splicing.scala | 31 +++++++------------ .../dotty/tools/dotc/transform/Staging.scala | 7 +++++ .../tools/dotc/typer/QuotesAndSplices.scala | 10 +++--- .../src/dotty/tools/dotc/typer/ReTyper.scala | 2 +- .../quoted/runtime/impl/QuotesImpl.scala | 2 +- 14 files changed, 74 insertions(+), 71 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index c9840cd36cdf..19c189047c6d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -678,7 +678,7 @@ object Trees { override def isType = expansion.isType } - /** A tree representing a quote `'{ expr }` + /** A tree representing a quote `'{ body }` or `'[ body ]`. * `Quote`s are created by the `Parser`. In typer they can be typed as a * `Quote` with a known `tpt` or desugared and typed as a quote pattern. * @@ -686,23 +686,31 @@ object Trees { * phases. After `pickleQuotes` phase, the only quotes that exist are in `inline` * methods. These are dropped when we remove the inline method implementations. * - * @param expr The tree that was quoted + * Type quotes `'[body]` from the parser are desugared into quote patterns (using a `Type.of[T]]`) + * when type checking. TASTy files will not contain type quotes. Type quotes are used again + * in the `staging` phase to represent the reification of `Type.of[T]]`. + * + * @param body The tree that was quoted */ case class Quote[+T <: Untyped] private[ast] (body: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Quote[T] + /** Is this a type quote `'[tpe]' */ + def isTypeQuote = body.isType + /** Type of the quoted expression as seen from outside the quote */ - def exprType(using Context): Type = - val quoteType = typeOpt // Quotes ?=> Expr[T] - val exprType = quoteType.argInfos.last // Expr[T] + def bodyType(using Context): Type = + val quoteType = typeOpt // `Quotes ?=> Expr[T]` or `Quotes ?=> Type[T]` + val exprType = quoteType.argInfos.last // `Expr[T]` or `Type[T]` exprType.argInfos.head // T - /** Set the type of the quoted expression as seen from outside the quote */ - def withExprType(tpe: Type)(using Context): Quote[Type] = - val exprType = // Expr[T] - defn.QuotedExprClass.typeRef.appliedTo(tpe) - val quoteType = // Quotes ?=> Expr[T] + /** Set the type of the body of the quote */ + def withBodyType(tpe: Type)(using Context): Quote[Type] = + val exprType = // `Expr[T]` or `Type[T]` + if body.isTerm then defn.QuotedExprClass.typeRef.appliedTo(tpe) + else defn.QuotedTypeClass.typeRef.appliedTo(tpe) + val quoteType = // `Quotes ?=> Expr[T]` or `Quotes ?=> Type[T]` defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, exprType) withType(quoteType) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index a44472fed658..3a957c8f4612 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,8 +170,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - def Quote(body: Tree, tpe: Type)(using Context): Quote = - untpd.Quote(body).withExprType(tpe) + def Quote(body: Tree)(using Context): Quote = + untpd.Quote(body).withBodyType(body.tpe) def Splice(expr: Tree, tpe: Type)(using Context): Splice = untpd.Splice(expr).withType(tpe) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 97bfe4d28854..87620db36482 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -666,15 +666,19 @@ class TreePickler(pickler: TastyPickler) { pickleTree(alias) } case tree @ Quote(body) => + // Add QUOTE tag to TASTy + assert(body.isTerm, + """Quote with type should not be pickled. + |Quote with type should only exists after staging phase at level staging level 0.""".stripMargin) pickleTree( - // scala.quoted.runtime.Expr.quoted[]() + // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) - .appliedToType(tree.exprType) + .appliedToType(tree.bodyType) .appliedTo(body) .withSpan(tree.span) ) case Splice(expr) => - pickleTree( + pickleTree( // Add SPLICE tag to TASTy // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) .appliedToType(tree.tpe) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 4f81e1477ea7..10395a488640 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1269,7 +1269,7 @@ class TreeUnpickler(reader: TastyReader, def quotedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - Quote(args.head, targs.head.tpe) + untpd.Quote(args.head).withBodyType(targs.head.tpe) def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index d93cf1d87e02..93de33778750 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -717,8 +717,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) case tree @ Quote(body) => - val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.exprType) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) - keywordStr("'") ~ exprTypeText ~ keywordStr("{") ~ toTextGlobal(body) ~ keywordStr("}") + val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.bodyType) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) + val open = if (body.isTerm) keywordStr("{") else keywordStr("[") + val close = if (body.isTerm) keywordStr("}") else keywordStr("]") + keywordStr("'") ~ exprTypeText ~ open ~ toTextGlobal(body) ~ close case Splice(expr) => val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 32b54ffa7ff8..cb5f3520429a 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -101,10 +101,10 @@ class CrossStageSafety extends TreeMapWithStages { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) val transformedBody = transformQuoteBody(body, quote.span) - val stripAnnotsDeep: TypeMap = new TypeMap: + val stripAnnotationsDeep: TypeMap = new TypeMap: def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val exprType1 = healType(quote.srcPos)(stripAnnotsDeep(quote.exprType)) - cpy.Quote(quote)(transformedBody).withExprType(exprType1) + val bodyType1 = healType(quote.srcPos)(stripAnnotationsDeep(quote.bodyType)) + cpy.Quote(quote)(transformedBody).withBodyType(bodyType1) } override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { @@ -120,10 +120,11 @@ class CrossStageSafety extends TreeMapWithStages { // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` ref(termRef).withSpan(quote.span) case transformedBody => - val quotes = quote.args.mapConserve(transform) + val quotes = transform(quote.args.head) // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` val TypeApply(fun, _) = quote.fun: @unchecked - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes) + if level != 0 then cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes :: Nil) + else tpd.Quote(transformedBody).select(nme.apply).appliedTo(quotes).withSpan(quote.span) } private def transformQuoteBody(body: Tree, span: Span)(using Context): Tree = { diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index ddf2017af368..e262530ba11e 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -52,10 +52,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } tree match { - case Apply(Select(QuotedTypeOf(SplicedType(t)), _), _) => - // Optimization: `quoted.Type.of[x.Underlying]` --> `x` - transform(t) - case tree @ QuotedTypeOf(quotedTree) => val old = inQuoteOrSplice inQuoteOrSplice = true diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index a0ea03f238df..b1a707a70cb1 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -103,17 +103,11 @@ class PickleQuotes extends MacroTransform { case Apply(Select(quote: Quote, nme.apply), List(quotes)) => val (contents, codeWithHoles) = makeHoles(quote.body) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) - val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles) - val pickled = PickleQuotes(quotes, codeWithHoles2, contents, quote.exprType, false) + val bodyWithHoles2 = + if quote.body.isType then codeWithHoles + else Inlined(sourceRef, Nil, codeWithHoles) + val pickled = PickleQuotes.pickle(quotes, bodyWithHoles2, contents, quote.bodyType) transform(pickled) // pickle quotes that are in the contents - case Apply(TypeApply(_, List(tpt)), List(quotes)) if tree.symbol == defn.QuotedTypeModule_of => - tpt match - case Select(t, _) if tpt.symbol == defn.QuotedType_splice => - // `Type.of[t.Underlying](quotes)` --> `t` - ref(t.symbol)(using ctx.withSource(tpt.source)).withSpan(tpt.span) - case _ => - val (contents, tptWithHoles) = makeHoles(tpt) - PickleQuotes(quotes, tptWithHoles, contents, tpt.tpe, true) case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod => // Shrink size of the tree. The methods have already been inlined. // TODO move to FirstTransform to trigger even without quotes @@ -208,7 +202,7 @@ object PickleQuotes { val name: String = "pickleQuotes" val description: String = "turn quoted trees into explicit run-time data structures" - def apply(quotes: Tree, body: Tree, contents: List[Tree], originalTp: Type, isType: Boolean)(using Context) = { + def pickle(quotes: Tree, body: Tree, contents: List[Tree], originalTp: Type)(using Context) = { /** Helper methods to construct trees calling methods in `Quotes.reflect` based on the current `quotes` tree */ object reflect extends ReifiedReflect { val quotesTree = quotes @@ -340,14 +334,14 @@ object PickleQuotes { case _ => Match(args(0).annotated(New(ref(defn.UncheckedAnnot.typeRef))), cases) ) - val quoteClass = if isType then defn.QuotedTypeClass else defn.QuotedExprClass + val quoteClass = if body.isType then defn.QuotedTypeClass else defn.QuotedExprClass val quotedType = quoteClass.typeRef.appliedTo(originalTp) val lambdaTpe = MethodType(defn.QuotesClass.typeRef :: Nil, quotedType) val unpickleMeth = - if isType then defn.QuoteUnpickler_unpickleTypeV2 + if body.isType then defn.QuoteUnpickler_unpickleTypeV2 else defn.QuoteUnpickler_unpickleExprV2 val unpickleArgs = - if isType then List(pickledQuoteStrings, types) + if body.isType then List(pickledQuoteStrings, types) else List(pickledQuoteStrings, types, termHoles) quotes .asInstance(defn.QuoteUnpicklerClass.typeRef) @@ -378,7 +372,7 @@ object PickleQuotes { case Inlined(_, Nil, e) => getLiteral(e) case _ => None - if (isType) then + if body.isType then if contents.isEmpty && body.symbol.isPrimitiveValueClass then taggedType() else pickleAsTasty() else diff --git a/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala b/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala index b2059195b8e4..bd0c063cd21a 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala @@ -75,8 +75,8 @@ trait ReifiedReflect: .select(defn.Quotes_reflect_TypeRepr_of) .appliedToType(tpe) .appliedTo( - ref(defn.QuotedTypeModule_of) - .appliedToType(tpe) + tpd.Quote(TypeTree(tpe)) + .select(nme.apply) .appliedTo(quotesTree) ) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index e8102a5db224..c833a0800e87 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -88,8 +88,6 @@ class Splicing extends MacroTransform: tree match case Apply(Select(_: Quote, nme.apply), _) => QuoteTransformer().transform(tree) - case TypeApply(_, _) if tree.symbol == defn.QuotedTypeModule_of => - QuoteTransformer().transform(tree) case tree: DefDef if tree.symbol.is(Inline) => // Quotes in inlined methods are only pickled after they are inlined. tree @@ -228,25 +226,18 @@ class Splicing extends MacroTransform: case tree @ Assign(lhs: RefTree, rhs) => if isCaptured(lhs.symbol) then transformSplicedAssign(tree) else super.transform(tree) - case Apply(sel @ Select(app @ Quote(body), nme.apply), quotesArgs) => - body match - case body: RefTree if isCaptured(body.symbol) => capturedTerm(body) - case _ => withCurrentQuote(quotesArgs.head) { super.transform(tree) } - case Apply(TypeApply(typeof, List(tpt)), List(quotes)) - if tree.symbol == defn.QuotedTypeModule_of && containsCapturedType(tpt.tpe) => - val newContent = capturedPartTypes(tpt) - newContent match - case block: Block => - inContext(ctx.withSource(tree.source)) { - Apply(TypeApply(typeof, List(newContent)), List(quotes)).withSpan(tree.span) - } - case _ => - ref(capturedType(newContent))(using ctx.withSource(tree.source)).withSpan(tree.span) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) - case Quote(expr) if level == 0 => - val newExpr = transformLevel0QuoteContent(expr)(using quoteContext) - cpy.Quote(tree)(newExpr) + case Apply(sel @ Select(app @ Quote(body), nme.apply), quotes :: Nil) if level == 0 && body.isTerm => + body match + case _: RefTree if isCaptured(body.symbol) => capturedTerm(body) + case _ => withCurrentQuote(quotes) { super.transform(tree) } + case Quote(body) if level == 0 => + val newBody = + if body.isTerm then transformLevel0QuoteContent(body)(using quoteContext) + else if containsCapturedType(body.tpe) then capturedPartTypes(body) + else body + cpy.Quote(tree)(newBody) case _ => super.transform(tree) @@ -400,7 +391,7 @@ class Splicing extends MacroTransform: Splice(closure, tpe) private def quoted(expr: Tree)(using Context): Tree = - Quote(expr, expr.tpe.widenTermRefExpr) + untpd.Quote(expr).withBodyType(expr.tpe.widenTermRefExpr) // TODO do we need widenTermRefExpr? .select(nme.apply) .appliedTo(quotes.nn) diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index a025f86a1db6..581fd2753504 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -57,6 +57,13 @@ class Staging extends MacroTransform { case _ => } + tree match { + case tree: RefTree => + assert(level != 0 || tree.symbol != defn.QuotedTypeModule_of, + "scala.quoted.Type.of at level 0 should have been replaced with Quote AST in staging phase") + case _ => + } + tree.tpe match { case tpe @ TypeRef(prefix, _) if tpe.typeSymbol.isTypeSplice => // Type splices must have a know term ref, usually to an implicit argument diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index ebff31414d68..5372dc4d04b6 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -61,7 +61,7 @@ trait QuotesAndSplices { // TODO typecheck directly (without `exprQuote`) val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.body) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match - case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => Quote(quotedExpr, tpt.tpe) + case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => untpd.Quote(quotedExpr).withBodyType(tpt.tpe) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) } @@ -382,7 +382,7 @@ trait QuotesAndSplices { * ) => ... * ``` */ - private def typedQuotePattern(tree: untpd.Quote, pt: Type, qctx: Tree)(using Context): Tree = { + private def typedQuotePattern(tree: untpd.Quote, pt: Type, quotes: Tree)(using Context): Tree = { val quoted = tree.body if quoted.isTerm && !pt.derivesFrom(defn.QuotedExprClass) then report.error("Quote pattern can only match scrutinees of type scala.quoted.Expr", tree.srcPos) @@ -442,11 +442,11 @@ trait QuotesAndSplices { val quoteClass = if (quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (quoted.isTerm) tpd.Quote(shape, defn.AnyType).select(nme.apply).appliedTo(qctx) - else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(qctx) + if (quoted.isTerm) tpd.Quote(shape).select(nme.apply).appliedTo(quotes) + else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(quotes) val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch - val unapplyFun = qctx.asInstance(defn.QuoteMatchingClass.typeRef).select(matchModule).select(nme.unapply) + val unapplyFun = quotes.asInstance(defn.QuoteMatchingClass.typeRef).select(matchModule).select(nme.unapply) UnApply( fun = unapplyFun.appliedToTypeTrees(typeBindingsTuple :: TypeTree(patType) :: Nil), diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index d8e9b115ea49..f8cfd4ca3ae0 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -97,7 +97,7 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = assertTyped(tree) - val body1 = typed(tree.body, tree.exprType)(using quoteContext) + val body1 = typed(tree.body, tree.bodyType)(using quoteContext) untpd.cpy.Quote(tree)(body1).withType(tree.typeOpt) override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 1df2abcf0453..245bcdac70af 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -637,7 +637,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case self: tpd.Quote => // TODO expose Quote AST in Quotes import dotty.tools.dotc.ast.tpd.TreeOps tpd.ref(dotc.core.Symbols.defn.QuotedRuntime_exprQuote) - .appliedToType(self.exprType) + .appliedToType(self.bodyType) .withSpan(self.span) case self: tpd.Splice => // TODO expose Splice AST in Quotes import dotty.tools.dotc.ast.tpd.TreeOps From 3a4e1ccca3a49268ac5a348becd5d9657586469b Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Fri, 28 Apr 2023 15:17:02 +0200 Subject: [PATCH 453/657] =?UTF-8?q?Move=20=E2=80=9CWildcard=20Types?= =?UTF-8?q?=E2=80=9D=20to=20a=20subsection=20of=20=E2=80=9CParameterized?= =?UTF-8?q?=20Types=E2=80=9D.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specify them independently of `forSome`. --- docs/_spec/03-types.md | 105 +++++++++++++++++++++-------------- docs/_spec/06-expressions.md | 26 ++++----- 2 files changed, 73 insertions(+), 58 deletions(-) diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index 5e1da82f1f11..a1ad1ddffa57 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -8,13 +8,9 @@ chapter: 3 ```ebnf Type ::= FunctionArgTypes ‘=>’ Type - | InfixType [ExistentialClause] + | InfixType FunctionArgTypes ::= InfixType | ‘(’ [ ParamType {‘,’ ParamType } ] ‘)’ - ExistentialClause ::= ‘forSome’ ‘{’ ExistentialDcl - {semi ExistentialDcl} ‘}’ - ExistentialDcl ::= ‘type’ TypeDcl - | ‘val’ ValDcl InfixType ::= CompoundType {id [nl] CompoundType} CompoundType ::= AnnotType {‘with’ AnnotType} [Refinement] | Refinement @@ -163,13 +159,14 @@ SimpleType ::= SimpleType TypeArgs TypeArgs ::= ‘[’ Types ‘]’ ``` -A _parameterized type_ ´T[ T_1, ..., T_n ]´ consists of a type designator ´T´ and type parameters ´T_1, ..., T_n´ where ´n \geq 1´. +A _parameterized type_ ´T[ T_1, ..., T_n ]´ consists of a type designator ´T´ and type arguments ´T_1, ..., T_n´ where ´n \geq 1´. ´T´ must refer to a type constructor which takes ´n´ type parameters ´a_1, ..., a_n´. + Say the type parameters have lower bounds ´L_1, ..., L_n´ and upper bounds ´U_1, ..., U_n´. -The parameterized type is well-formed if each actual type parameter _conforms to its bounds_, i.e. ´\sigma L_i <: T_i <: \sigma U_i´ where ´\sigma´ is the substitution ´[ a_1 := T_1, ..., a_n := T_n ]´. +The parameterized type is well-formed if each type argument _conforms to its bounds_, i.e. ´\sigma L_i <: T_i <: \sigma U_i´ where ´\sigma´ is the substitution ´[ a_1 := T_1, ..., a_n := T_n ]´. -###### Example Parameterized Types +#### Example Parameterized Types Given the partial type definitions: @@ -178,9 +175,9 @@ class TreeMap[A <: Comparable[A], B] { ... } class List[A] { ... } class I extends Comparable[I] { ... } -class F[M[_], X] { ... } +class F[M[A], X] { ... } class S[K <: String] { ... } -class G[M[ Z <: I ], I] { ... } +class G[M[Z <: I], I] { ... } ``` the following parameterized types are well-formed: @@ -194,9 +191,7 @@ F[List, Int] G[S, String] ``` -###### Example - -Given the [above type definitions](#example-parameterized-types), the following types are ill-formed: +and the following types are ill-formed: ```scala TreeMap[I] // illegal: wrong number of parameters @@ -211,6 +206,50 @@ G[S, Int] // illegal: S constrains its parameter to // that conforms to Int ``` +#### Wildcard Type Argument + + +```ebnf +WildcardType ::= ‘_’ TypeBounds +``` + +A _wildcard type argument_ is of the form `_´\;´>:´\,L\,´<:´\,U´`. +A wildcard type must appear as a type argument of a parameterized type. +The parameterized type to which the wildcard type is applied cannot be an abstract type constructor. + +Both bound clauses may be omitted. +If both bounds are omitted, the real bounds are inferred from the bounds of the corresponding type parameter in the target type constructor. +Otherwise, if a lower bound clause `>:´\,L´` is missing, `>:´\,´scala.Nothing` is assumed. +Otherwise, if an upper bound clause `<:´\,U´` is missing, `<:´\,´scala.Any` is assumed. + +Given the [above type definitions](#example-parameterized-types), the following types are well-formed: + +```scala +List[_] // inferred as List[_ >: Nothing <: Any] +List[_ <: java.lang.Number] +S[_ <: String] +F[_, Boolean] +``` + +and the following code contains an ill-formed type: + +```scala +trait H[F[A]]: + def f: F[_] // illegal : an abstract type constructor + // cannot be applied to wildcard arguments. +``` + +Wildcard types may also appear as parts of [infix types](#infix-types), [function types](#function-types), or [tuple types](#tuple-types). +Their expansion is then the expansion in the equivalent parameterized type. + +##### Simplification Rules + +Let ´T[T_1, ..., T_n]´ be a parameterized type. +Then, applying a wildcard type argument ´t´ of the form ´\\_ >: L <: U´ at the ´i´'th position obeys the following equivalences: + +- If the type parameter ´T_i´ is declared covariant, then ´t \equiv U´ +- If the type parameter ´T_i´ is declared contravariant, then ´t \equiv L´ + ### Tuple Types ```ebnf @@ -355,30 +394,6 @@ trait Function´_n´[-´T_1´, ..., -´T_n´, +´R´]: Their exact supertype and implementation can be consulted in the [function classes section](./12-the-scala-standard-library.md#the-function-classes) of the standard library page in this document. -#### Wildcard Types - -```ebnf -WildcardType ::= ‘_’ TypeBounds -``` - -A _wildcard type_ is of the form `_´\;´>:´\,L\,´<:´\,U´`. -Both bound clauses may be omitted. -If a lower bound clause `>:´\,L´` is missing, `>:´\,´scala.Nothing` is assumed. -If an upper bound clause `<:´\,U´` is missing, `<:´\,´scala.Any` is assumed. -A wildcard type is a shorthand for an existentially quantified type variable, where the existential quantification is implicit. - -A wildcard type must appear as a type argument of a parameterized type. -Let ´T = p.c[\mathit{targs},T,\mathit{targs}']´ be a parameterized type where ´\mathit{targs}, \mathit{targs}'´ may be empty and ´T´ is a wildcard type `_´\ ´>:´\,L\,´<:´\,U´`. -Then ´T´ is equivalent to the existential type - -```scala -´p.c[\mathit{targs},t,\mathit{targs}']´ forSome { type ´t´ >: ´L´ <: ´U´ } -``` - -where ´t´ is some fresh type variable. -Wildcard types may also appear as parts of [infix types](#infix-types), [function types](#function-types), or [tuple types](#tuple-types). -Their expansion is then the expansion in the equivalent parameterized type. - ## Non-Value Types The types explained in the following do not denote sets of values, nor do they appear explicitly in programs. @@ -563,7 +578,6 @@ Equivalence ´(\equiv)´ between types is the smallest congruence [^congruence] - corresponding parameters have equivalent types. Note that the names of parameters do not matter for method type equivalence. - Two [polymorphic method types](#polymorphic-method-types) are equivalent if they have the same number of type parameters, and, after renaming one set of type parameters by another, the result types as well as lower and upper bounds of corresponding type parameters are equivalent. -- Two [existential types](#existential-types) are equivalent if they have the same number of quantifiers, and, after renaming one list of type quantifiers by another, the quantified types as well as lower and upper bounds of corresponding quantifiers are equivalent. - Two [type constructors](#type-constructors) are equivalent if they have the same number of type parameters, and, after renaming one list of type parameters by another, the result types as well as variances, lower and upper bounds of corresponding type parameters are equivalent. [^congruence]: A congruence is an equivalence relation which is closed under formation of contexts. @@ -573,7 +587,7 @@ Equivalence ´(\equiv)´ between types is the smallest congruence [^congruence] The conformance relation ´(<:)´ is the smallest transitive relation that satisfies the following conditions. -- Conformance includes equivalence. If `T \equiv U` then `T <: U`. +- Conformance includes equivalence. If ´T \equiv U´ then ´T <: U´. - For every value type `T`, `scala.Nothing <: ´T´ <: scala.Any`. - For every type constructor ´T´ (with any number of type parameters), `scala.Nothing <: ´T´ <: scala.Any`. - For every value type ´T´, `scala.Null <: ´T´` unless `´T´ <: scala.AnyVal`. @@ -582,10 +596,13 @@ The conformance relation ´(<:)´ is the smallest transitive relation that satis - A singleton type `´p´.type` conforms to the type of the path ´p´. - A singleton type `´p´.type` conforms to the type `scala.Singleton`. - A type projection `´T´#´t´` conforms to `´U´#´t´` if ´T´ conforms to ´U´. -- A parameterized type `´T´[´T_1´, ..., ´T_n´]` conforms to `´T´[´U_1´, ..., ´U_n´]` if the following three conditions hold for ´i \in \{ 1, ..., n \}´: - 1. If the ´i´'th type parameter of ´T´ is declared covariant, then ´T_i <: U_i´. - 1. If the ´i´'th type parameter of ´T´ is declared contravariant, then ´U_i <: T_i´. - 1. If the ´i´'th type parameter of ´T´ is declared neither covariant nor contravariant, then ´U_i \equiv T_i´. +- A parameterized type `´T´[´T_1´, ..., ´T_n´]` conforms to `´T´[´U_1´, ..., ´U_n´]` if the following conditions hold for ´i \in \{ 1, ..., n \}´: + 1. If the ´i´'th type parameter of ´T´ is declared covariant, then ´T_i <: U_i´. [^argisnotwildcard] + 1. If the ´i´'th type parameter of ´T´ is declared contravariant, then ´U_i <: T_i´. [^argisnotwildcard] + 1. If the ´i´'th type parameter of ´T´ is declared neither covariant nor contravariant: + 1. If neither ´T_i´ nor ´U_i´ are wildcard type arguments, then ´U_i \equiv T_i´. + 1. If ´T_i´ is a wildcard type argument of the form ´\\_ >: L_1 <: U_1´ and ´U_i´ is a wildcard argument of the form ´\\_ >: L_2 <: U_2´, then ´L_2 <: L_1´ and ´H_1 <: H_2´ (i.e., the ´T_i´ "interval" is contained in the ´U_i´ "interval"). + 1. If ´U_i´ is a wildcard type argument of the form ´\\_ >: L_2 <: U_2´, then ´L_2 <: T_i´ and ´T_i <: U_2´. - A compound type `´T_1´ with ... with ´T_n´ {´R\,´}` conforms to each of its component types ´T_i´. - If ´T <: U_i´ for ´i \in \{ 1, ..., n \}´ and for every binding ´d´ of a type or value ´x´ in ´R´ there exists a member binding of ´x´ in ´T´ which subsumes ´d´, then ´T´ conforms to the compound type `´U_1´ with ... with ´U_n´ {´R\,´}`. - If ´T_i \equiv T_i'´ for ´i \in \{ 1, ..., n\}´ and ´U´ conforms to ´U'´ then the method type ´(p_1:T_1, ..., p_n:T_n) U´ conforms to ´(p_1':T_1', ..., p_n':T_n') U'´. @@ -598,6 +615,8 @@ Note that this entails that: - The variance of ´a_i´ must match the variance of ´a'_i´, where covariance matches covariance, contravariance matches contravariance and any variance matches invariance. - Recursively, these restrictions apply to the corresponding higher-order type parameter clauses of ´a_i´ and ´a'_i´. + [^argisnotwildcard]: In these cases, if `T_i` and/or `U_i` are wildcard type arguments, the [simplification rules](#simplification-rules) for parameterized types allow to reduce them to real types. + A declaration or definition in some compound type of class type ´C´ _subsumes_ another declaration of the same name in some compound type or class type ´C'´, if one of the following holds. - A value declaration or definition that defines a name ´x´ with type ´T´ subsumes a value or method declaration that defines ´x´ with type ´T'´, provided ´T <: T'´. diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index bb90c2b071a4..5adb58ec626c 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -460,16 +460,13 @@ The expected type of the final expression ´e´ is the expected type of the bloc The expected type of all preceding statements is undefined. -The type of a block `´s_1´; ...; ´s_n´; ´e´` is `´T´ forSome {´\,Q\,´}`, where ´T´ is the type of ´e´ and ´Q´ contains [existential clauses](03-types.html#existential-types) for every value or type name which is free in ´T´ and which is defined locally in one of the statements ´s_1, ..., s_n´. -We say the existential clause _binds_ the occurrence of the value or type name. -Specifically, +The type of a block `´s_1´; ...; ´s_n´; ´e´` is some type ´T´ such that: -- A locally defined type definition `type´\;t = T´` is bound by the existential clause `type´\;t >: T <: T´`. -It is an error if ´t´ carries type parameters. -- A locally defined value definition `val´\;x: T = e´` is bound by the existential clause `val´\;x: T´`. -- A locally defined class definition `class´\;c´ extends´\;t´` is bound by the existential clause `type´\;c <: T´` where ´T´ is the least class type or refinement type which is a proper supertype of the type ´c´. -It is an error if ´c´ carries type parameters. -- A locally defined object definition `object´\;x\;´extends´\;t´` is bound by the existential clause `val´\;x: T´` where ´T´ is the least class type or refinement type which is a proper supertype of the type `´x´.type`. +- ´U <: T´ where ´U´ is the type of ´e´. +- No value or type name is free in ´T´, i.e., ´T´ does not refer to any value or type locally defined in one of the statements ´s_1, ..., s_n´. +- ´T´ is "as small as possible" (this is a soft requirement). + +The precise way in which we compute ´T´, called _type avoidance_, is currently not defined in this specification. Evaluation of the block entails evaluation of its statement sequence, followed by an evaluation of the final expression ´e´, which defines the result of the block. @@ -1181,12 +1178,11 @@ question: given --> - A parameterized method ´m´ of type `(´p_1:T_1, ..., p_n:T_n´)´U´` is _as specific as_ some other member ´m'´ of type ´S´ if ´m'´ is [applicable](#method-applications) to arguments `(´p_1, ..., p_n´)` of types ´T_1, ..., T_n´. -- A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is as specific as some other member of type ´S´ if ´T´ is as specific as ´S´ under the assumption that for ´i = 1, ..., n´ each ´a_i´ is an abstract type name bounded from below by ´L_i´ and from above by ´U_i´. -- A member of any other type is always as specific as a parameterized method or a polymorphic method. - -- Given two members of types ´T´ and ´U´ which are neither parameterized nor polymorphic method types, the member of type ´T´ is as specific as the member of type ´U´ if the existential dual of ´T´ conforms to the existential dual of ´U´. -Here, the existential dual of a polymorphic type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is `´T´ forSome { type ´a_1´ >: ´L_1´ <: ´U_1´, ..., type ´a_n´ >: ´L_n´ <: ´U_n´}`. -The existential dual of every other type is the type itself. + If the last parameter `´p_n´` has a vararg type `´T*´`, then `m` must be applicable to arbitrary numbers of `´T´` parameters (which implies that it must be a varargs method as well). +- A polymorphic method of type `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]´T´` is as specific as some other member ´m'´ of type ´S´ if ´T´ is as specific as ´S´ under the assumption that for ´i = 1, ..., n´ each ´a_i´ is an abstract type name bounded from below by ´L_i´ and from above by ´U_i´. +- A member of any other type ´T´ is: + - always as specific as a parameterized method or a polymorphic method. + - as specific as a member ´m'´ of any other type ´S´ if ´T´ is [compatible](03-types.html#compatibility) with ´S´. The _relative weight_ of an alternative ´A´ over an alternative ´B´ is a number from 0 to 2, defined as the sum of From 095a7f1e53b8339c21b238e77245f0027e0c5719 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 11:00:07 +0200 Subject: [PATCH 454/657] Remove state from TreeMapWithStages --- .../tools/dotc/staging/CrossStageSafety.scala | 2 +- .../tools/dotc/staging/StagingLevel.scala | 4 +++ .../dotc/staging/TreeMapWithStages.scala | 27 +++---------------- 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index cb5f3520429a..fbf9eac7af03 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -50,7 +50,7 @@ class CrossStageSafety extends TreeMapWithStages { override def transform(tree: Tree)(using Context): Tree = if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) - else if !isInQuoteOrSplice then + else if !inQuoteOrSpliceScope then checkAnnotations(tree) super.transform(tree) else tree match { diff --git a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala index 4704501e38ff..05b3efab408c 100644 --- a/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala +++ b/compiler/src/dotty/tools/dotc/staging/StagingLevel.scala @@ -31,6 +31,10 @@ object StagingLevel { def spliceContext(using Context): FreshContext = ctx.fresh.setProperty(LevelKey, level - 1) + /** If we are inside a quote or a splice */ + def inQuoteOrSpliceScope(using Context): Boolean = + ctx.property(LevelKey).isDefined + /** The quotation level of the definition of the locally defined symbol */ def levelOf(sym: Symbol)(using Context): Int = ctx.property(LevelOfKey) match diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index e262530ba11e..6c193ee7a34b 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -15,12 +15,6 @@ import scala.collection.mutable abstract class TreeMapWithStages extends TreeMapWithImplicits { import tpd._ - /** If we are inside a quote or a splice */ - private[this] var inQuoteOrSplice = false - - /** If we are inside a quote or a splice */ - protected def isInQuoteOrSplice: Boolean = inQuoteOrSplice - /** Transform the quote `quote` which contains the quoted `body`. * * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` @@ -53,15 +47,9 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { tree match { case tree @ QuotedTypeOf(quotedTree) => - val old = inQuoteOrSplice - inQuoteOrSplice = true - try transformQuotedType(quotedTree, tree) - finally inQuoteOrSplice = old - + transformQuotedType(quotedTree, tree) case tree @ Quote(quotedTree) => - val old = inQuoteOrSplice - inQuoteOrSplice = true - try dropEmptyBlocks(quotedTree) match { + dropEmptyBlocks(quotedTree) match { case Splice(t) => // Optimization: `'{ $x }` --> `x` // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` @@ -69,25 +57,18 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { case _ => transformQuote(quotedTree, tree) } - finally inQuoteOrSplice = old case tree @ Splice(splicedTree) => - val old = inQuoteOrSplice - inQuoteOrSplice = true - try dropEmptyBlocks(splicedTree) match { + dropEmptyBlocks(splicedTree) match { case Quote(t) => // Optimization: `${ 'x }` --> `x` transform(t) case _ => transformSplice(splicedTree, tree) } - finally inQuoteOrSplice = old case tree @ SplicedType(splicedTree) => - val old = inQuoteOrSplice - inQuoteOrSplice = true - try transformSpliceType(splicedTree, tree) - finally inQuoteOrSplice = old + transformSpliceType(splicedTree, tree) case Block(stats, _) => val defSyms = stats.collect { case defTree: DefTree => defTree.symbol } From 10cbc605f9ead7a144b70af607c72e46fa105e54 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 11:13:43 +0200 Subject: [PATCH 455/657] Fix wrong staging level error message The tests were failing when the code was correct. The wrong message was ``` access to parameter t from wrong staging level: - the definition is at level 0, - but the access is at level -1. ``` The access to `t.Underlying` or `t.x.Underlying` are fine when we have an implicit `Type.of[t.Underlying]` or `Type.of[t.x.Underlying]`. These will probably be of the type of the aliased type of `A`, wherefore summoning `Type.of[A]`. --- .../src/dotty/tools/dotc/staging/CrossStageSafety.scala | 4 +++- .../src/dotty/tools/dotc/staging/TreeMapWithStages.scala | 6 ------ tests/neg-macros/i10127-a.scala | 2 +- tests/neg-macros/i10127-b.scala | 2 +- tests/pos-macros/i10127-a.scala | 8 ++++++++ 5 files changed, 13 insertions(+), 9 deletions(-) create mode 100644 tests/pos-macros/i10127-a.scala diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index fbf9eac7af03..02404337db80 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -92,6 +92,8 @@ class CrossStageSafety extends TreeMapWithStages { case tree: TypeDef if tree.symbol.is(Case) && level > 0 => report.error(reporting.CaseClassInInlinedCode(tree), tree) super.transform(tree) + case tree @ SplicedType(splicedTree) => + transformSpliceType(splicedTree, tree) case _ => super.transform(tree) } @@ -151,7 +153,7 @@ class CrossStageSafety extends TreeMapWithStages { untpd.cpy.Splice(splice)(body1).withType(tpe1) } - protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { + private def transformSpliceType(body: Tree, splice: Select)(using Context): Tree = { val body1 = transform(body)(using spliceContext) if ctx.reporter.hasErrors then splice diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 6c193ee7a34b..31a764fe7040 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -33,9 +33,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { /** Transform the expression splice `splice` which contains the spliced `body`. */ protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree - /** Transform the type splice `splice` which contains the spliced `body`. */ - protected def transformSpliceType(body: Tree, splice: Select)(using Context): Tree - override def transform(tree: Tree)(using Context): Tree = if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) @@ -67,9 +64,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { transformSplice(splicedTree, tree) } - case tree @ SplicedType(splicedTree) => - transformSpliceType(splicedTree, tree) - case Block(stats, _) => val defSyms = stats.collect { case defTree: DefTree => defTree.symbol } super.transform(tree)(using symbolsInCurrentLevel(defSyms)) diff --git a/tests/neg-macros/i10127-a.scala b/tests/neg-macros/i10127-a.scala index 3e23cf10bd30..2da4d0924870 100644 --- a/tests/neg-macros/i10127-a.scala +++ b/tests/neg-macros/i10127-a.scala @@ -1,7 +1,7 @@ import scala.quoted.* object T { - def impl[A](using t: Type[A])(using Quotes): Expr[Unit] = { + def impl[A](t: Type[A])(using Quotes): Expr[Unit] = { Expr.summon[t.Underlying] // error '{} } diff --git a/tests/neg-macros/i10127-b.scala b/tests/neg-macros/i10127-b.scala index 2e87e92efa63..13992bf95362 100644 --- a/tests/neg-macros/i10127-b.scala +++ b/tests/neg-macros/i10127-b.scala @@ -4,7 +4,7 @@ case class T(x: Type[_ <: Any]) object T { def impl[A](t: T)(using ctx: Quotes): Expr[Unit] = { - Expr.summon[t.x.Underlying] // error // error + Expr.summon[t.x.Underlying] // error '{} } } \ No newline at end of file diff --git a/tests/pos-macros/i10127-a.scala b/tests/pos-macros/i10127-a.scala new file mode 100644 index 000000000000..3b9efc2a829d --- /dev/null +++ b/tests/pos-macros/i10127-a.scala @@ -0,0 +1,8 @@ +import scala.quoted.* + +object T { + def impl[A](using t: Type[A])(using Quotes): Expr[Unit] = { + Expr.summon[t.Underlying] + '{} + } +} \ No newline at end of file From 3097f490dc3e56a7ff6146753f93285216477bc0 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 11:28:55 +0200 Subject: [PATCH 456/657] Simplify quoted type transformation in CrossStageSafety --- .../dotty/tools/dotc/staging/CrossStageSafety.scala | 9 +++++++-- .../dotty/tools/dotc/staging/TreeMapWithStages.scala | 10 ---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 02404337db80..d4ccf3321222 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -52,7 +52,10 @@ class CrossStageSafety extends TreeMapWithStages { transform(tree)(using ctx.withSource(tree.source)) else if !inQuoteOrSpliceScope then checkAnnotations(tree) - super.transform(tree) + tree match + case tree @ QuotedTypeOf(quotedTree) => + transformQuotedType(quotedTree, tree) + case _ => super.transform(tree) else tree match { case _: TypeTree => val tp1 = transformTypeAnnotationSplices(tree.tpe) @@ -92,6 +95,8 @@ class CrossStageSafety extends TreeMapWithStages { case tree: TypeDef if tree.symbol.is(Case) && level > 0 => report.error(reporting.CaseClassInInlinedCode(tree), tree) super.transform(tree) + case tree @ QuotedTypeOf(quotedTree) => + transformQuotedType(quotedTree, tree) case tree @ SplicedType(splicedTree) => transformSpliceType(splicedTree, tree) case _ => @@ -109,7 +114,7 @@ class CrossStageSafety extends TreeMapWithStages { cpy.Quote(quote)(transformedBody).withBodyType(bodyType1) } - override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { + private def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) body.tpe match diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 31a764fe7040..67fb17551253 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -22,14 +22,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = cpy.Quote(quote)(body) - /** Transform the quote `quote` which contains the quoted `body`. - * - * - `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` - */ - protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = - val TypeApply(fun, _) = quote.fun: @unchecked - cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, body :: Nil), quote.args) - /** Transform the expression splice `splice` which contains the spliced `body`. */ protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree @@ -43,8 +35,6 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits { } tree match { - case tree @ QuotedTypeOf(quotedTree) => - transformQuotedType(quotedTree, tree) case tree @ Quote(quotedTree) => dropEmptyBlocks(quotedTree) match { case Splice(t) => From 8a9871fe5c0599ae42bb8378157b265e4269d247 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 11:45:39 +0200 Subject: [PATCH 457/657] Simplify Quote/Splice transformations in CrossStageSafety --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 21 +++++++++++ .../tools/dotc/staging/CrossStageSafety.scala | 20 +++++++++- .../dotc/staging/TreeMapWithStages.scala | 37 +------------------ 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 19c189047c6d..fbc68ebe63d7 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -714,6 +714,17 @@ object Trees { defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, exprType) withType(quoteType) + + /** Cancel this Quote if it contains a Splice */ + def cancelled(using Context): Option[Tree[T]] = + def rec(tree: Tree[T]): Option[Tree[T]] = tree match + case Block(Nil, expr) => rec(expr) + case Splice(inner) => + // Optimization: `'{ $x }` --> `x` + // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` + Some(inner) + case _ => None + rec(body) } /** A tree representing a splice `${ expr }` @@ -730,6 +741,16 @@ object Trees { case class Splice[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] + + /** Cancel this Splice if it contains a Quote */ + def cancelled(using Context): Option[Tree[T]] = + def rec(tree: Tree[T]): Option[Tree[T]] = tree match + case Block(Nil, expr1) => rec(expr1) + case Quote(body) => + // Optimization: `${ 'x }` --> `x` + Some(body) + case _ => None + rec(expr) } /** A type tree that represents an existing or inferred type */ diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index d4ccf3321222..37654bc0673b 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -53,6 +53,14 @@ class CrossStageSafety extends TreeMapWithStages { else if !inQuoteOrSpliceScope then checkAnnotations(tree) tree match + case tree @ Quote(quotedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? + case None => transformQuote(quotedTree, tree) + case tree @ Splice(splicedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1) + case None => transformSplice(splicedTree, tree) case tree @ QuotedTypeOf(quotedTree) => transformQuotedType(quotedTree, tree) case _ => super.transform(tree) @@ -95,6 +103,14 @@ class CrossStageSafety extends TreeMapWithStages { case tree: TypeDef if tree.symbol.is(Case) && level > 0 => report.error(reporting.CaseClassInInlinedCode(tree), tree) super.transform(tree) + case tree @ Quote(quotedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? + case None => transformQuote(quotedTree, tree) + case tree @ Splice(splicedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1) + case None => transformSplice(splicedTree, tree) case tree @ QuotedTypeOf(quotedTree) => transformQuotedType(quotedTree, tree) case tree @ SplicedType(splicedTree) => @@ -104,7 +120,7 @@ class CrossStageSafety extends TreeMapWithStages { } /** Transform quoted trees while maintaining level correctness */ - override protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = { + private def transformQuote(body: Tree, quote: Quote)(using Context): Tree = { if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", quote.srcPos) val transformedBody = transformQuoteBody(body, quote.span) @@ -150,7 +166,7 @@ class CrossStageSafety extends TreeMapWithStages { * - If inside inlined code, expand the macro code. * - If inside of a macro definition, check the validity of the macro. */ - protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree = { + private def transformSplice(body: Tree, splice: Splice)(using Context): Tree = { val body1 = transform(body)(using spliceContext) val tpe1 = if level == 0 then splice.tpe diff --git a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala index 67fb17551253..674dfff2f642 100644 --- a/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala +++ b/compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala @@ -6,7 +6,6 @@ import dotty.tools.dotc.config.Printers.staging import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Symbols._ -import dotty.tools.dotc.util.Property import dotty.tools.dotc.staging.StagingLevel.* import scala.collection.mutable @@ -15,45 +14,11 @@ import scala.collection.mutable abstract class TreeMapWithStages extends TreeMapWithImplicits { import tpd._ - /** Transform the quote `quote` which contains the quoted `body`. - * - * - `quoted.runtime.Expr.quote[T]()` --> `quoted.runtime.Expr.quote[T]()` - */ - protected def transformQuote(body: Tree, quote: Quote)(using Context): Tree = - cpy.Quote(quote)(body) - - /** Transform the expression splice `splice` which contains the spliced `body`. */ - protected def transformSplice(body: Tree, splice: Splice)(using Context): Tree - override def transform(tree: Tree)(using Context): Tree = if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) - else reporting.trace(i"StagingTransformer.transform $tree at $level", staging, show = true) { - def dropEmptyBlocks(tree: Tree): Tree = tree match { - case Block(Nil, expr) => dropEmptyBlocks(expr) - case _ => tree - } - + else reporting.trace(i"TreeMapWithStages.transform $tree at $level", staging, show = true) { tree match { - case tree @ Quote(quotedTree) => - dropEmptyBlocks(quotedTree) match { - case Splice(t) => - // Optimization: `'{ $x }` --> `x` - // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` - transform(t).asInstance(tree.tpe) - case _ => - transformQuote(quotedTree, tree) - } - - case tree @ Splice(splicedTree) => - dropEmptyBlocks(splicedTree) match { - case Quote(t) => - // Optimization: `${ 'x }` --> `x` - transform(t) - case _ => - transformSplice(splicedTree, tree) - } - case Block(stats, _) => val defSyms = stats.collect { case defTree: DefTree => defTree.symbol } super.transform(tree)(using symbolsInCurrentLevel(defSyms)) From 966835b1b20f61c65fd60c6fc68dfbd20e2e0add Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 11:50:57 +0200 Subject: [PATCH 458/657] Deduplicate code that transforms `Quote`, `Splice`, `Type.of` --- .../tools/dotc/staging/CrossStageSafety.scala | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 37654bc0673b..350f90216ba4 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -50,21 +50,20 @@ class CrossStageSafety extends TreeMapWithStages { override def transform(tree: Tree)(using Context): Tree = if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) - else if !inQuoteOrSpliceScope then - checkAnnotations(tree) - tree match - case tree @ Quote(quotedTree) => - tree.cancelled match - case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? - case None => transformQuote(quotedTree, tree) - case tree @ Splice(splicedTree) => - tree.cancelled match - case Some(tree1) => transform(tree1) - case None => transformSplice(splicedTree, tree) - case tree @ QuotedTypeOf(quotedTree) => - transformQuotedType(quotedTree, tree) - case _ => super.transform(tree) - else tree match { + else tree match + case tree @ Quote(quotedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? + case None => transformQuote(quotedTree, tree) + case tree @ Splice(splicedTree) => + tree.cancelled match + case Some(tree1) => transform(tree1) + case None => transformSplice(splicedTree, tree) + case tree @ QuotedTypeOf(quotedTree) => + transformQuotedType(quotedTree, tree) + case _ if !inQuoteOrSpliceScope => + checkAnnotations(tree) + super.transform(tree) case _: TypeTree => val tp1 = transformTypeAnnotationSplices(tree.tpe) val healedType = healType(tree.srcPos)(tp1) @@ -103,21 +102,11 @@ class CrossStageSafety extends TreeMapWithStages { case tree: TypeDef if tree.symbol.is(Case) && level > 0 => report.error(reporting.CaseClassInInlinedCode(tree), tree) super.transform(tree) - case tree @ Quote(quotedTree) => - tree.cancelled match - case Some(tree1) => transform(tree1.asInstance(tree.tpe)) // TODO is this asInstance still needed? - case None => transformQuote(quotedTree, tree) - case tree @ Splice(splicedTree) => - tree.cancelled match - case Some(tree1) => transform(tree1) - case None => transformSplice(splicedTree, tree) - case tree @ QuotedTypeOf(quotedTree) => - transformQuotedType(quotedTree, tree) case tree @ SplicedType(splicedTree) => transformSpliceType(splicedTree, tree) case _ => super.transform(tree) - } + end transform /** Transform quoted trees while maintaining level correctness */ private def transformQuote(body: Tree, quote: Quote)(using Context): Tree = { From 038dc9c7fbca231ace7025df3f38bffe54ea2f7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 1 May 2023 13:55:37 +0200 Subject: [PATCH 459/657] Remove extra whitespace in the spec. And ensure there is a single EOL at EOF. --- docs/_spec/01-lexical-syntax.md | 2 +- docs/_spec/03-types.md | 14 +++++++------- .../_spec/04-basic-declarations-and-definitions.md | 4 ++-- docs/_spec/05-classes-and-objects.md | 2 +- docs/_spec/06-expressions.md | 2 +- docs/_spec/08-pattern-matching.md | 8 ++++---- docs/_spec/11-annotations.md | 2 +- docs/_spec/A1-deprecated.md | 2 +- docs/_spec/A2-scala-2-compatibility.md | 2 +- docs/_spec/A3-to-be-deprecated.md | 2 +- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md index e2470e27d2e0..d12c778cb7de 100644 --- a/docs/_spec/01-lexical-syntax.md +++ b/docs/_spec/01-lexical-syntax.md @@ -448,7 +448,7 @@ Inside an interpolated string none of the usual escape characters are interprete Note that the sequence `\"` does not close a normal string literal (enclosed in single quotes). There are three forms of dollar sign escape. -The most general form encloses an expression in `${` and `}`, i.e. `${expr}`. +The most general form encloses an expression in `${` and `}`, i.e. `${expr}`. The expression enclosed in the braces that follow the leading `$` character is of syntactical category BlockExpr. Hence, it can contain multiple statements, and newlines are significant. Single ‘$’-signs are not permitted in isolation in an interpolated string. diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index a1ad1ddffa57..c1cdb110bda8 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -45,7 +45,7 @@ Non-value types capture properties of identifiers that [are not values](#non-val For example, a [type constructor](#type-constructors) does not directly specify a type of values. However, when a type constructor is applied to the correct type arguments, it yields a proper type, which may be a value type. -Non-value types are expressed indirectly in Scala. +Non-value types are expressed indirectly in Scala. E.g., a method type is described by writing down a method signature, which in itself is not a real type, although it gives rise to a corresponding [method type](#method-types). Type constructors are another example, as one can write `type Swap[m[_, _], a,b] = m[b, a]`, but there is no syntax to write the corresponding anonymous type function directly. @@ -130,7 +130,7 @@ SimpleType ::= StableId ``` A _type designator_ refers to a named value type. -It can be simple or qualified. +It can be simple or qualified. All such type designators are shorthands for type projections. Specifically, the unqualified type name ´t´ where ´t´ is bound in some class, object, or package ´C´ is taken as a shorthand for @@ -306,14 +306,14 @@ RefineStat ::= Dcl | ``` -A _compound type_ ´T_1´ `with` ... `with` ´T_n \\{ R \\}´ represents objects with members as given in the component types ´T_1, ..., T_n´ and the refinement ´\\{ R \\}´. +A _compound type_ ´T_1´ `with` ... `with` ´T_n \\{ R \\}´ represents objects with members as given in the component types ´T_1, ..., T_n´ and the refinement ´\\{ R \\}´. A refinement ´\\{ R \\}´ contains declarations and type definitions. If a declaration or definition overrides a declaration or definition in one of the component types ´T_1, ..., T_n´, the usual rules for [overriding](05-classes-and-objects.html#overriding) apply; otherwise the declaration or definition is said to be “structural” [^2]. [^2]: A reference to a structurally defined member (method call or access to a value or variable) may generate binary code that is significantly slower than an equivalent code to a non-structural member. -Within a method declaration in a structural refinement, the type of any value parameter may only refer to type parameters or abstract types that are contained inside the refinement. -That is, it must refer either to a type parameter of the method itself, or to a type definition within the refinement. +Within a method declaration in a structural refinement, the type of any value parameter may only refer to type parameters or abstract types that are contained inside the refinement. +That is, it must refer either to a type parameter of the method itself, or to a type definition within the refinement. This restriction does not apply to the method's result type. If no refinement is given, the empty refinement is implicitly added, i.e. ´T_1´ `with` ... `with` ´T_n´ is a shorthand for ´T_1´ `with` ... `with` ´T_n \\{\\}´. @@ -382,7 +382,7 @@ Function types associate to the right, e.g. ´S \Rightarrow T \Rightarrow R´ is Function types are [covariant](04-basic-declarations-and-definitions.md#variance-annotations) in their result type and [contravariant](04-basic-declarations-and-definitions.md#variance-annotations) in their argument types. Function types are shorthands for class types that define an `apply` method. -Specifically, the ´n´-ary function type ´(T_1, ..., T_n) \Rightarrow R´ is a shorthand for the class type `Function´_n´[´T_1´, ..., ´T_n´, ´R´]`. +Specifically, the ´n´-ary function type ´(T_1, ..., T_n) \Rightarrow R´ is a shorthand for the class type `Function´_n´[´T_1´, ..., ´T_n´, ´R´]`. In particular ´() \Rightarrow R´ is a shorthand for class type `Function´_0´[´R´]`. Such class types behave as if they were instances of the following trait: @@ -396,7 +396,7 @@ Their exact supertype and implementation can be consulted in the [function class ## Non-Value Types -The types explained in the following do not denote sets of values, nor do they appear explicitly in programs. +The types explained in the following do not denote sets of values, nor do they appear explicitly in programs. They are introduced in this report as the internal types of defined identifiers. ### Method Types diff --git a/docs/_spec/04-basic-declarations-and-definitions.md b/docs/_spec/04-basic-declarations-and-definitions.md index 1d50fd649efd..e0985c68dbc6 100644 --- a/docs/_spec/04-basic-declarations-and-definitions.md +++ b/docs/_spec/04-basic-declarations-and-definitions.md @@ -79,7 +79,7 @@ final val x = e ``` where `e` is a [constant expression](06-expressions.html#constant-expressions). -The `final` modifier must be present and no type annotation may be given. +The `final` modifier must be present and no type annotation may be given. References to the constant value `x` are themselves treated as constant expressions; in the generated code they are replaced by the definition's right-hand side `e`. Value definitions can alternatively have a [pattern](08-pattern-matching.html#patterns) as left-hand side. @@ -311,7 +311,7 @@ TypeParam ::= (id | ‘_’) [TypeParamClause] [‘>:’ Type] [‘<:’ Type parameters appear in type definitions, class definitions, and method definitions. In this section we consider only type parameter definitions with lower bounds `>: ´L´` and upper bounds `<: ´U´` whereas a discussion of context bounds `: ´U´` and view bounds `<% ´U´` is deferred to [here](07-implicits.html#context-bounds-and-view-bounds). -The most general form of a proper type parameter is +The most general form of a proper type parameter is `´@a_1 ... @a_n´ ´\pm´ ´t´ >: ´L´ <: ´U´`. Here, ´L´, and ´U´ are lower and upper bounds that constrain possible type arguments for the parameter. It is a compile-time error if ´L´ does not conform to ´U´. diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index b73a4aa56071..f02aa5368b30 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -538,7 +538,7 @@ These are defined by constructor definitions of the form `def this(´\mathit{ps} Such a definition introduces an additional constructor for the enclosing class, with parameters as given in the formal parameter lists ´\mathit{ps}_1 , ..., \mathit{ps}_n´, and whose evaluation is defined by the constructor expression ´e´. The scope of each formal parameter is the subsequent parameter sections and the constructor expression ´e´. A constructor expression is either a self constructor invocation `this(´\mathit{args}_1´)...(´\mathit{args}_n´)` or a block which begins with a self constructor invocation. -The self constructor invocation must construct a generic instance of the class. +The self constructor invocation must construct a generic instance of the class. I.e. if the class in question has name ´C´ and type parameters `[´\mathit{tps}\,´]`, then a self constructor invocation must generate an instance of `´C´[´\mathit{tps}\,´]`; it is not permitted to instantiate formal type parameters. The signature and the self constructor invocation of a constructor definition are type-checked and evaluated in the scope which is in effect at the point of the enclosing class definition, augmented by any type parameters of the enclosing class. diff --git a/docs/_spec/06-expressions.md b/docs/_spec/06-expressions.md index 5adb58ec626c..fa21b4330728 100644 --- a/docs/_spec/06-expressions.md +++ b/docs/_spec/06-expressions.md @@ -713,7 +713,7 @@ Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pat Guard ::= ‘if’ PostfixExpr ``` -A _for loop_ `for (´\mathit{enums}\,´) ´e´` executes expression ´e´ for each binding generated by the enumerators ´\mathit{enums}´. +A _for loop_ `for (´\mathit{enums}\,´) ´e´` executes expression ´e´ for each binding generated by the enumerators ´\mathit{enums}´. A _for comprehension_ `for (´\mathit{enums}\,´) yield ´e´` evaluates expression ´e´ for each binding generated by the enumerators ´\mathit{enums}´ and collects the results. An enumerator sequence always starts with a generator; this can be followed by further generators, value definitions, or guards. diff --git a/docs/_spec/08-pattern-matching.md b/docs/_spec/08-pattern-matching.md index f16f4c3dfa88..ee7b0d246aa7 100644 --- a/docs/_spec/08-pattern-matching.md +++ b/docs/_spec/08-pattern-matching.md @@ -113,10 +113,10 @@ Taking XML as an example implicit class XMLinterpolation(s: StringContext) = { object xml { def apply(exprs: Any*) = - // parse ‘s’ and build an XML tree with ‘exprs’ + // parse ‘s’ and build an XML tree with ‘exprs’ //in the holes def unapplySeq(xml: Node): Option[Seq[Node]] = - // match `s’ against `xml’ tree and produce + // match `s’ against `xml’ tree and produce //subtrees in holes } } @@ -500,7 +500,7 @@ Each case consists of a (possibly guarded) pattern ´p_i´ and a block ´b_i´. Each ´p_i´ might be complemented by a guard `if ´e´` where ´e´ is a boolean expression. The scope of the pattern variables in ´p_i´ comprises the pattern's guard and the corresponding block ´b_i´. -Let ´T´ be the type of the selector expression ´e´ and let ´a_1, ..., a_m´ be the type parameters of all methods enclosing the pattern matching expression. +Let ´T´ be the type of the selector expression ´e´ and let ´a_1, ..., a_m´ be the type parameters of all methods enclosing the pattern matching expression. For every ´a_i´, let ´L_i´ be its lower bound and ´U_i´ be its higher bound. Every pattern ´p \in \{p_1,, ..., p_n\}´ can be typed in two ways. First, it is attempted to type ´p´ with ´T´ as its expected type. @@ -566,7 +566,7 @@ def eval[T](t: Term[T]): T = t match { Note that the evaluator makes crucial use of the fact that type parameters of enclosing methods can acquire new bounds through pattern matching. -For instance, the type of the pattern in the second case, `Succ(u)`, is `Int`. +For instance, the type of the pattern in the second case, `Succ(u)`, is `Int`. It conforms to the selector type `T` only if we assume an upper and lower bound of `Int` for `T`. Under the assumption `Int <: T <: Int` we can also verify that the type right hand side of the second case, `Int` conforms to its expected type, `T`. diff --git a/docs/_spec/11-annotations.md b/docs/_spec/11-annotations.md index 11325a1639f0..3388d55318ea 100644 --- a/docs/_spec/11-annotations.md +++ b/docs/_spec/11-annotations.md @@ -109,7 +109,7 @@ trait Function0[@specialized(Unit, Int, Double) T] { ``` Whenever the static type of an expression matches a specialized variant of a definition, the compiler will instead use the specialized version. See the [specialization sid](https://docs.scala-lang.org/sips/scala-specialization.html) for more details of the implementation. - + ## User-defined Annotations diff --git a/docs/_spec/A1-deprecated.md b/docs/_spec/A1-deprecated.md index e2ebc8af1d35..649c2d7d92e6 100644 --- a/docs/_spec/A1-deprecated.md +++ b/docs/_spec/A1-deprecated.md @@ -18,4 +18,4 @@ scalac Test.scala | For now, you can also `import language.deprecated.symbolLiterals` to accept | the idiom, but this possibility might no longer be available in the future. 1 error found -``` \ No newline at end of file +``` diff --git a/docs/_spec/A2-scala-2-compatibility.md b/docs/_spec/A2-scala-2-compatibility.md index 3621887581a6..461bd37592b0 100644 --- a/docs/_spec/A2-scala-2-compatibility.md +++ b/docs/_spec/A2-scala-2-compatibility.md @@ -32,4 +32,4 @@ def f(): Unit = { ... } Scala 3 accepts the old syntax under the `-source:3.0-migration` option. If the `-migration` option is set, it can even rewrite old syntax to new. The [Scalafix](https://scalacenter.github.io/scalafix/) tool also -can rewrite procedure syntax to make it Scala 3 compatible. \ No newline at end of file +can rewrite procedure syntax to make it Scala 3 compatible. diff --git a/docs/_spec/A3-to-be-deprecated.md b/docs/_spec/A3-to-be-deprecated.md index adc37111520c..98f758dee2d4 100644 --- a/docs/_spec/A3-to-be-deprecated.md +++ b/docs/_spec/A3-to-be-deprecated.md @@ -1,4 +1,4 @@ This is a simple list of feature that are not deprecated yet, but will be in the future. They should emit warnings or errors only when using the `-source:future` compiler flag. -- [private[this] and protected[this]](../_docs/reference/dropped-features/this-qualifier.md) \ No newline at end of file +- [private[this] and protected[this]](../_docs/reference/dropped-features/this-qualifier.md) From e6c0b58d9a346c521945743eb2ccad243082e024 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Mon, 1 May 2023 13:55:36 +0200 Subject: [PATCH 460/657] Fix #16899: Better handle X instanceOf P where X is T1 | T2 --- .../tools/dotc/transform/TypeTestsCasts.scala | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 3763af243881..378ad0679fe9 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -16,6 +16,8 @@ import util.Spans._ import reporting._ import config.Printers.{ transforms => debug } +import patmat.Typ + /** This transform normalizes type tests and type casts, * also replacing type tests with singleton argument type with reference equality check * Any remaining type tests @@ -51,7 +53,8 @@ object TypeTestsCasts { * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). * 7. if `P` is a refinement type, "it's a refinement type" * 8. if `P` is a local class which is not statically reachable from the scope where `X` is defined, "it's a local class" - * 9. otherwise, "" + * 9. if `X` is `T1 | T2`, (isCheckDefinitelyFalse(T1, P) && checkable(T2, P)) or (checkable(T1, P) && isCheckDefinitelyFalse(T2, P)). + * 10. otherwise, "" */ def whyUncheckable(X: Type, P: Type, span: Span)(using Context): String = atPhase(Phases.refchecksPhase.next) { extension (inline s1: String) inline def &&(inline s2: String): String = if s1 == "" then s2 else s1 @@ -129,6 +132,34 @@ object TypeTestsCasts { } + /** Whether the check X.isInstanceOf[P] is definitely false? */ + def isCheckDefinitelyFalse(X: Type, P: Type)(using Context): Boolean = trace(s"isCheckDefinitelyFalse(${X.show}, ${P.show})") { + X.dealias match + case AndType(x1, x2) => + isCheckDefinitelyFalse(x1, P) || isCheckDefinitelyFalse(x2, P) + + case x => + P.dealias match + case AndType(p1, p2) => + isCheckDefinitelyFalse(x, p1) || isCheckDefinitelyFalse(x, p2) + + case p => + val pSpace = Typ(p) + val xSpace = Typ(x) + if pSpace.canDecompose then + val ps = pSpace.decompose.map(_.tp) + ps.forall(p => isCheckDefinitelyFalse(x, p)) + else if xSpace.canDecompose then + val xs = xSpace.decompose.map(_.tp) + xs.exists(x => isCheckDefinitelyFalse(x, p)) + else + val xClass = effectiveClass(x.widen) + val pClass = effectiveClass(p.widen) + + !xClass.derivesFrom(pClass) + && (xClass.is(Final) || pClass.is(Final) || !xClass.is(Trait) && !pClass.is(Trait)) + } + def recur(X: Type, P: Type): String = (X <:< P) ||| (P.dealias match { case _: SingletonType => "" case _: TypeProxy @@ -146,7 +177,14 @@ object TypeTestsCasts { // - T1 <:< T2 | T3 // - T1 & T2 <:< T3 // See TypeComparer#either - recur(tp1, P) && recur(tp2, P) + val res1 = recur(tp1, P) + val res2 = recur(tp2, P) + + if res1.isEmpty && res2.isEmpty then res1 + else if isCheckDefinitelyFalse(tp1, P) && res2.isEmpty then res2 + else if res1.isEmpty && isCheckDefinitelyFalse(tp2, P) then res1 + else res1 + case _ => // always false test warnings are emitted elsewhere X.classSymbol.exists && P.classSymbol.exists && @@ -302,8 +340,8 @@ object TypeTestsCasts { /** Transform isInstanceOf * - * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B] - * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B] + * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B] + * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B] * expr.isInstanceOf[Tuple] ~~> scala.runtime.Tuples.isInstanceOfTuple(expr) * expr.isInstanceOf[EmptyTuple] ~~> scala.runtime.Tuples.isInstanceOfEmptyTuple(expr) * expr.isInstanceOf[NonEmptyTuple] ~~> scala.runtime.Tuples.isInstanceOfNonEmptyTuple(expr) From 6f370fd997df863730f16e35e94272f36b2edca0 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Mon, 1 May 2023 13:56:22 +0200 Subject: [PATCH 461/657] Add test --- tests/pos-special/isInstanceOf/i16899.scala | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/pos-special/isInstanceOf/i16899.scala diff --git a/tests/pos-special/isInstanceOf/i16899.scala b/tests/pos-special/isInstanceOf/i16899.scala new file mode 100644 index 000000000000..650e1e5c7b23 --- /dev/null +++ b/tests/pos-special/isInstanceOf/i16899.scala @@ -0,0 +1,5 @@ +sealed trait Unset + +def foo(v: Unset|Option[Int]): Unit = v match + case v: Unset => () + case v: Option[Int] => () From 8b3eee87544530464dfe8bfabbfcaeade6287775 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Mon, 1 May 2023 13:56:38 +0200 Subject: [PATCH 462/657] Fix tests --- .../dotty/tools/dotc/transform/TypeTestsCasts.scala | 13 +++++++++---- tests/neg-custom-args/isInstanceOf/i5826.scala | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 378ad0679fe9..7684d7f2867e 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -151,7 +151,7 @@ object TypeTestsCasts { ps.forall(p => isCheckDefinitelyFalse(x, p)) else if xSpace.canDecompose then val xs = xSpace.decompose.map(_.tp) - xs.exists(x => isCheckDefinitelyFalse(x, p)) + xs.forall(x => isCheckDefinitelyFalse(x, p)) else val xClass = effectiveClass(x.widen) val pClass = effectiveClass(p.widen) @@ -181,9 +181,14 @@ object TypeTestsCasts { val res2 = recur(tp2, P) if res1.isEmpty && res2.isEmpty then res1 - else if isCheckDefinitelyFalse(tp1, P) && res2.isEmpty then res2 - else if res1.isEmpty && isCheckDefinitelyFalse(tp2, P) then res1 - else res1 + else if res2.isEmpty then + if isCheckDefinitelyFalse(tp1, P) then res2 + else i"it cannot be checked against the type $tp1" + else if res1.isEmpty then + if isCheckDefinitelyFalse(tp2, P) then res1 + else i"it cannot be checked against the type $tp2" + else + i"it cannot be checked against the type $tp2" case _ => // always false test warnings are emitted elsewhere diff --git a/tests/neg-custom-args/isInstanceOf/i5826.scala b/tests/neg-custom-args/isInstanceOf/i5826.scala index bff95e740b4f..89af39958541 100644 --- a/tests/neg-custom-args/isInstanceOf/i5826.scala +++ b/tests/neg-custom-args/isInstanceOf/i5826.scala @@ -1,6 +1,6 @@ class Foo { def test[A]: List[Int] | A => Int = { - case ls: List[Int] => ls.head // error + case ls: List[Int] => ls.head // ok, List decomposes to Some and None case _ => 0 } From 128c7a1682738d5167c393ee2c74e1ed7ab8acc3 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Mon, 1 May 2023 13:57:39 +0200 Subject: [PATCH 463/657] Fix doc --- compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 7684d7f2867e..15503b475d65 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -53,7 +53,7 @@ object TypeTestsCasts { * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). * 7. if `P` is a refinement type, "it's a refinement type" * 8. if `P` is a local class which is not statically reachable from the scope where `X` is defined, "it's a local class" - * 9. if `X` is `T1 | T2`, (isCheckDefinitelyFalse(T1, P) && checkable(T2, P)) or (checkable(T1, P) && isCheckDefinitelyFalse(T2, P)). + * 9. if `X` is `T1 | T2`, checkable(T1, P) && checkable(T2, P) or (isCheckDefinitelyFalse(T1, P) && checkable(T2, P)) or (checkable(T1, P) && isCheckDefinitelyFalse(T2, P)). * 10. otherwise, "" */ def whyUncheckable(X: Type, P: Type, span: Span)(using Context): String = atPhase(Phases.refchecksPhase.next) { From 757d01dd65d578ed929d83d8c1ec46bfe7fb3718 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 28 Apr 2023 10:55:21 +0200 Subject: [PATCH 464/657] =?UTF-8?q?docs:=20Replace=20=E2=80=A6=20by=20...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the same change already applied to docs/_spec in 38e79aae2f11522e55948f4073e852ac90d821fb, made here to make it easier to port changes between docs/_docs and doc/_spec. --- docs/_docs/internals/syntax-3.1.md | 14 +++++++------- docs/_docs/internals/syntax.md | 14 +++++++------- .../changed-features/overload-resolution.md | 4 ++-- docs/_docs/reference/syntax.md | 14 +++++++------- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/_docs/internals/syntax-3.1.md b/docs/_docs/internals/syntax-3.1.md index 4d4d3b6d858d..0104222f50f5 100644 --- a/docs/_docs/internals/syntax-3.1.md +++ b/docs/_docs/internals/syntax-3.1.md @@ -11,7 +11,7 @@ hexadecimal code: ```ebnf UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit -hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ +hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ ``` Informal descriptions are typeset as `“some comment”`. @@ -22,15 +22,15 @@ form. ```ebnf whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ -upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and Unicode category Lu” -lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll” -letter ::= upper | lower “… and Unicode categories Lo, Lt, Lm, Nl” -digit ::= ‘0’ | … | ‘9’ +upper ::= ‘A’ | ... | ‘Z’ | ‘\$’ | ‘_’ “... and Unicode category Lu” +lower ::= ‘a’ | ... | ‘z’ “... and Unicode category Ll” +letter ::= upper | lower “... and Unicode categories Lo, Lt, Lm, Nl” +digit ::= ‘0’ | ... | ‘9’ paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ | ‘'(’ | ‘'[’ | ‘'{’ delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ - “… and Unicode categories Sm, So” + “... and Unicode categories Sm, So” printableChar ::= “all characters in [\u0020, \u007E] inclusive” charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) @@ -49,7 +49,7 @@ integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] decimalNumeral ::= ‘0’ | nonZeroDigit {digit} hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit {hexDigit} digit ::= ‘0’ | nonZeroDigit -nonZeroDigit ::= ‘1’ | … | ‘9’ +nonZeroDigit ::= ‘1’ | ... | ‘9’ floatingPointLiteral ::= digit {digit} ‘.’ {digit} [exponentPart] [floatType] diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 445e86ee2408..35ec4955d27d 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -25,7 +25,7 @@ hexadecimal code: ```ebnf UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit -hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ +hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ ``` Informal descriptions are typeset as `“some comment”`. @@ -37,15 +37,15 @@ form. ```ebnf whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ -upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and Unicode category Lu” -lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll” -letter ::= upper | lower “… and Unicode categories Lo, Lt, Lm, Nl” -digit ::= ‘0’ | … | ‘9’ +upper ::= ‘A’ | ... | ‘Z’ | ‘\$’ | ‘_’ “... and Unicode category Lu” +lower ::= ‘a’ | ... | ‘z’ “... and Unicode category Ll” +letter ::= upper | lower “... and Unicode categories Lo, Lt, Lm, Nl” +digit ::= ‘0’ | ... | ‘9’ paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ - “… and Unicode categories Sm, So” + “... and Unicode categories Sm, So” printableChar ::= “all characters in [\u0020, \u007E] inclusive” charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) @@ -64,7 +64,7 @@ spliceId ::= ‘$’ alphaid ; integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] -nonZeroDigit ::= ‘1’ | … | ‘9’ +nonZeroDigit ::= ‘1’ | ... | ‘9’ floatingPointLiteral ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] diff --git a/docs/_docs/reference/changed-features/overload-resolution.md b/docs/_docs/reference/changed-features/overload-resolution.md index bd7782ded520..621515c2a7f8 100644 --- a/docs/_docs/reference/changed-features/overload-resolution.md +++ b/docs/_docs/reference/changed-features/overload-resolution.md @@ -66,11 +66,11 @@ as follows: Replace the sentence -> Otherwise, let `S1,…,Sm` be the vector of types obtained by typing each argument with an undefined expected type. +> Otherwise, let `S1,...,Sm` be the vector of types obtained by typing each argument with an undefined expected type. with the following paragraph: -> Otherwise, let `S1,…,Sm` be the vector of known types of all argument types, where the _known type_ of an argument `E` +> Otherwise, let `S1,...,Sm` be the vector of known types of all argument types, where the _known type_ of an argument `E` is determined as followed: - If `E` is a function value `(p_1, ..., p_n) => B` that misses some parameter types, the known type diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 6abc3b2011d1..96d88dc5932a 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -26,7 +26,7 @@ hexadecimal code: ``` UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit -hexDigit ::= ‘0’ | … | ‘9’ | ‘A’ | … | ‘F’ | ‘a’ | … | ‘f’ +hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ ``` Informal descriptions are typeset as `“some comment”`. @@ -38,15 +38,15 @@ form. ``` whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ -upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and Unicode category Lu” -lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll” -letter ::= upper | lower “… and Unicode categories Lo, Lt, Nl” -digit ::= ‘0’ | … | ‘9’ +upper ::= ‘A’ | ... | ‘Z’ | ‘\$’ | ‘_’ “... and Unicode category Lu” +lower ::= ‘a’ | ... | ‘z’ “... and Unicode category Ll” +letter ::= upper | lower “... and Unicode categories Lo, Lt, Nl” +digit ::= ‘0’ | ... | ‘9’ paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ - “… and Unicode categories Sm, So” + “... and Unicode categories Sm, So” printableChar ::= “all characters in [\u0020, \u007E] inclusive” charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) @@ -65,7 +65,7 @@ spliceId ::= ‘$’ alphaid ; integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] -nonZeroDigit ::= ‘1’ | … | ‘9’ +nonZeroDigit ::= ‘1’ | ... | ‘9’ floatingPointLiteral ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] From e5b41152c4abc833d5c24e840d422537b573ab28 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 28 Apr 2023 10:58:24 +0200 Subject: [PATCH 465/657] Synchronize {reference,TODOreference}/syntax.md --- docs/_spec/TODOreference/syntax.md | 48 ++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/docs/_spec/TODOreference/syntax.md b/docs/_spec/TODOreference/syntax.md index d3526783a5eb..96d88dc5932a 100644 --- a/docs/_spec/TODOreference/syntax.md +++ b/docs/_spec/TODOreference/syntax.md @@ -105,7 +105,10 @@ semi ::= ‘;’ | nl {nl} ## Optional Braces -The lexical analyzer also inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](./other-new-features/indentation.md). +The principle of optional braces is that any keyword that can be followed by `{` can also be followed by an indented block, without needing an intervening `:`. +(Allowing an optional `:` would be counterproductive since it would introduce several ways to do the same thing.) + +The lexical analyzer inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](./other-new-features/indentation.md). In the context-free productions below we use the notation `<<< ts >>>` to indicate a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent`. Analogously, the @@ -249,6 +252,7 @@ Catches ::= ‘catch’ (Expr | ExprCaseClause) PostfixExpr ::= InfixExpr [id] -- only if language.postfixOperators is enabled InfixExpr ::= PrefixExpr | InfixExpr id [nl] InfixExpr + | InfixExpr id ColonArgument | InfixExpr MatchClause MatchClause ::= ‘match’ <<< CaseClauses >>> PrefixExpr ::= [PrefixOperator] SimpleExpr @@ -267,6 +271,11 @@ SimpleExpr ::= SimpleRef | SimpleExpr ‘.’ MatchClause | SimpleExpr TypeArgs | SimpleExpr ArgumentExprs + | SimpleExpr ColonArgument +ColonArgument ::= colon [LambdaStart] + indent (CaseClauses | Block) outdent +LambdaStart ::= FunParams (‘=>’ | ‘?=>’) + | HkTypeParamClause ‘=>’ Quoted ::= ‘'’ ‘{’ Block ‘}’ | ‘'’ ‘[’ Type ‘]’ ExprSplice ::= spliceId -- if inside quoted block @@ -306,7 +315,10 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } -Pattern1 ::= Pattern2 [‘:’ RefinedType] +Pattern1 ::= PatVar ‘:’ RefinedType + | [‘-’] integerLiteral ‘:’ RefinedType + | [‘-’] floatingPointLiteral ‘:’ RefinedType + | Pattern2 Pattern2 ::= [id ‘@’] InfixPattern [‘*’] InfixPattern ::= SimplePattern { id [nl] SimplePattern } SimplePattern ::= PatVar @@ -329,9 +341,6 @@ ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] id [HkTypeParamClause] TypeParamBounds -DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ -DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds - TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds @@ -343,13 +352,20 @@ ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ ClsParams ::= ClsParam {‘,’ ClsParam} ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param -Param ::= id ‘:’ ParamType [‘=’ Expr] -DefParamClauses ::= {DefParamClause} [[nl] ‘(’ [‘implicit’] DefParams ‘)’] -DefParamClause ::= [nl] ‘(’ DefParams ‘)’ | UsingParamClause -UsingParamClause ::= [nl] ‘(’ ‘using’ (DefParams | FunArgTypes) ‘)’ -DefParams ::= DefParam {‘,’ DefParam} -DefParam ::= {Annotation} [‘inline’] Param +TypelessClauses ::= TypelessClause {TypelessClause} +TypelessClause ::= DefTermParamClause + | UsingParamClause + +DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ +DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds +DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ +UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’ +DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ + +DefTermParams ::= DefTermParam {‘,’ DefTermParam} +DefTermParam ::= {Annotation} [‘inline’] Param +Param ::= id ‘:’ ParamType [‘=’ Expr] ``` ### Bindings and Imports @@ -400,8 +416,8 @@ Dcl ::= RefineDcl ValDcl ::= ids ‘:’ Type VarDcl ::= ids ‘:’ Type DefDcl ::= DefSig ‘:’ Type -DefSig ::= id [DefTypeParamClause] DefParamClauses -TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds [‘=’ Type] +DefSig ::= id [DefTypeParamClause] [TypelessClauses] [DefImplicitClause] +TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds Def ::= ‘val’ PatDef | ‘var’ PatDef @@ -411,7 +427,7 @@ Def ::= ‘val’ PatDef PatDef ::= ids [‘:’ Type] ‘=’ Expr | Pattern2 [‘:’ Type] ‘=’ Expr DefDef ::= DefSig [‘:’ Type] ‘=’ Expr - | ‘this’ DefParamClause DefParamClauses ‘=’ ConstrExpr + | ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef @@ -423,10 +439,10 @@ ConstrMods ::= {Annotation} [AccessModifier] ObjectDef ::= id [Template] EnumDef ::= id ClassConstr InheritClauses EnumBody GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) -GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefParamClause`, `UsingParamClause` must be present +GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody] Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} - ‘(’ DefParam ‘)’ {UsingParamClause} ExtMethods + ‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef | Export From 692b2b376069222b57af8e798d1dff497eb167a7 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 28 Apr 2023 11:36:07 +0200 Subject: [PATCH 466/657] Align lexical syntax between docs and spec Scala 3 changes compared to the existing Scala 2 spec: - Reusing alphaid in the definition of plainid (this does not change its meaning) - Addition of quoteId and spliceId - Correctly specifying the use of _ in numeric literals. - Dropping symbolLiteral Scala 2 changes compared to the existing Scala 3 spec: - Various refactorings - Specifying the new Unicode escape handling stuff, this was already implemented in Scala 3 but not part of syntax.md (see #8480). --- docs/_docs/internals/syntax.md | 71 ++++++++++++++---------------- docs/_docs/reference/syntax.md | 71 ++++++++++++++---------------- docs/_spec/01-lexical-syntax.md | 20 ++++----- docs/_spec/13-syntax-summary.md | 25 ++++++----- docs/_spec/TODOreference/syntax.md | 11 +++-- 5 files changed, 97 insertions(+), 101 deletions(-) diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index 35ec4955d27d..c4e71ad11612 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -20,51 +20,46 @@ productions map to AST nodes. The following description of Scala tokens uses literal characters `‘c’` when referring to the ASCII fragment `\u0000` – `\u007F`. -_Unicode escapes_ are used to represent the [Unicode character](https://www.w3.org/International/articles/definitions-characters/) with the given -hexadecimal code: - -```ebnf -UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit -hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ -``` - -Informal descriptions are typeset as `“some comment”`. - ## Lexical Syntax -The lexical syntax of Scala is given by the following grammar in EBNF -form. +The lexical syntax of Scala is given by the following grammar in EBNF form: ```ebnf whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ -upper ::= ‘A’ | ... | ‘Z’ | ‘\$’ | ‘_’ “... and Unicode category Lu” -lower ::= ‘a’ | ... | ‘z’ “... and Unicode category Ll” -letter ::= upper | lower “... and Unicode categories Lo, Lt, Lm, Nl” +upper ::= ‘A’ | ... | ‘Z’ | ‘$’ and any character in Unicode categories Lu, Lt or Nl, + and any character in Unicode categories Lo and Lm that doesn't have + contributory property Other_Lowercase +lower ::= ‘a’ | ... | ‘z’ | ‘_’ and any character in Unicode category Ll, + and any character in Unicode categories Lo or Lm that has contributory + property Other_Lowercase +letter ::= upper | lower digit ::= ‘0’ | ... | ‘9’ paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ - “... and Unicode categories Sm, So” -printableChar ::= “all characters in [\u0020, \u007E] inclusive” + and any character in Unicode categories Sm or So +printableChar ::= all characters in [\u0020, \u007E] inclusive +UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit +hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) +escapeSeq ::= UnicodeEscape | charEscapeSeq op ::= opchar {opchar} varid ::= lower idrest -alphaid ::= upper idrest - | varid +boundvarid ::= varid + | ‘`’ varid ‘`’ plainid ::= alphaid | op id ::= plainid - | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’ + | ‘`’ { charNoBackQuoteOrNewline | escapeSeq } ‘`’ idrest ::= {letter | digit} [‘_’ op] quoteId ::= ‘'’ alphaid spliceId ::= ‘$’ alphaid ; integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] -decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] +decimalNumeral ::= ‘0’ | digit [{digit | ‘_’} digit] hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] -nonZeroDigit ::= ‘1’ | ... | ‘9’ floatingPointLiteral ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] @@ -75,25 +70,25 @@ floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ booleanLiteral ::= ‘true’ | ‘false’ -characterLiteral ::= ‘'’ (printableChar | charEscapeSeq) ‘'’ +characterLiteral ::= ‘'’ (charNoQuoteOrNewline | escapeSeq) ‘'’ stringLiteral ::= ‘"’ {stringElement} ‘"’ | ‘"""’ multiLineChars ‘"""’ -stringElement ::= printableChar \ (‘"’ | ‘\’) - | UnicodeEscape - | charEscapeSeq -multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} -processedStringLiteral - ::= alphaid ‘"’ {[‘\’] processedStringPart | ‘\\’ | ‘\"’} ‘"’ - | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ -processedStringPart +stringElement ::= charNoDoubleQuoteOrNewline + | escapeSeq +multiLineChars ::= {[‘"’] [‘"’] charNoDoubleQuote} {‘"’} + +interpolatedString + ::= alphaid ‘"’ {[‘\’] interpolatedStringPart | ‘\\’ | ‘\"’} ‘"’ + | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘\$’) | escape} {‘"’} ‘"""’ +interpolatedStringPart ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape -escape ::= ‘$$’ - | ‘$’ letter { letter | digit } - | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ -stringFormat ::= {printableChar \ (‘"’ | ‘}’ | ‘ ’ | ‘\t’ | ‘\n’)} - -symbolLiteral ::= ‘'’ plainid // until 2.13 +escape ::= ‘\$\$’ + | ‘\$"’ + | ‘\$’ alphaid + | ‘\$’ BlockExpr +alphaid ::= upper idrest + | varid comment ::= ‘/*’ “any sequence of characters; nested comments are allowed” ‘*/’ | ‘//’ “any sequence of characters up to end of line” @@ -159,7 +154,7 @@ SimpleLiteral ::= [‘-’] integerLiteral | characterLiteral | stringLiteral Literal ::= SimpleLiteral - | processedStringLiteral + | interpolatedStringLiteral | symbolLiteral | ‘null’ diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 96d88dc5932a..dd2e7ce118c4 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -21,51 +21,48 @@ productions map to AST nodes. The following description of Scala tokens uses literal characters `‘c’` when referring to the ASCII fragment `\u0000` – `\u007F`. -_Unicode escapes_ are used to represent the [Unicode character](https://www.w3.org/International/articles/definitions-characters/) with the given -hexadecimal code: - -``` -UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit -hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ -``` - Informal descriptions are typeset as `“some comment”`. ## Lexical Syntax -The lexical syntax of Scala is given by the following grammar in EBNF -form. +The lexical syntax of Scala is given by the following grammar in EBNF form: -``` +```ebnf whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ -upper ::= ‘A’ | ... | ‘Z’ | ‘\$’ | ‘_’ “... and Unicode category Lu” -lower ::= ‘a’ | ... | ‘z’ “... and Unicode category Ll” -letter ::= upper | lower “... and Unicode categories Lo, Lt, Nl” +upper ::= ‘A’ | ... | ‘Z’ | ‘$’ and any character in Unicode categories Lu, Lt or Nl, + and any character in Unicode categories Lo and Lm that doesn't have + contributory property Other_Lowercase +lower ::= ‘a’ | ... | ‘z’ | ‘_’ and any character in Unicode category Ll, + and any character in Unicode categories Lo or Lm that has contributory + property Other_Lowercase +letter ::= upper | lower digit ::= ‘0’ | ... | ‘9’ paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ - “... and Unicode categories Sm, So” -printableChar ::= “all characters in [\u0020, \u007E] inclusive” + and any character in Unicode categories Sm or So +printableChar ::= all characters in [\u0020, \u007E] inclusive +UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit +hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) +escapeSeq ::= UnicodeEscape | charEscapeSeq op ::= opchar {opchar} varid ::= lower idrest -alphaid ::= upper idrest - | varid +boundvarid ::= varid + | ‘`’ varid ‘`’ plainid ::= alphaid | op id ::= plainid - | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’ + | ‘`’ { charNoBackQuoteOrNewline | escapeSeq } ‘`’ idrest ::= {letter | digit} [‘_’ op] quoteId ::= ‘'’ alphaid spliceId ::= ‘$’ alphaid ; integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] -decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] +decimalNumeral ::= ‘0’ | digit [{digit | ‘_’} digit] hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] -nonZeroDigit ::= ‘1’ | ... | ‘9’ floatingPointLiteral ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] @@ -76,25 +73,25 @@ floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ booleanLiteral ::= ‘true’ | ‘false’ -characterLiteral ::= ‘'’ (printableChar | charEscapeSeq) ‘'’ +characterLiteral ::= ‘'’ (charNoQuoteOrNewline | escapeSeq) ‘'’ stringLiteral ::= ‘"’ {stringElement} ‘"’ | ‘"""’ multiLineChars ‘"""’ -stringElement ::= printableChar \ (‘"’ | ‘\’) - | UnicodeEscape - | charEscapeSeq -multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} -processedStringLiteral - ::= alphaid ‘"’ {[‘\’] processedStringPart | ‘\\’ | ‘\"’} ‘"’ - | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ -processedStringPart +stringElement ::= charNoDoubleQuoteOrNewline + | escapeSeq +multiLineChars ::= {[‘"’] [‘"’] charNoDoubleQuote} {‘"’} + +interpolatedString + ::= alphaid ‘"’ {[‘\’] interpolatedStringPart | ‘\\’ | ‘\"’} ‘"’ + | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘\$’) | escape} {‘"’} ‘"""’ +interpolatedStringPart ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape -escape ::= ‘$$’ - | ‘$’ letter { letter | digit } - | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ -stringFormat ::= {printableChar \ (‘"’ | ‘}’ | ‘ ’ | ‘\t’ | ‘\n’)} - -symbolLiteral ::= ‘'’ plainid // until 2.13 +escape ::= ‘\$\$’ + | ‘\$"’ + | ‘\$’ alphaid + | ‘\$’ BlockExpr +alphaid ::= upper idrest + | varid comment ::= ‘/*’ “any sequence of characters; nested comments are allowed” ‘*/’ | ‘//’ “any sequence of characters up to end of line” @@ -163,7 +160,7 @@ SimpleLiteral ::= [‘-’] integerLiteral | characterLiteral | stringLiteral Literal ::= SimpleLiteral - | processedStringLiteral + | interpolatedStringLiteral | symbolLiteral | ‘null’ diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md index d12c778cb7de..b042fab2e984 100644 --- a/docs/_spec/01-lexical-syntax.md +++ b/docs/_spec/01-lexical-syntax.md @@ -27,8 +27,9 @@ op ::= opchar {opchar} varid ::= lower idrest boundvarid ::= varid | ‘`’ varid ‘`’ -plainid ::= upper idrest - | varid +alphaid ::= upper idrest + | varid +plainid ::= alphaid | op id ::= plainid | ‘`’ { charNoBackQuoteOrNewline | escapeSeq } ‘`’ @@ -282,8 +283,8 @@ Literal ::= [‘-’] integerLiteral ```ebnf integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] -decimalNumeral ::= digit {digit} -hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit {hexDigit} +decimalNumeral ::= ‘0’ | digit [{digit | ‘_’} digit] +hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] ``` Values of type `Int` are all integer numbers between $-2\^{31}$ and $2\^{31}-1$, inclusive. @@ -312,12 +313,11 @@ The digits of a numeric literal may be separated by arbitrarily many underscores ### Floating Point Literals ```ebnf -floatingPointLiteral ::= digit {digit} ‘.’ digit {digit} [exponentPart] [floatType] - | ‘.’ digit {digit} [exponentPart] [floatType] - | digit {digit} exponentPart [floatType] - | digit {digit} [exponentPart] floatType -exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit {digit} -floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ +floatingPointLiteral + ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] + | decimalNumeral exponentPart [floatType] + | decimalNumeral floatType +exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit [{digit | ‘_’} digit] ``` Floating point literals are of type `Float` when followed by a floating point type suffix `F` or `f`, and are of type `Double` otherwise. diff --git a/docs/_spec/13-syntax-summary.md b/docs/_spec/13-syntax-summary.md index 7c1d394bd4e1..3de2298788b7 100644 --- a/docs/_spec/13-syntax-summary.md +++ b/docs/_spec/13-syntax-summary.md @@ -8,6 +8,8 @@ chapter: 13 The following descriptions of Scala tokens uses literal characters `‘c’` when referring to the ASCII fragment `\u0000` – `\u007F`. +Informal descriptions are typeset as `“some comment”`. + ## Lexical Syntax The lexical syntax of Scala is given by the following grammar in EBNF form: @@ -32,27 +34,30 @@ UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDi hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) escapeSeq ::= UnicodeEscape | charEscapeSeq + op ::= opchar {opchar} varid ::= lower idrest boundvarid ::= varid | ‘`’ varid ‘`’ -plainid ::= upper idrest +alphaid ::= upper idrest | varid +plainid ::= alphaid | op id ::= plainid | ‘`’ { charNoBackQuoteOrNewline | escapeSeq } ‘`’ idrest ::= {letter | digit} [‘_’ op] +quoteId ::= ‘'’ alphaid +spliceId ::= ‘$’ alphaid ; integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] -decimalNumeral ::= digit {digit} -hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit {hexDigit} +decimalNumeral ::= ‘0’ | digit [{digit | ‘_’} digit] +hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] floatingPointLiteral - ::= digit {digit} ‘.’ digit {digit} [exponentPart] [floatType] - | ‘.’ digit {digit} [exponentPart] [floatType] - | digit {digit} exponentPart [floatType] - | digit {digit} [exponentPart] floatType -exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit {digit} + ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] + | decimalNumeral exponentPart [floatType] + | decimalNumeral floatType +exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit [{digit | ‘_’} digit] floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ booleanLiteral ::= ‘true’ | ‘false’ @@ -74,10 +79,6 @@ escape ::= ‘\$\$’ | ‘\$"’ | ‘\$’ alphaid | ‘\$’ BlockExpr -alphaid ::= upper idrest - | varid - -symbolLiteral ::= ‘'’ plainid comment ::= ‘/*’ “any sequence of characters; nested comments are allowed” ‘*/’ | ‘//’ “any sequence of characters up to end of line” diff --git a/docs/_spec/TODOreference/syntax.md b/docs/_spec/TODOreference/syntax.md index 96d88dc5932a..c92bc2532e87 100644 --- a/docs/_spec/TODOreference/syntax.md +++ b/docs/_spec/TODOreference/syntax.md @@ -18,6 +18,8 @@ productions map to AST nodes. --> + + ## Optional Braces @@ -163,7 +166,7 @@ SimpleLiteral ::= [‘-’] integerLiteral | characterLiteral | stringLiteral Literal ::= SimpleLiteral - | processedStringLiteral + | interpolatedStringLiteral | symbolLiteral | ‘null’ From 45900c44f16fd8ffb9e250dbf4b42589a2133418 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 28 Apr 2023 12:21:40 +0200 Subject: [PATCH 467/657] Port most of syntax.md except context-free syntax section. --- docs/_spec/01-lexical-syntax.md | 56 +++++++++++++++++++++--------- docs/_spec/TODOreference/syntax.md | 5 +-- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/docs/_spec/01-lexical-syntax.md b/docs/_spec/01-lexical-syntax.md index b042fab2e984..de11de10402f 100644 --- a/docs/_spec/01-lexical-syntax.md +++ b/docs/_spec/01-lexical-syntax.md @@ -20,6 +20,28 @@ To construct tokens, characters are distinguished according to the following cla 1. Delimiter characters ``‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ ``. 1. Operator characters. These consist of all printable ASCII characters (`\u0020` - `\u007E`) that are in none of the sets above, mathematical symbols (`Sm`) and other symbols (`So`). +## Optional Braces + +The principle of optional braces is that any keyword that can be followed by `{` can also be followed by an indented block, without needing an intervening `:`. +(Allowing an optional `:` would be counterproductive since it would introduce several ways to do the same thing.) + +The lexical analyzer inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](./other-new-features/indentation.md). + +´\color{red}{\text{TODO SCALA3: Port soft-modifier.md and link it here.}}´ + +In the context-free productions below we use the notation `<<< ts >>>` to indicate a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent`. +Analogously, the notation `:<<< ts >>>` indicates a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent` that follows a `colon` token. + +A `colon` token reads as the standard colon "`:`" but is generated instead of it where `colon` is legal according to the context free syntax, but only if the previous token is an alphanumeric identifier, a backticked identifier, or one of the tokens `this`, `super`, `new`, "`)`", and "`]`". + +``` +colon ::= ':' -- with side conditions explained above + <<< ts >>> ::= ‘{’ ts ‘}’ + | indent ts outdent +:<<< ts >>> ::= [nl] ‘{’ ts ‘}’ + | colon indent ts outdent +``` + ## Identifiers ```ebnf @@ -80,28 +102,30 @@ Some examples of constant identifiers are The ‘$’ character is reserved for compiler-synthesized identifiers. User programs should not define identifiers that contain ‘$’ characters. +### Regular keywords + The following names are reserved words instead of being members of the syntactic class `id` of lexical identifiers. ```scala -abstract case catch class def -do else extends false final -finally for forSome if implicit -import lazy macro match new -null object override package private -protected return sealed super this -throw trait try true type -val var while with yield -_ : = => <- <: <% >: # @ +abstract case catch class def do else +enum export extends false final finally for +given if implicit import lazy match new +null object override package private protected return +sealed super then throw trait true try +type val var while with yield +: = <- => <: >: # +@ =>> ?=> ``` -The Unicode operators `\u21D2` ‘´\Rightarrow´’ and `\u2190` ‘´\leftarrow´’, which have the ASCII equivalents `=>` and `<-`, are also reserved. +### Soft keywords -> Here are examples of identifiers: -> ```scala -> x Object maxIndex p2p empty_? -> + `yield` αρετη _y dot_product_* -> __system _MAX_LEN_ -> ``` +Additionally, the following soft keywords are reserved only in some situations. + +´\color{red}{\text{TODO SCALA3: Port soft-modifier.md and link it here.}}´ + +``` +as derives end extension infix inline opaque open transparent using | * + - +``` diff --git a/docs/_spec/TODOreference/syntax.md b/docs/_spec/TODOreference/syntax.md index c92bc2532e87..bfffb8c4a618 100644 --- a/docs/_spec/TODOreference/syntax.md +++ b/docs/_spec/TODOreference/syntax.md @@ -18,7 +18,7 @@ productions map to AST nodes. --> - + ## Optional Braces @@ -153,6 +152,8 @@ as derives end extension infix inline opaque open transparent using | See the [separate section on soft keywords](./soft-modifier.md) for additional details on where a soft keyword is recognized. +--> + ## Context-free Syntax The context-free syntax of Scala is given by the following EBNF From 410ee0a98dc7abefe2564a953a3d0789a84305fe Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 28 Apr 2023 13:24:41 +0200 Subject: [PATCH 468/657] Port context-free syntax. --- docs/_spec/13-syntax-summary.md | 315 +++++------------- .../syntax.md | 4 - 2 files changed, 80 insertions(+), 239 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/syntax.md (99%) diff --git a/docs/_spec/13-syntax-summary.md b/docs/_spec/13-syntax-summary.md index 3de2298788b7..d16ff538441c 100644 --- a/docs/_spec/13-syntax-summary.md +++ b/docs/_spec/13-syntax-summary.md @@ -89,242 +89,87 @@ semi ::= ‘;’ | nl {nl} ## Context-free Syntax +´\color{red}{\text{TODO SCALA3: Once we're done porting the spec, make sure that +the references to grammar productions in the rest of the spec match this.}}´ + The context-free syntax of Scala is given by the following EBNF grammar: ```ebnf - Literal ::= [‘-’] integerLiteral - | [‘-’] floatingPointLiteral - | booleanLiteral - | characterLiteral - | stringLiteral - | interpolatedString - | symbolLiteral - | ‘null’ - - QualId ::= id {‘.’ id} - ids ::= id {‘,’ id} - - Path ::= StableId - | [id ‘.’] ‘this’ - StableId ::= id - | Path ‘.’ id - | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id - ClassQualifier ::= ‘[’ id ‘]’ - - Type ::= FunctionArgTypes ‘=>’ Type - | InfixType [ExistentialClause] - FunctionArgTypes ::= InfixType - | ‘(’ [ ParamType {‘,’ ParamType } ] ‘)’ - ExistentialClause ::= ‘forSome’ ‘{’ ExistentialDcl {semi ExistentialDcl} ‘}’ - ExistentialDcl ::= ‘type’ TypeDcl - | ‘val’ ValDcl - InfixType ::= CompoundType {id [nl] CompoundType} - CompoundType ::= AnnotType {‘with’ AnnotType} [Refinement] - | Refinement - AnnotType ::= SimpleType {Annotation} - SimpleType ::= SimpleType TypeArgs - | SimpleType ‘#’ id - | StableId - | Path ‘.’ ‘type’ - | ‘(’ Types ‘)’ - TypeArgs ::= ‘[’ Types ‘]’ - Types ::= Type {‘,’ Type} - Refinement ::= [nl] ‘{’ RefineStat {semi RefineStat} ‘}’ - RefineStat ::= Dcl - | ‘type’ TypeDef - | - TypePat ::= Type - - Ascription ::= ‘:’ InfixType - | ‘:’ Annotation {Annotation} - | ‘:’ ‘_’ ‘*’ - - Expr ::= (Bindings | [‘implicit’] id | ‘_’) ‘=>’ Expr - | Expr1 - Expr1 ::= ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] - | ‘while’ ‘(’ Expr ‘)’ {nl} Expr - | ‘try’ Expr [‘catch’ Expr] [‘finally’ Expr] - | ‘do’ Expr [semi] ‘while’ ‘(’ Expr ‘)’ - | ‘for’ (‘(’ Enumerators ‘)’ | ‘{’ Enumerators ‘}’) {nl} [‘yield’] Expr - | ‘throw’ Expr - | ‘return’ [Expr] - | [SimpleExpr ‘.’] id ‘=’ Expr - | PrefixOperator SimpleExpr ‘=’ Expr - | SimpleExpr1 ArgumentExprs ‘=’ Expr - | PostfixExpr - | PostfixExpr Ascription - | PostfixExpr ‘match’ ‘{’ CaseClauses ‘}’ - PostfixExpr ::= InfixExpr [id [nl]] - InfixExpr ::= PrefixExpr - | InfixExpr id [nl] InfixExpr - PrefixExpr ::= [PrefixOperator] SimpleExpr - PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ - SimpleExpr ::= ‘new’ (ClassTemplate | TemplateBody) - | BlockExpr - | SimpleExpr1 [‘_’] - SimpleExpr1 ::= Literal - | Path - | ‘_’ - | ‘(’ [Exprs] ‘)’ - | SimpleExpr ‘.’ id - | SimpleExpr TypeArgs - | SimpleExpr1 ArgumentExprs - | XmlExpr - Exprs ::= Expr {‘,’ Expr} - ArgumentExprs ::= ‘(’ [Exprs] ‘)’ - | ‘(’ [Exprs ‘,’] PostfixExpr ‘:’ ‘_’ ‘*’ ‘)’ - | [nl] BlockExpr - BlockExpr ::= ‘{’ CaseClauses ‘}’ - | ‘{’ Block ‘}’ - Block ::= BlockStat {semi BlockStat} [ResultExpr] - BlockStat ::= Import - | {Annotation} [‘implicit’] [‘lazy’] Def - | {Annotation} {LocalModifier} TmplDef - | Expr1 - | - ResultExpr ::= Expr1 - | (Bindings | ([‘implicit’] id | ‘_’) ‘:’ CompoundType) ‘=>’ Block - - Enumerators ::= Generator {semi Generator} - Generator ::= [‘case’] Pattern1 ‘<-’ Expr {[semi] Guard | semi Pattern1 ‘=’ Expr} - - CaseClauses ::= CaseClause { CaseClause } - CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block - Guard ::= ‘if’ PostfixExpr - - Pattern ::= Pattern1 { ‘|’ Pattern1 } - Pattern1 ::= boundvarid ‘:’ TypePat - | ‘_’ ‘:’ TypePat - | Pattern2 - Pattern2 ::= id [‘@’ Pattern3] - | Pattern3 - Pattern3 ::= SimplePattern - | SimplePattern { id [nl] SimplePattern } - SimplePattern ::= ‘_’ - | varid - | Literal - | StableId - | StableId ‘(’ [Patterns] ‘)’ - | StableId ‘(’ [Patterns ‘,’] [id ‘@’] ‘_’ ‘*’ ‘)’ - | ‘(’ [Patterns] ‘)’ - | XmlPattern - Patterns ::= Pattern [‘,’ Patterns] - | ‘_’ ‘*’ - - TypeParamClause ::= ‘[’ VariantTypeParam {‘,’ VariantTypeParam} ‘]’ - FunTypeParamClause::= ‘[’ TypeParam {‘,’ TypeParam} ‘]’ - VariantTypeParam ::= {Annotation} [‘+’ | ‘-’] TypeParam - TypeParam ::= (id | ‘_’) [TypeParamClause] [‘>:’ Type] [‘<:’ Type] - {‘<%’ Type} {‘:’ Type} - ParamClauses ::= {ParamClause} [[nl] ‘(’ ‘implicit’ Params ‘)’] - ParamClause ::= [nl] ‘(’ [Params] ‘)’ - Params ::= Param {‘,’ Param} - Param ::= {Annotation} id [‘:’ ParamType] [‘=’ Expr] - ParamType ::= Type - | ‘=>’ Type - | Type ‘*’ - ClassParamClauses ::= {ClassParamClause} - [[nl] ‘(’ ‘implicit’ ClassParams ‘)’] - ClassParamClause ::= [nl] ‘(’ [ClassParams] ‘)’ - ClassParams ::= ClassParam {‘,’ ClassParam} - ClassParam ::= {Annotation} {Modifier} [(‘val’ | ‘var’)] - id ‘:’ ParamType [‘=’ Expr] - Bindings ::= ‘(’ Binding {‘,’ Binding} ‘)’ - Binding ::= (id | ‘_’) [‘:’ Type] - - Modifier ::= LocalModifier - | AccessModifier - | ‘override’ - LocalModifier ::= ‘abstract’ - | ‘final’ - | ‘sealed’ - | ‘implicit’ - | ‘lazy’ - AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] - AccessQualifier ::= ‘[’ (id | ‘this’) ‘]’ - - Annotation ::= ‘@’ SimpleType {ArgumentExprs} - ConstrAnnotation ::= ‘@’ SimpleType ArgumentExprs - - TemplateBody ::= [nl] ‘{’ [SelfType] TemplateStat {semi TemplateStat} ‘}’ - TemplateStat ::= Import - | {Annotation [nl]} {Modifier} Def - | {Annotation [nl]} {Modifier} Dcl - | Expr - | - SelfType ::= id [‘:’ Type] ‘=>’ - | ‘this’ ‘:’ Type ‘=>’ - - Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} - ImportExpr ::= StableId ‘.’ (id | ‘_’ | ImportSelectors) - ImportSelectors ::= ‘{’ {ImportSelector ‘,’} (ImportSelector | ‘_’) ‘}’ - ImportSelector ::= id [‘=>’ id | ‘=>’ ‘_’] - - Dcl ::= ‘val’ ValDcl - | ‘var’ VarDcl - | ‘def’ FunDcl - | ‘type’ {nl} TypeDcl - - ValDcl ::= ids ‘:’ Type - VarDcl ::= ids ‘:’ Type - FunDcl ::= FunSig [‘:’ Type] - FunSig ::= id [FunTypeParamClause] ParamClauses - TypeDcl ::= id [TypeParamClause] [‘>:’ Type] [‘<:’ Type] - - PatVarDef ::= ‘val’ PatDef - | ‘var’ VarDef - Def ::= PatVarDef - | ‘def’ FunDef - | ‘type’ {nl} TypeDef - | TmplDef - PatDef ::= Pattern2 {‘,’ Pattern2} [‘:’ Type] ‘=’ Expr - VarDef ::= PatDef - | ids ‘:’ Type ‘=’ ‘_’ - FunDef ::= FunSig [‘:’ Type] ‘=’ Expr - | FunSig [nl] ‘{’ Block ‘}’ - | ‘this’ ParamClause ParamClauses - (‘=’ ConstrExpr | [nl] ConstrBlock) - TypeDef ::= id [TypeParamClause] ‘=’ Type - - TmplDef ::= [‘case’] ‘class’ ClassDef - | [‘case’] ‘object’ ObjectDef - | ‘trait’ TraitDef - ClassDef ::= id [TypeParamClause] {ConstrAnnotation} [AccessModifier] - ClassParamClauses ClassTemplateOpt - TraitDef ::= id [TypeParamClause] TraitTemplateOpt - ObjectDef ::= id ClassTemplateOpt - ClassTemplateOpt ::= ‘extends’ ClassTemplate | [[‘extends’] TemplateBody] - TraitTemplateOpt ::= ‘extends’ TraitTemplate | [[‘extends’] TemplateBody] - ClassTemplate ::= [EarlyDefs] ClassParents [TemplateBody] - TraitTemplate ::= [EarlyDefs] TraitParents [TemplateBody] - ClassParents ::= Constr {‘with’ AnnotType} - TraitParents ::= AnnotType {‘with’ AnnotType} - Constr ::= AnnotType {ArgumentExprs} - EarlyDefs ::= ‘{’ [EarlyDef {semi EarlyDef}] ‘}’ ‘with’ - EarlyDef ::= {Annotation [nl]} {Modifier} PatVarDef - - ConstrExpr ::= SelfInvocation - | ConstrBlock - ConstrBlock ::= ‘{’ SelfInvocation {semi BlockStat} ‘}’ - SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} - - TopStatSeq ::= TopStat {semi TopStat} - TopStat ::= {Annotation [nl]} {Modifier} TmplDef - | Import - | Packaging - | PackageObject - | - Packaging ::= ‘package’ QualId [nl] ‘{’ TopStatSeq ‘}’ - PackageObject ::= ‘package’ ‘object’ ObjectDef - - CompilationUnit ::= {‘package’ QualId semi} TopStatSeq +RefineDcl ::= ‘val’ ValDcl + | ‘def’ DefDcl + | ‘type’ {nl} TypeDcl +Dcl ::= RefineDcl + | ‘var’ VarDcl +ValDcl ::= ids ‘:’ Type +VarDcl ::= ids ‘:’ Type +DefDcl ::= DefSig ‘:’ Type +DefSig ::= id [DefTypeParamClause] [TypelessClauses] [DefImplicitClause] +TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds + +Def ::= ‘val’ PatDef + | ‘var’ PatDef + | ‘def’ DefDef + | ‘type’ {nl} TypeDcl + | TmplDef +PatDef ::= ids [‘:’ Type] ‘=’ Expr + | Pattern2 [‘:’ Type] ‘=’ Expr +DefDef ::= DefSig [‘:’ Type] ‘=’ Expr + | ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr + +TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef + | [‘case’] ‘object’ ObjectDef + | ‘enum’ EnumDef + | ‘given’ GivenDef +ClassDef ::= id ClassConstr [Template] +ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses +ConstrMods ::= {Annotation} [AccessModifier] +ObjectDef ::= id [Template] +EnumDef ::= id ClassConstr InheritClauses EnumBody +GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) +GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present +StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody] +Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} + ‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods +ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> +ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef + | Export +Template ::= InheritClauses [TemplateBody] +InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] +ConstrApps ::= ConstrApp ({‘,’ ConstrApp} | {‘with’ ConstrApp}) +ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} +ConstrExpr ::= SelfInvocation + | <<< SelfInvocation {semi BlockStat} >>> +SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} + +WithTemplateBody ::= <<< [SelfType] TemplateStat {semi TemplateStat} >>> +TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> +TemplateStat ::= Import + | Export + | {Annotation [nl]} {Modifier} Def + | {Annotation [nl]} {Modifier} Dcl + | Extension + | Expr1 + | EndMarker + | +SelfType ::= id [‘:’ InfixType] ‘=>’ + | ‘this’ ‘:’ InfixType ‘=>’ + +EnumBody ::= :<<< [SelfType] EnumStat {semi EnumStat} >>> +EnumStat ::= TemplateStat + | {Annotation [nl]} {Modifier} EnumCase +EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps]] | ids) + +TopStats ::= TopStat {semi TopStat} +TopStat ::= Import + | Export + | {Annotation [nl]} {Modifier} Def + | Extension + | Packaging + | PackageObject + | EndMarker + | +Packaging ::= ‘package’ QualId :<<< TopStats >>> +PackageObject ::= ‘package’ ‘object’ ObjectDef + +CompilationUnit ::= {‘package’ QualId semi} TopStats ``` - - diff --git a/docs/_spec/TODOreference/syntax.md b/docs/_spec/APPLIEDreference/syntax.md similarity index 99% rename from docs/_spec/TODOreference/syntax.md rename to docs/_spec/APPLIEDreference/syntax.md index bfffb8c4a618..0a2fad157566 100644 --- a/docs/_spec/TODOreference/syntax.md +++ b/docs/_spec/APPLIEDreference/syntax.md @@ -18,8 +18,6 @@ productions map to AST nodes. --> - - - ## Context-free Syntax The context-free syntax of Scala is given by the following EBNF From 2e2a55e986a457ca3111605669f6cefc1dd0d20d Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 28 Apr 2023 13:26:39 +0200 Subject: [PATCH 469/657] Context-free syntax refers to the weird optional brace meta-syntax Copied from 01-lexical-syntax.md --- docs/_spec/13-syntax-summary.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/_spec/13-syntax-summary.md b/docs/_spec/13-syntax-summary.md index d16ff538441c..2dc971fc9840 100644 --- a/docs/_spec/13-syntax-summary.md +++ b/docs/_spec/13-syntax-summary.md @@ -87,6 +87,16 @@ nl ::= ´\mathit{“new line character”}´ semi ::= ‘;’ | nl {nl} ``` +## Optional Braces + +``` +colon ::= ':' -- with side conditions explained in 01-literal-syntax.md + <<< ts >>> ::= ‘{’ ts ‘}’ + | indent ts outdent +:<<< ts >>> ::= [nl] ‘{’ ts ‘}’ + | colon indent ts outdent +``` + ## Context-free Syntax ´\color{red}{\text{TODO SCALA3: Once we're done porting the spec, make sure that From 645e149b82a1956a4e974501695897333d3d2747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Mon, 1 May 2023 14:15:55 +0200 Subject: [PATCH 470/657] Remove APPLIEDreference/syntax.md, as the syntax now lives in the spec. --- docs/_spec/APPLIEDreference/syntax.md | 488 -------------------------- 1 file changed, 488 deletions(-) delete mode 100644 docs/_spec/APPLIEDreference/syntax.md diff --git a/docs/_spec/APPLIEDreference/syntax.md b/docs/_spec/APPLIEDreference/syntax.md deleted file mode 100644 index 0a2fad157566..000000000000 --- a/docs/_spec/APPLIEDreference/syntax.md +++ /dev/null @@ -1,488 +0,0 @@ ---- -layout: doc-page -title: "Scala 3 Syntax Summary" -nightlyOf: https://docs.scala-lang.org/scala3/reference/syntax.html ---- - - - -The following description of Scala tokens uses literal characters `‘c’` when -referring to the ASCII fragment `\u0000` – `\u007F`. - -_Unicode escapes_ are used to represent the [Unicode character](https://www.w3.org/International/articles/definitions-characters/) with the given -hexadecimal code: - -``` -UnicodeEscape ::= ‘\’ ‘u’ {‘u’} hexDigit hexDigit hexDigit hexDigit -hexDigit ::= ‘0’ | ... | ‘9’ | ‘A’ | ... | ‘F’ | ‘a’ | ... | ‘f’ -``` - -Informal descriptions are typeset as `“some comment”`. - -## Lexical Syntax - -The lexical syntax of Scala is given by the following grammar in EBNF -form. - -``` -whiteSpace ::= ‘\u0020’ | ‘\u0009’ | ‘\u000D’ | ‘\u000A’ -upper ::= ‘A’ | ... | ‘Z’ | ‘\$’ | ‘_’ “... and Unicode category Lu” -lower ::= ‘a’ | ... | ‘z’ “... and Unicode category Ll” -letter ::= upper | lower “... and Unicode categories Lo, Lt, Nl” -digit ::= ‘0’ | ... | ‘9’ -paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ -delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’ -opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | - ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ - “... and Unicode categories Sm, So” -printableChar ::= “all characters in [\u0020, \u007E] inclusive” -charEscapeSeq ::= ‘\’ (‘b’ | ‘t’ | ‘n’ | ‘f’ | ‘r’ | ‘"’ | ‘'’ | ‘\’) - -op ::= opchar {opchar} -varid ::= lower idrest -alphaid ::= upper idrest - | varid -plainid ::= alphaid - | op -id ::= plainid - | ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’ -idrest ::= {letter | digit} [‘_’ op] -quoteId ::= ‘'’ alphaid -spliceId ::= ‘$’ alphaid ; - -integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’] -decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit] -hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit] -nonZeroDigit ::= ‘1’ | ... | ‘9’ - -floatingPointLiteral - ::= [decimalNumeral] ‘.’ digit [{digit | ‘_’} digit] [exponentPart] [floatType] - | decimalNumeral exponentPart [floatType] - | decimalNumeral floatType -exponentPart ::= (‘E’ | ‘e’) [‘+’ | ‘-’] digit [{digit | ‘_’} digit] -floatType ::= ‘F’ | ‘f’ | ‘D’ | ‘d’ - -booleanLiteral ::= ‘true’ | ‘false’ - -characterLiteral ::= ‘'’ (printableChar | charEscapeSeq) ‘'’ - -stringLiteral ::= ‘"’ {stringElement} ‘"’ - | ‘"""’ multiLineChars ‘"""’ -stringElement ::= printableChar \ (‘"’ | ‘\’) - | UnicodeEscape - | charEscapeSeq -multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} -interpolatedStringLiteral - ::= alphaid ‘"’ {[‘\’] interpolatedStringPart | ‘\\’ | ‘\"’} ‘"’ - | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ -interpolatedStringPart - ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape -escape ::= ‘$$’ - | ‘$’ letter { letter | digit } - | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ -stringFormat ::= {printableChar \ (‘"’ | ‘}’ | ‘ ’ | ‘\t’ | ‘\n’)} - -symbolLiteral ::= ‘'’ plainid // until 2.13 - -comment ::= ‘/*’ “any sequence of characters; nested comments are allowed” ‘*/’ - | ‘//’ “any sequence of characters up to end of line” - -nl ::= “new line character” -semi ::= ‘;’ | nl {nl} -``` - -## Optional Braces - -The principle of optional braces is that any keyword that can be followed by `{` can also be followed by an indented block, without needing an intervening `:`. -(Allowing an optional `:` would be counterproductive since it would introduce several ways to do the same thing.) - -The lexical analyzer inserts `indent` and `outdent` tokens that represent regions of indented code [at certain points](./other-new-features/indentation.md). - -In the context-free productions below we use the notation `<<< ts >>>` -to indicate a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent`. Analogously, the -notation `:<<< ts >>>` indicates a token sequence `ts` that is either enclosed in a pair of braces `{ ts }` or that constitutes an indented region `indent ts outdent` that follows -a `colon` token. - -A `colon` token reads as the standard colon "`:`" but is generated instead of it where `colon` is legal according to the context free syntax, but only if the previous token -is an alphanumeric identifier, a backticked identifier, or one of the tokens `this`, `super`, `new`, "`)`", and "`]`". - -``` -colon ::= ':' -- with side conditions explained above - <<< ts >>> ::= ‘{’ ts ‘}’ - | indent ts outdent -:<<< ts >>> ::= [nl] ‘{’ ts ‘}’ - | colon indent ts outdent -``` - -## Keywords - -### Regular keywords - -``` -abstract case catch class def do else -enum export extends false final finally for -given if implicit import lazy match new -null object override package private protected return -sealed super then throw trait true try -type val var while with yield -: = <- => <: >: # -@ =>> ?=> -``` - -### Soft keywords - -``` -as derives end extension infix inline opaque open transparent using | * + - -``` - -See the [separate section on soft keywords](./soft-modifier.md) for additional -details on where a soft keyword is recognized. - -## Context-free Syntax - -The context-free syntax of Scala is given by the following EBNF -grammar: - -### Literals and Paths -``` -SimpleLiteral ::= [‘-’] integerLiteral - | [‘-’] floatingPointLiteral - | booleanLiteral - | characterLiteral - | stringLiteral -Literal ::= SimpleLiteral - | interpolatedStringLiteral - | symbolLiteral - | ‘null’ - -QualId ::= id {‘.’ id} -ids ::= id {‘,’ id} - -SimpleRef ::= id - | [id ‘.’] ‘this’ - | [id ‘.’] ‘super’ [ClassQualifier] ‘.’ id - -ClassQualifier ::= ‘[’ id ‘]’ -``` - -### Types -``` -Type ::= FunType - | HkTypeParamClause ‘=>>’ Type - | FunParamClause ‘=>>’ Type - | MatchType - | InfixType -FunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type - | HKTypeParamClause '=>' Type -FunTypeArgs ::= InfixType - | ‘(’ [ FunArgTypes ] ‘)’ - | FunParamClause -FunParamClause ::= ‘(’ TypedFunParam {‘,’ TypedFunParam } ‘)’ -TypedFunParam ::= id ‘:’ Type -MatchType ::= InfixType `match` <<< TypeCaseClauses >>> -InfixType ::= RefinedType {id [nl] RefinedType} -RefinedType ::= AnnotType {[nl] Refinement} -AnnotType ::= SimpleType {Annotation} - -SimpleType ::= SimpleLiteral - | ‘?’ TypeBounds - | id - | Singleton ‘.’ id - | Singleton ‘.’ ‘type’ - | ‘(’ Types ‘)’ - | Refinement - | SimpleType1 TypeArgs - | SimpleType1 ‘#’ id -Singleton ::= SimpleRef - | SimpleLiteral - | Singleton ‘.’ id - -FunArgType ::= Type - | ‘=>’ Type -FunArgTypes ::= FunArgType { ‘,’ FunArgType } -ParamType ::= [‘=>’] ParamValueType -ParamValueType ::= Type [‘*’] -TypeArgs ::= ‘[’ Types ‘]’ -Refinement ::= :<<< [RefineDcl] {semi [RefineDcl]} >>> -TypeBounds ::= [‘>:’ Type] [‘<:’ Type] -TypeParamBounds ::= TypeBounds {‘:’ Type} -Types ::= Type {‘,’ Type} -``` - -### Expressions -``` -Expr ::= FunParams (‘=>’ | ‘?=>’) Expr - | HkTypeParamClause ‘=>’ Expr - | Expr1 -BlockResult ::= FunParams (‘=>’ | ‘?=>’) Block - | HkTypeParamClause ‘=>’ Block - | Expr1 -FunParams ::= Bindings - | id - | ‘_’ -Expr1 ::= [‘inline’] ‘if’ ‘(’ Expr ‘)’ {nl} Expr [[semi] ‘else’ Expr] - | [‘inline’] ‘if’ Expr ‘then’ Expr [[semi] ‘else’ Expr] - | ‘while’ ‘(’ Expr ‘)’ {nl} Expr - | ‘while’ Expr ‘do’ Expr - | ‘try’ Expr Catches [‘finally’ Expr] - | ‘try’ Expr [‘finally’ Expr] - | ‘throw’ Expr - | ‘return’ [Expr] - | ForExpr - | [SimpleExpr ‘.’] id ‘=’ Expr - | PrefixOperator SimpleExpr ‘=’ Expr - | SimpleExpr ArgumentExprs ‘=’ Expr - | PostfixExpr [Ascription] - | ‘inline’ InfixExpr MatchClause -Ascription ::= ‘:’ InfixType - | ‘:’ Annotation {Annotation} -Catches ::= ‘catch’ (Expr | ExprCaseClause) -PostfixExpr ::= InfixExpr [id] -- only if language.postfixOperators is enabled -InfixExpr ::= PrefixExpr - | InfixExpr id [nl] InfixExpr - | InfixExpr id ColonArgument - | InfixExpr MatchClause -MatchClause ::= ‘match’ <<< CaseClauses >>> -PrefixExpr ::= [PrefixOperator] SimpleExpr -PrefixOperator ::= ‘-’ | ‘+’ | ‘~’ | ‘!’ -- unless backquoted -SimpleExpr ::= SimpleRef - | Literal - | ‘_’ - | BlockExpr - | ExprSplice - | Quoted - | quoteId -- only inside splices - | ‘new’ ConstrApp {‘with’ ConstrApp} [TemplateBody] - | ‘new’ TemplateBody - | ‘(’ ExprsInParens ‘)’ - | SimpleExpr ‘.’ id - | SimpleExpr ‘.’ MatchClause - | SimpleExpr TypeArgs - | SimpleExpr ArgumentExprs - | SimpleExpr ColonArgument -ColonArgument ::= colon [LambdaStart] - indent (CaseClauses | Block) outdent -LambdaStart ::= FunParams (‘=>’ | ‘?=>’) - | HkTypeParamClause ‘=>’ -Quoted ::= ‘'’ ‘{’ Block ‘}’ - | ‘'’ ‘[’ Type ‘]’ -ExprSplice ::= spliceId -- if inside quoted block - | ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern - | ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern -ExprsInParens ::= ExprInParens {‘,’ ExprInParens} -ExprInParens ::= PostfixExpr ‘:’ Type - | Expr -ParArgumentExprs ::= ‘(’ [ExprsInParens] ‘)’ - | ‘(’ ‘using’ ExprsInParens ‘)’ - | ‘(’ [ExprsInParens ‘,’] PostfixExpr ‘*’ ‘)’ -ArgumentExprs ::= ParArgumentExprs - | BlockExpr -BlockExpr ::= <<< (CaseClauses | Block) >>> -Block ::= {BlockStat semi} [BlockResult] -BlockStat ::= Import - | {Annotation {nl}} {LocalModifier} Def - | Extension - | Expr1 - | EndMarker - -ForExpr ::= ‘for’ ‘(’ Enumerators0 ‘)’ {nl} [‘do‘ | ‘yield’] Expr - | ‘for’ ‘{’ Enumerators0 ‘}’ {nl} [‘do‘ | ‘yield’] Expr - | ‘for’ Enumerators0 (‘do‘ | ‘yield’) Expr -Enumerators0 ::= {nl} Enumerators [semi] -Enumerators ::= Generator {semi Enumerator | Guard} -Enumerator ::= Generator - | Guard {Guard} - | Pattern1 ‘=’ Expr -Generator ::= [‘case’] Pattern1 ‘<-’ Expr -Guard ::= ‘if’ PostfixExpr - -CaseClauses ::= CaseClause { CaseClause } -CaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Block -ExprCaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Expr -TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } -TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] - -Pattern ::= Pattern1 { ‘|’ Pattern1 } -Pattern1 ::= PatVar ‘:’ RefinedType - | [‘-’] integerLiteral ‘:’ RefinedType - | [‘-’] floatingPointLiteral ‘:’ RefinedType - | Pattern2 -Pattern2 ::= [id ‘@’] InfixPattern [‘*’] -InfixPattern ::= SimplePattern { id [nl] SimplePattern } -SimplePattern ::= PatVar - | Literal - | ‘(’ [Patterns] ‘)’ - | Quoted - | SimplePattern1 [TypeArgs] [ArgumentPatterns] - | ‘given’ RefinedType -SimplePattern1 ::= SimpleRef - | SimplePattern1 ‘.’ id -PatVar ::= varid - | ‘_’ -Patterns ::= Pattern {‘,’ Pattern} -ArgumentPatterns ::= ‘(’ [Patterns] ‘)’ - | ‘(’ [Patterns ‘,’] PatVar ‘*’ ‘)’ -``` - -### Type and Value Parameters -``` -ClsTypeParamClause::= ‘[’ ClsTypeParam {‘,’ ClsTypeParam} ‘]’ -ClsTypeParam ::= {Annotation} [‘+’ | ‘-’] id [HkTypeParamClause] TypeParamBounds - -TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ -TypTypeParam ::= {Annotation} id [HkTypeParamClause] TypeBounds - -HkTypeParamClause ::= ‘[’ HkTypeParam {‘,’ HkTypeParam} ‘]’ -HkTypeParam ::= {Annotation} [‘+’ | ‘-’] (id [HkTypeParamClause] | ‘_’) TypeBounds - -ClsParamClauses ::= {ClsParamClause} [[nl] ‘(’ [‘implicit’] ClsParams ‘)’] -ClsParamClause ::= [nl] ‘(’ ClsParams ‘)’ - | [nl] ‘(’ ‘using’ (ClsParams | FunArgTypes) ‘)’ -ClsParams ::= ClsParam {‘,’ ClsParam} -ClsParam ::= {Annotation} [{Modifier} (‘val’ | ‘var’) | ‘inline’] Param - -TypelessClauses ::= TypelessClause {TypelessClause} -TypelessClause ::= DefTermParamClause - | UsingParamClause - -DefTypeParamClause::= [nl] ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ -DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds -DefTermParamClause::= [nl] ‘(’ [DefTermParams] ‘)’ -UsingParamClause ::= [nl] ‘(’ ‘using’ (DefTermParams | FunArgTypes) ‘)’ -DefImplicitClause ::= [nl] ‘(’ ‘implicit’ DefTermParams ‘)’ - -DefTermParams ::= DefTermParam {‘,’ DefTermParam} -DefTermParam ::= {Annotation} [‘inline’] Param -Param ::= id ‘:’ ParamType [‘=’ Expr] -``` - -### Bindings and Imports -``` -Bindings ::= ‘(’ [Binding {‘,’ Binding}] ‘)’ -Binding ::= (id | ‘_’) [‘:’ Type] - -Modifier ::= LocalModifier - | AccessModifier - | ‘override’ - | ‘opaque’ -LocalModifier ::= ‘abstract’ - | ‘final’ - | ‘sealed’ - | ‘open’ - | ‘implicit’ - | ‘lazy’ - | ‘inline’ -AccessModifier ::= (‘private’ | ‘protected’) [AccessQualifier] -AccessQualifier ::= ‘[’ id ‘]’ - -Annotation ::= ‘@’ SimpleType1 {ParArgumentExprs} - -Import ::= ‘import’ ImportExpr {‘,’ ImportExpr} -Export ::= ‘export’ ImportExpr {‘,’ ImportExpr} -ImportExpr ::= SimpleRef {‘.’ id} ‘.’ ImportSpec - | SimpleRef ‘as’ id -ImportSpec ::= NamedSelector - | WildcardSelector - | ‘{’ ImportSelectors) ‘}’ -NamedSelector ::= id [‘as’ (id | ‘_’)] -WildCardSelector ::= ‘*' | ‘given’ [InfixType] -ImportSelectors ::= NamedSelector [‘,’ ImportSelectors] - | WildCardSelector {‘,’ WildCardSelector} - -EndMarker ::= ‘end’ EndMarkerTag -- when followed by EOL -EndMarkerTag ::= id | ‘if’ | ‘while’ | ‘for’ | ‘match’ | ‘try’ - | ‘new’ | ‘this’ | ‘given’ | ‘extension’ | ‘val’ -``` - -### Declarations and Definitions -``` -RefineDcl ::= ‘val’ ValDcl - | ‘def’ DefDcl - | ‘type’ {nl} TypeDcl -Dcl ::= RefineDcl - | ‘var’ VarDcl -ValDcl ::= ids ‘:’ Type -VarDcl ::= ids ‘:’ Type -DefDcl ::= DefSig ‘:’ Type -DefSig ::= id [DefTypeParamClause] [TypelessClauses] [DefImplicitClause] -TypeDcl ::= id [TypeParamClause] {FunParamClause} TypeBounds - -Def ::= ‘val’ PatDef - | ‘var’ PatDef - | ‘def’ DefDef - | ‘type’ {nl} TypeDcl - | TmplDef -PatDef ::= ids [‘:’ Type] ‘=’ Expr - | Pattern2 [‘:’ Type] ‘=’ Expr -DefDef ::= DefSig [‘:’ Type] ‘=’ Expr - | ‘this’ TypelessClauses [DefImplicitClause] ‘=’ ConstrExpr - -TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef - | [‘case’] ‘object’ ObjectDef - | ‘enum’ EnumDef - | ‘given’ GivenDef -ClassDef ::= id ClassConstr [Template] -ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses -ConstrMods ::= {Annotation} [AccessModifier] -ObjectDef ::= id [Template] -EnumDef ::= id ClassConstr InheritClauses EnumBody -GivenDef ::= [GivenSig] (AnnotType [‘=’ Expr] | StructuralInstance) -GivenSig ::= [id] [DefTypeParamClause] {UsingParamClause} ‘:’ -- one of `id`, `DefTypeParamClause`, `UsingParamClause` must be present -StructuralInstance ::= ConstrApp {‘with’ ConstrApp} [‘with’ WithTemplateBody] -Extension ::= ‘extension’ [DefTypeParamClause] {UsingParamClause} - ‘(’ DefTermParam ‘)’ {UsingParamClause} ExtMethods -ExtMethods ::= ExtMethod | [nl] <<< ExtMethod {semi ExtMethod} >>> -ExtMethod ::= {Annotation [nl]} {Modifier} ‘def’ DefDef - | Export -Template ::= InheritClauses [TemplateBody] -InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] -ConstrApps ::= ConstrApp ({‘,’ ConstrApp} | {‘with’ ConstrApp}) -ConstrApp ::= SimpleType1 {Annotation} {ParArgumentExprs} -ConstrExpr ::= SelfInvocation - | <<< SelfInvocation {semi BlockStat} >>> -SelfInvocation ::= ‘this’ ArgumentExprs {ArgumentExprs} - -WithTemplateBody ::= <<< [SelfType] TemplateStat {semi TemplateStat} >>> -TemplateBody ::= :<<< [SelfType] TemplateStat {semi TemplateStat} >>> -TemplateStat ::= Import - | Export - | {Annotation [nl]} {Modifier} Def - | {Annotation [nl]} {Modifier} Dcl - | Extension - | Expr1 - | EndMarker - | -SelfType ::= id [‘:’ InfixType] ‘=>’ - | ‘this’ ‘:’ InfixType ‘=>’ - -EnumBody ::= :<<< [SelfType] EnumStat {semi EnumStat} >>> -EnumStat ::= TemplateStat - | {Annotation [nl]} {Modifier} EnumCase -EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps]] | ids) - -TopStats ::= TopStat {semi TopStat} -TopStat ::= Import - | Export - | {Annotation [nl]} {Modifier} Def - | Extension - | Packaging - | PackageObject - | EndMarker - | -Packaging ::= ‘package’ QualId :<<< TopStats >>> -PackageObject ::= ‘package’ ‘object’ ObjectDef - -CompilationUnit ::= {‘package’ QualId semi} TopStats -``` From d9dfc2174d7f53900d5719e64063ff0b46e5d2dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Fri, 28 Apr 2023 14:10:55 +0200 Subject: [PATCH 471/657] Apply kind-polymorphism.md. --- docs/_spec/03-types.md | 51 +++++++++++++++++-- .../other-new-features/kind-polymorphism.md | 6 --- 2 files changed, 47 insertions(+), 10 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/other-new-features/kind-polymorphism.md (85%) diff --git a/docs/_spec/03-types.md b/docs/_spec/03-types.md index c1cdb110bda8..27b04308138c 100644 --- a/docs/_spec/03-types.md +++ b/docs/_spec/03-types.md @@ -26,15 +26,16 @@ chapter: 3 ``` We distinguish between proper types and type constructors, which take type parameters and yield types. +All types have a _kind_, either the kind of proper types or a _higher kind_. A subset of proper types called _value types_ represents sets of (first-class) values. -Value types are either _concrete_ or _abstract_. +Types are either _concrete_ or _abstract_. Every concrete value type can be represented as a _class type_, i.e. a [type designator](#type-designators) that refers to a [class or a trait](05-classes-and-objects.html#class-definitions) [^1], or as a [compound type](#compound-types) representing an intersection of types, possibly with a [refinement](#compound-types) that further constrains the types of its members. -Abstract value types are introduced by [type parameters](04-basic-declarations-and-definitions.html#type-parameters) and [abstract type bindings](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases). +Abstract types are introduced by [type parameters](04-basic-declarations-and-definitions.html#type-parameters) and [abstract type bindings](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases). Parentheses in types can be used for grouping. [^1]: We assume that objects and packages also implicitly @@ -49,6 +50,10 @@ Non-value types are expressed indirectly in Scala. E.g., a method type is described by writing down a method signature, which in itself is not a real type, although it gives rise to a corresponding [method type](#method-types). Type constructors are another example, as one can write `type Swap[m[_, _], a,b] = m[b, a]`, but there is no syntax to write the corresponding anonymous type function directly. +`AnyKind` is the super type of all types in the Scala type system. +It has all possible kinds to encode [kind polymorphism](#kind-polymorphism). +As such, it is neither a value type nor a type constructor. + ## Paths ```ebnf @@ -502,6 +507,42 @@ val f = 0 define a function `f} which has type `(x: T)T ´\overload´ Int`. --> +## Kind Polymorphism + +Type parameters are normally partitioned into _kinds_, indicated by the top type of which it is a subtype. +Proper types are the types of values and are subtypes of `Any`. +Higher-kinded types are type constructors such as `List` or `Map`. +Covariant single argument type constructors such as `List` are subtypes of `[+X] =>> Any`. +The `Map` type constructor is a subtype of `[X, +Y] =>> Any`. + +A type can be used only as prescribed by its kind. +Subtypes of `Any` cannot be applied to type arguments whereas subtypes of `[X] =>> Any` _must_ be applied to a type argument, unless they are passed to type parameters of the same kind. + +A type parameter whose upper bound is [`scala.AnyKind`](https://scala-lang.org/api/3.x/scala/AnyKind.html) can have any kind and is called an _any-kinded type_. + +```scala +def f[T <: AnyKind] = ... +``` + +The actual type arguments of `f` can then be types of arbitrary kinds. +So the following are all legal: + +```scala +f[Int] +f[List] +f[Map] +f[[X] =>> String] +``` + +Since the actual kind of an any-kinded type is unknown, its usage is heavily restricted. +An any-kinded type can neither be the type of a value, nor be instantiated with type parameters. +The only thing one can do with an any-kinded type is to pass it to another any-kinded type argument. + +`AnyKind` plays a special role in Scala's subtype system. +It is a supertype of all other types, no matter what their kind is. +It is also assumed to be kind-compatible with all other types. +Furthermore, `AnyKind` is itself an any-kinded type, so it cannot be the type of values and it cannot be instantiated. + ## Base Types and Member Definitions Types of class members depend on the way the members are referenced. @@ -588,8 +629,9 @@ Equivalence ´(\equiv)´ between types is the smallest congruence [^congruence] The conformance relation ´(<:)´ is the smallest transitive relation that satisfies the following conditions. - Conformance includes equivalence. If ´T \equiv U´ then ´T <: U´. -- For every value type `T`, `scala.Nothing <: ´T´ <: scala.Any`. -- For every type constructor ´T´ (with any number of type parameters), `scala.Nothing <: ´T´ <: scala.Any`. +- For every type `´T´` (of any kind), `scala.Nothing <: ´T´ <: scala.AnyKind`. +- For every value type `´T´`, `´T´ <: scala.Any`. +- For every type constructor `´T´` with type parameters `[´U_1´, ..., ´U_n´]`, `[´U_1´, ..., ´U_n´] =>> scala.Nothing <: ´T´ <: [´U_1´, ..., ´U_n´] =>> scala.Any`. - For every value type ´T´, `scala.Null <: ´T´` unless `´T´ <: scala.AnyVal`. - A type variable or abstract type ´t´ conforms to its upper bound and its lower bound conforms to ´t´. - A class type or parameterized type conforms to any of its base-types. @@ -716,6 +758,7 @@ _Type erasure_ is a mapping from (possibly generic) types to non-generic types. We write ´|T|´ for the erasure of type ´T´. The erasure mapping is defined as follows. +- The erasure of `scala.AnyKind` is `Object`. - The erasure of an alias type is the erasure of its right-hand side. - The erasure of an abstract type is the erasure of its upper bound. - The erasure of the parameterized type `scala.Array´[T_1]´` is `scala.Array´[|T_1|]´`. diff --git a/docs/_spec/TODOreference/other-new-features/kind-polymorphism.md b/docs/_spec/APPLIEDreference/other-new-features/kind-polymorphism.md similarity index 85% rename from docs/_spec/TODOreference/other-new-features/kind-polymorphism.md rename to docs/_spec/APPLIEDreference/other-new-features/kind-polymorphism.md index 8f0172c4c04b..685630b86f73 100644 --- a/docs/_spec/TODOreference/other-new-features/kind-polymorphism.md +++ b/docs/_spec/APPLIEDreference/other-new-features/kind-polymorphism.md @@ -38,10 +38,4 @@ through advanced uses of implicits. (todo: insert good concise example) -Some technical details: [`AnyKind`](https://scala-lang.org/api/3.x/scala/AnyKind.html) is a synthesized class just like `Any`, but without any members. It extends no other class. -It is declared `abstract` and `final`, so it can be neither instantiated nor extended. - `AnyKind` plays a special role in Scala's subtype system: It is a supertype of all other types no matter what their kind is. It is also assumed to be kind-compatible with all other types. Furthermore, `AnyKind` is treated as a higher-kinded type (so it cannot be used as a type of values), but at the same time it has no type parameters (so it cannot be instantiated). - -**Note**: This feature is considered experimental but stable and it can be disabled under compiler flag -(i.e. `-Yno-kind-polymorphism`). From 5913c89b5c9feaf4370526a7258d865c9b429325 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Fri, 28 Apr 2023 09:53:08 +0200 Subject: [PATCH 472/657] Apply `auto-apply` to the Spec - Move `auto-apply.md` from TODOreference to APPLIEDreference with minor changes - Remove the related content from the specification - Also move the index file `dropped-features.md` --- docs/_spec/05-classes-and-objects.md | 4 ++-- .../dropped-features/auto-apply.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/dropped-features/auto-apply.md (98%) diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index f02aa5368b30..5de7048ba00e 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -186,7 +186,7 @@ A member definition ´M´ _matches_ a member definition ´M'´, if ´M´ and ´M 1. Neither ´M´ nor ´M'´ is a method definition. 2. ´M´ and ´M'´ define both monomorphic methods with equivalent argument types. -3. ´M´ defines a parameterless method and ´M'´ defines a method with an empty parameter list `()` or _vice versa_. +3. ´M´ is defined in Java and defines a method with an empty parameter list `()` and ´M'´ defines a parameterless method. 4. ´M´ and ´M'´ define both polymorphic methods with equal number of argument types ´\overline T´, ´\overline T'´ and equal numbers of type parameters ´\overline t´, ´\overline t'´, say, and ´\overline T' = [\overline t'/\overline t]\overline T´. -Consider this fragment of the `Iterable[+X]` class: +A _type constructor_ is either: +- a _type lambda_, of the form `[´\mathit{tps}\,´] =>> ´T´` where `[´\mathit{tps}\,´]` is a type parameter clause `[´a_1´ >: ´L_1´ <: ´U_1, ..., a_n´ >: ´L_n´ <: ´U_n´]` for some ´n \gt 0´ and ´T´ is either a value type +or another type lambda. +- a reference to a [desugared type declaration](04-basic-declarations-and-definitions.html#type-declarations-and-type-aliases) upper-bounded by a type lambda. +- a reference to a [polymorphic class](05-classes-and-objects.html##class-definitions). -```scala -trait Iterable[+X] { - def flatMap[newType[+X] <: Iterable[X], S](f: X => newType[S]): newType[S] -} -``` +Each type parameter ´a_i´ of a type lambda has a variance ´v_i´ which cannot be written down by the user but is inferred from the body of the type lambda to maximize the number of types that conform to the type lambda. + -Conceptually, the type constructor `Iterable` is a name for the anonymous type `[+X] Iterable[X]`, which may be passed to the `newType` type constructor parameter in `flatMap`. +#### Inferred type parameter clause + +To each type constructor corresponds an _inferred type parameter clause_ which is computed as follow: +- For a type lambda, its type parameter clause (including variance annotations). +- For a type declaration upper-bounded by a type lambda ´T´, the inferred clause of ´T´. +- For a polymorphic class, its type parameter clause. `x` + ref(termRef).withSpan(tree.span) + case _ => + transformQuoteBody(body, tree.span) match + case DirectTypeOf.Healed(termRef) => + // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` + ref(termRef).withSpan(tree.span) + case transformedBody => + val quotes = transform(tree.args.head) + // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` + val TypeApply(fun, _) = tree.fun: @unchecked + if level != 0 then cpy.Apply(tree)(cpy.TypeApply(tree.fun)(fun, transformedBody :: Nil), quotes :: Nil) + else tpd.Quote(transformedBody).select(nme.apply).appliedTo(quotes).withSpan(tree.span) + case _ if !inQuoteOrSpliceScope => - checkAnnotations(tree) + checkAnnotations(tree) // Check quotes in annotations super.transform(tree) + case _: TypeTree => val tp1 = transformTypeAnnotationSplices(tree.tpe) val healedType = healType(tree.srcPos)(tp1) @@ -106,37 +140,6 @@ class CrossStageSafety extends TreeMapWithStages { super.transform(tree) end transform - /** Transform quoted trees while maintaining level correctness */ - private def transformQuote(body: Tree, quote: Quote)(using Context): Tree = { - if (ctx.property(InAnnotation).isDefined) - report.error("Cannot have a quote in an annotation", quote.srcPos) - val transformedBody = transformQuoteBody(body, quote.span) - val stripAnnotationsDeep: TypeMap = new TypeMap: - def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val bodyType1 = healType(quote.srcPos)(stripAnnotationsDeep(quote.bodyType)) - cpy.Quote(quote)(transformedBody).withBodyType(bodyType1) - } - - private def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = { - if (ctx.property(InAnnotation).isDefined) - report.error("Cannot have a quote in an annotation", quote.srcPos) - body.tpe match - case DirectTypeOf(termRef) => - // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` - ref(termRef).withSpan(quote.span) - case _ => - transformQuoteBody(body, quote.span) match - case DirectTypeOf.Healed(termRef) => - // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` - ref(termRef).withSpan(quote.span) - case transformedBody => - val quotes = transform(quote.args.head) - // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` - val TypeApply(fun, _) = quote.fun: @unchecked - if level != 0 then cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes :: Nil) - else tpd.Quote(transformedBody).select(nme.apply).appliedTo(quotes).withSpan(quote.span) - } - private def transformQuoteBody(body: Tree, span: Span)(using Context): Tree = { val taggedTypes = new QuoteTypeTags(span) val contextWithQuote = @@ -148,19 +151,6 @@ class CrossStageSafety extends TreeMapWithStages { case tags => tpd.Block(tags, transformedBody).withSpan(body.span) } - /** Transform splice - * - If inside a quote, transform the contents of the splice. - * - If inside inlined code, expand the macro code. - * - If inside of a macro definition, check the validity of the macro. - */ - private def transformSplice(body: Tree, splice: Splice)(using Context): Tree = { - val body1 = transform(body)(using spliceContext) - val tpe1 = - if level == 0 then splice.tpe - else healType(splice.srcPos)(splice.tpe.widenTermRefExpr) - untpd.cpy.Splice(splice)(body1).withType(tpe1) - } - def transformTypeAnnotationSplices(tp: Type)(using Context) = new TypeMap { def apply(tp: Type): Type = tp match case tp: AnnotatedType => From 6956c43356010d6d6a144713ee0737bd0baac7c9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 13:27:00 +0200 Subject: [PATCH 483/657] Refactor quote cancellation logic --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 21 -------- .../tools/dotc/staging/CrossStageSafety.scala | 52 ++++++++++++------- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index fbc68ebe63d7..19c189047c6d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -714,17 +714,6 @@ object Trees { defn.FunctionType(1, isContextual = true) .appliedTo(defn.QuotesClass.typeRef, exprType) withType(quoteType) - - /** Cancel this Quote if it contains a Splice */ - def cancelled(using Context): Option[Tree[T]] = - def rec(tree: Tree[T]): Option[Tree[T]] = tree match - case Block(Nil, expr) => rec(expr) - case Splice(inner) => - // Optimization: `'{ $x }` --> `x` - // and adapt the refinement of `Quotes { type reflect: ... } ?=> Expr[T]` - Some(inner) - case _ => None - rec(body) } /** A tree representing a splice `${ expr }` @@ -741,16 +730,6 @@ object Trees { case class Splice[+T <: Untyped] private[ast] (expr: Tree[T])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Splice[T] - - /** Cancel this Splice if it contains a Quote */ - def cancelled(using Context): Option[Tree[T]] = - def rec(tree: Tree[T]): Option[Tree[T]] = tree match - case Block(Nil, expr1) => rec(expr1) - case Quote(body) => - // Optimization: `${ 'x }` --> `x` - Some(body) - case _ => None - rec(expr) } /** A type tree that represents an existing or inferred type */ diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index cceb60719289..032067eca482 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -51,29 +51,25 @@ class CrossStageSafety extends TreeMapWithStages { if (tree.source != ctx.source && tree.source.exists) transform(tree)(using ctx.withSource(tree.source)) else tree match + case CancelledQuote(tree) => + transform(tree) // Optimization: `'{ $x }` --> `x` case tree: Quote => - tree.cancelled match - case Some(tree1) => - transform(tree1) - case None => - if (ctx.property(InAnnotation).isDefined) - report.error("Cannot have a quote in an annotation", tree.srcPos) - val body1 = transformQuoteBody(tree.body, tree.span) - val stripAnnotationsDeep: TypeMap = new TypeMap: - def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val bodyType1 = healType(tree.srcPos)(stripAnnotationsDeep(tree.bodyType)) - cpy.Quote(tree)(body1).withBodyType(bodyType1) + if (ctx.property(InAnnotation).isDefined) + report.error("Cannot have a quote in an annotation", tree.srcPos) + val body1 = transformQuoteBody(tree.body, tree.span) + val stripAnnotationsDeep: TypeMap = new TypeMap: + def apply(tp: Type): Type = mapOver(tp.stripAnnots) + val bodyType1 = healType(tree.srcPos)(stripAnnotationsDeep(tree.bodyType)) + cpy.Quote(tree)(body1).withBodyType(bodyType1) + case CancelledSplice(tree) => + transform(tree) // Optimization: `${ 'x }` --> `x` case tree: Splice => - tree.cancelled match - case Some(tree1) => - transform(tree1) - case None => - val body1 = transform(tree.expr)(using spliceContext) - val tpe1 = - if level == 0 then tree.tpe - else healType(tree.srcPos)(tree.tpe.widenTermRefExpr) - untpd.cpy.Splice(tree)(body1).withType(tpe1) + val body1 = transform(tree.expr)(using spliceContext) + val tpe1 = + if level == 0 then tree.tpe + else healType(tree.srcPos)(tree.tpe.widenTermRefExpr) + untpd.cpy.Splice(tree)(body1).withType(tpe1) case tree @ QuotedTypeOf(body) => if (ctx.property(InAnnotation).isDefined) @@ -224,4 +220,20 @@ class CrossStageSafety extends TreeMapWithStages { | - but the access is at level $level.$hint""", pos) tp } + + private object CancelledQuote: + def unapply(tree: Quote): Option[Tree] = + def rec(tree: Tree): Option[Tree] = tree match + case Block(Nil, expr) => rec(expr) + case Splice(inner) => Some(inner) + case _ => None + rec(tree.body) + + private object CancelledSplice: + def unapply(tree: Splice): Option[Tree] = + def rec(tree: Tree): Option[Tree] = tree match + case Block(Nil, expr) => rec(expr) + case Quote(inner) => Some(inner) + case _ => None + rec(tree.expr) } From 859f6e6bff9d0b113909507ec4c7acb2b75aee42 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 14:18:47 +0200 Subject: [PATCH 484/657] Remove unnecessary quotation level change --- .../src/dotty/tools/dotc/inlines/PrepareInlineable.scala | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala index ed8683bf210e..060c8d21f390 100644 --- a/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala @@ -86,13 +86,7 @@ object PrepareInlineable { } override def transform(tree: Tree)(using Context): Tree = - inContext(stagingContext(tree)) { - postTransform(super.transform(preTransform(tree))) - } - - private def stagingContext(tree: Tree)(using Context): Context = tree match - case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext - case _ => ctx + postTransform(super.transform(preTransform(tree))) } /** Direct approach: place the accessor with the accessed symbol. This has the From e4ddc88ba974117e3c95466e40d3ba2414705967 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 14:21:55 +0200 Subject: [PATCH 485/657] Add missing change of staging level --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 19c189047c6d..77583a9a77e2 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1705,9 +1705,9 @@ object Trees { case Thicket(ts) => this(x, ts) case Quote(body) => - this(x, body) + this(x, body)(using quoteContext) case Splice(expr) => - this(x, expr) + this(x, expr)(using spliceContext) case Hole(_, _, args, content, tpt) => this(this(this(x, args), content), tpt) case _ => From 7e8d2b106f5f852c13b2fc8d1ffc89e10b0a426d Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 14:42:14 +0200 Subject: [PATCH 486/657] Simplify macroDependencies using level from context --- .../dotty/tools/dotc/inlines/Inliner.scala | 38 ++++--------------- 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 9179e92527e6..ccef777380e1 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -23,7 +23,7 @@ import util.Spans.Span import dotty.tools.dotc.transform.Splicer import dotty.tools.dotc.transform.BetaReduce import quoted.QuoteUtils -import staging.StagingLevel +import staging.StagingLevel.{level, spliceContext} import scala.annotation.constructorOnly /** General support for inlining */ @@ -834,9 +834,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = super.typedSplice(tree, pt) match - case tree1 @ Splice(expr) - if StagingLevel.level == 0 - && !hasInliningErrors => + case tree1 @ Splice(expr) if level == 0 && !hasInliningErrors => val expanded = expandMacro(expr, tree1.srcPos) transform.TreeChecker.checkMacroGeneratedTree(tree1, expanded) typedExpr(expanded) // Inline calls and constant fold code generated by the macro @@ -1032,9 +1030,9 @@ class Inliner(val call: tpd.Tree)(using Context): } private def expandMacro(body: Tree, splicePos: SrcPos)(using Context) = { - assert(StagingLevel.level == 0) + assert(level == 0) val inlinedFrom = enclosingInlineds.last - val dependencies = macroDependencies(body) + val dependencies = macroDependencies(body)(using spliceContext) val suspendable = ctx.compilationUnit.isSuspendable if dependencies.nonEmpty && !ctx.reporter.errorsReported then for sym <- dependencies do @@ -1064,32 +1062,12 @@ class Inliner(val call: tpd.Tree)(using Context): */ private def macroDependencies(tree: Tree)(using Context) = new TreeAccumulator[List[Symbol]] { - private var level = -1 override def apply(syms: List[Symbol], tree: tpd.Tree)(using Context): List[Symbol] = - if level != -1 then foldOver(syms, tree) - else tree match { - case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => + tree match { + case tree: RefTree if tree.isTerm && level == -1 && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal => foldOver(tree.symbol :: syms, tree) - case Quote(body) => - level += 1 - try apply(syms, body) - finally level -= 1 - case QuotedTypeOf(body) => - level += 1 - try apply(syms, body) - finally level -= 1 - case Splice(body) => - level -= 1 - try apply(syms, body) - finally level += 1 - case SplicedType(body) => - level -= 1 - try apply(syms, body) - finally level += 1 - case _: TypTree => - syms - case _ => - foldOver(syms, tree) + case _: TypTree => syms + case _ => foldOver(syms, tree) } }.apply(Nil, tree) end Inliner From a3c657ba8c86960caf866020443a743f6410b032 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 14:44:01 +0200 Subject: [PATCH 487/657] Fix typo --- compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 87620db36482..924f2e9ffa92 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -669,7 +669,7 @@ class TreePickler(pickler: TastyPickler) { // Add QUOTE tag to TASTy assert(body.isTerm, """Quote with type should not be pickled. - |Quote with type should only exists after staging phase at level staging level 0.""".stripMargin) + |Quote with type should only exists after staging phase at staging level 0.""".stripMargin) pickleTree( // scala.quoted.runtime.Expr.quoted[]() ref(defn.QuotedRuntime_exprQuote) From be3cd481c160336ebfaa9bc2b6e8ebd28a2e2142 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 14:58:08 +0200 Subject: [PATCH 488/657] Refactor use of Quote --- .../tools/dotc/transform/PickleQuotes.scala | 22 +++++++++++-------- .../tools/dotc/typer/QuotesAndSplices.scala | 2 +- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index b1a707a70cb1..75af32af247e 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -104,9 +104,10 @@ class PickleQuotes extends MacroTransform { val (contents, codeWithHoles) = makeHoles(quote.body) val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) val bodyWithHoles2 = - if quote.body.isType then codeWithHoles + if quote.isTypeQuote then codeWithHoles else Inlined(sourceRef, Nil, codeWithHoles) - val pickled = PickleQuotes.pickle(quotes, bodyWithHoles2, contents, quote.bodyType) + val quote1 = cpy.Quote(quote)(body = bodyWithHoles2) + val pickled = PickleQuotes.pickle(quote1, quotes, contents) transform(pickled) // pickle quotes that are in the contents case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod => // Shrink size of the tree. The methods have already been inlined. @@ -202,7 +203,10 @@ object PickleQuotes { val name: String = "pickleQuotes" val description: String = "turn quoted trees into explicit run-time data structures" - def pickle(quotes: Tree, body: Tree, contents: List[Tree], originalTp: Type)(using Context) = { + def pickle(quote: Quote, quotes: Tree, contents: List[Tree])(using Context) = { + val body = quote.body + val bodyType = quote.bodyType + /** Helper methods to construct trees calling methods in `Quotes.reflect` based on the current `quotes` tree */ object reflect extends ReifiedReflect { val quotesTree = quotes @@ -256,7 +260,7 @@ object PickleQuotes { */ def liftedValue(lit: Literal, lifter: Symbol) = val exprType = defn.QuotedExprClass.typeRef.appliedTo(body.tpe) - ref(lifter).appliedToType(originalTp).select(nme.apply).appliedTo(lit).appliedTo(quotes) + ref(lifter).appliedToType(bodyType).select(nme.apply).appliedTo(lit).appliedTo(quotes) def pickleAsValue(lit: Literal) = { // TODO should all constants be pickled as Literals? @@ -334,18 +338,18 @@ object PickleQuotes { case _ => Match(args(0).annotated(New(ref(defn.UncheckedAnnot.typeRef))), cases) ) - val quoteClass = if body.isType then defn.QuotedTypeClass else defn.QuotedExprClass - val quotedType = quoteClass.typeRef.appliedTo(originalTp) + val quoteClass = if quote.isTypeQuote then defn.QuotedTypeClass else defn.QuotedExprClass + val quotedType = quoteClass.typeRef.appliedTo(bodyType) val lambdaTpe = MethodType(defn.QuotesClass.typeRef :: Nil, quotedType) val unpickleMeth = - if body.isType then defn.QuoteUnpickler_unpickleTypeV2 + if quote.isTypeQuote then defn.QuoteUnpickler_unpickleTypeV2 else defn.QuoteUnpickler_unpickleExprV2 val unpickleArgs = - if body.isType then List(pickledQuoteStrings, types) + if quote.isTypeQuote then List(pickledQuoteStrings, types) else List(pickledQuoteStrings, types, termHoles) quotes .asInstance(defn.QuoteUnpicklerClass.typeRef) - .select(unpickleMeth).appliedToType(originalTp) + .select(unpickleMeth).appliedToType(bodyType) .appliedToArgs(unpickleArgs).withSpan(body.span) } diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 5372dc4d04b6..3a5ea05726a5 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -50,7 +50,7 @@ trait QuotesAndSplices { if ctx.mode.is(Mode.Pattern) then typedQuotePattern(tree, pt, quotes).withSpan(tree.span) - else if tree.body.isType then + else if tree.isTypeQuote then val msg = em"""Quoted types `'[..]` can only be used in patterns. | |Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead. From 7bd7c921eb480a21ad5533d2966ee8b7a5dd736b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 16:36:09 +0200 Subject: [PATCH 489/657] Do not traverse type trees to find inline method calls --- compiler/src/dotty/tools/dotc/transform/Inlining.scala | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Inlining.scala b/compiler/src/dotty/tools/dotc/transform/Inlining.scala index f5b72759b57e..10f73fa94e08 100644 --- a/compiler/src/dotty/tools/dotc/transform/Inlining.scala +++ b/compiler/src/dotty/tools/dotc/transform/Inlining.scala @@ -45,8 +45,6 @@ class Inlining extends MacroTransform { new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match - case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => - traverseChildren(tree)(using StagingLevel.quoteContext) case tree: RefTree if !Inlines.inInlineMethod && StagingLevel.level == 0 => assert(!tree.symbol.isInlineMethod, tree.show) case _ => @@ -95,8 +93,6 @@ class Inlining extends MacroTransform { val tree1 = super.transform(tree) if tree1.tpe.isError then tree1 else Inlines.inlineCall(tree1) - case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of => - super.transform(tree)(using StagingLevel.quoteContext) case _: PackageDef => super.transform(tree) match case tree1: PackageDef => @@ -108,7 +104,8 @@ class Inlining extends MacroTransform { case _ => tree1 case tree1 => tree1 case _ => - super.transform(tree) + if tree.isType then tree + else super.transform(tree) } } } From b991b4066b481d0515441731ea6c1e87b55a77f3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 16:38:12 +0200 Subject: [PATCH 490/657] Remove post-condition that is already checked by Staging --- compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 75af32af247e..190d0c983a1c 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -87,8 +87,6 @@ class PickleQuotes extends MacroTransform { assert(Inlines.inInlineMethod) case tree: Splice => assert(Inlines.inInlineMethod) - case tree: RefTree if !Inlines.inInlineMethod => - assert(tree.symbol != defn.QuotedTypeModule_of) case _ : TypeDef if !Inlines.inInlineMethod => assert(!tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot), s"${tree.symbol} should have been removed by PickledQuotes because it has a @quoteTypeTag") From e611139bf865ce11fd7aa35868a5bf930ca06a01 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 16:52:46 +0200 Subject: [PATCH 491/657] Refactor quote pickling case --- .../tools/dotc/transform/PickleQuotes.scala | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 190d0c983a1c..0c3a94e7d9cd 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -99,12 +99,7 @@ class PickleQuotes extends MacroTransform { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match case Apply(Select(quote: Quote, nme.apply), List(quotes)) => - val (contents, codeWithHoles) = makeHoles(quote.body) - val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos) - val bodyWithHoles2 = - if quote.isTypeQuote then codeWithHoles - else Inlined(sourceRef, Nil, codeWithHoles) - val quote1 = cpy.Quote(quote)(body = bodyWithHoles2) + val (contents, quote1) = makeHoles(quote) val pickled = PickleQuotes.pickle(quote1, quotes, contents) transform(pickled) // pickle quotes that are in the contents case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod => @@ -115,8 +110,7 @@ class PickleQuotes extends MacroTransform { super.transform(tree) } - private def makeHoles(tree: tpd.Tree)(using Context): (List[Tree], tpd.Tree) = - + private def makeHoles(quote: tpd.Quote)(using Context): (List[Tree], tpd.Quote) = class HoleContentExtractor extends Transformer: private val contents = List.newBuilder[Tree] override def transform(tree: tpd.Tree)(using Context): tpd.Tree = @@ -187,10 +181,13 @@ class PickleQuotes extends MacroTransform { end HoleContentExtractor val holeMaker = new HoleContentExtractor - val newTree = holeMaker.transform(tree) - (holeMaker.getContents(), newTree) - + val body1 = holeMaker.transform(quote.body) + val body2 = + if quote.isTypeQuote then body1 + else Inlined(Inlines.inlineCallTrace(ctx.owner, quote.sourcePos), Nil, body1) + val quote1 = cpy.Quote(quote)(body2) + (holeMaker.getContents(), quote1) end makeHoles } From 37200bef72c019ea9e23e169ea8a06664f777d56 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 17:21:51 +0200 Subject: [PATCH 492/657] Cleanup --- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index c833a0800e87..5c333c8c22ea 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -228,7 +228,7 @@ class Splicing extends MacroTransform: else super.transform(tree) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) - case Apply(sel @ Select(app @ Quote(body), nme.apply), quotes :: Nil) if level == 0 && body.isTerm => + case Apply(Select(Quote(body), nme.apply), quotes :: Nil) if level == 0 && body.isTerm => body match case _: RefTree if isCaptured(body.symbol) => capturedTerm(body) case _ => withCurrentQuote(quotes) { super.transform(tree) } From e75cafe8b297157ea3f36fe9ce3576999b1f20e9 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 17:26:13 +0200 Subject: [PATCH 493/657] Remove unnecessary widening --- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 5c333c8c22ea..4b288349fc33 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -391,9 +391,7 @@ class Splicing extends MacroTransform: Splice(closure, tpe) private def quoted(expr: Tree)(using Context): Tree = - untpd.Quote(expr).withBodyType(expr.tpe.widenTermRefExpr) // TODO do we need widenTermRefExpr? - .select(nme.apply) - .appliedTo(quotes.nn) + tpd.Quote(expr).select(nme.apply).appliedTo(quotes.nn) /** Helper methods to construct trees calling methods in `Quotes.reflect` based on the current `quotes` tree */ private object reflect extends ReifiedReflect { From 9214daa630cd7450ee47a85b964beef1e893fb01 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 1 May 2023 21:04:46 +0200 Subject: [PATCH 494/657] Apply suggestions from code review Co-authored-by: Guillaume Martres --- compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 924f2e9ffa92..5662b17b6697 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -666,7 +666,7 @@ class TreePickler(pickler: TastyPickler) { pickleTree(alias) } case tree @ Quote(body) => - // Add QUOTE tag to TASTy + // TODO: Add QUOTE tag to TASTy assert(body.isTerm, """Quote with type should not be pickled. |Quote with type should only exists after staging phase at staging level 0.""".stripMargin) @@ -678,7 +678,7 @@ class TreePickler(pickler: TastyPickler) { .withSpan(tree.span) ) case Splice(expr) => - pickleTree( // Add SPLICE tag to TASTy + pickleTree( // TODO: Add SPLICE tag to TASTy // scala.quoted.runtime.Expr.splice[]() ref(defn.QuotedRuntime_exprSplice) .appliedToType(tree.tpe) From fdfa1e4b9154d6984147873b54c101806ade439b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 May 2023 09:14:43 +0200 Subject: [PATCH 495/657] Add regression tests Closes #13376 --- tests/neg-macros/i13376a.scala | 6 ++++++ tests/neg-macros/i13376b.scala | 6 ++++++ tests/pos-macros/i13376a.scala | 7 +++++++ tests/pos-macros/i13376b.scala | 7 +++++++ 4 files changed, 26 insertions(+) create mode 100644 tests/neg-macros/i13376a.scala create mode 100644 tests/neg-macros/i13376b.scala create mode 100644 tests/pos-macros/i13376a.scala create mode 100644 tests/pos-macros/i13376b.scala diff --git a/tests/neg-macros/i13376a.scala b/tests/neg-macros/i13376a.scala new file mode 100644 index 000000000000..563513eed232 --- /dev/null +++ b/tests/neg-macros/i13376a.scala @@ -0,0 +1,6 @@ +import scala.quoted.* +trait C: + type T + def foo: T +inline def makro(inline x: C): x.T = ${ impl[x.type]('x) } // error // error +def impl[CC <: C](xp: Expr[CC])(using Quotes): Expr[CC#T] = '{ $xp.foo } diff --git a/tests/neg-macros/i13376b.scala b/tests/neg-macros/i13376b.scala new file mode 100644 index 000000000000..8866c24102fd --- /dev/null +++ b/tests/neg-macros/i13376b.scala @@ -0,0 +1,6 @@ +import scala.quoted.* +trait C: + type T + def foo: T +inline def makro(x: C): x.T = ${ impl[x.type]('x) } +def impl[CC <: C](xp: Expr[CC])(using Quotes): Expr[CC#T] = '{ $xp.foo } // error diff --git a/tests/pos-macros/i13376a.scala b/tests/pos-macros/i13376a.scala new file mode 100644 index 000000000000..8e746d0e34a8 --- /dev/null +++ b/tests/pos-macros/i13376a.scala @@ -0,0 +1,7 @@ +import scala.quoted.* +trait C: + type T + def foo: T +inline def makro(x: C): x.T = ${ impl[x.T]('x) } +def impl[U: Type](xp: Expr[C { def foo: U }])(using Quotes): Expr[U] = + '{ $xp.foo } diff --git a/tests/pos-macros/i13376b.scala b/tests/pos-macros/i13376b.scala new file mode 100644 index 000000000000..8aa171ff07dd --- /dev/null +++ b/tests/pos-macros/i13376b.scala @@ -0,0 +1,7 @@ +import scala.quoted.* +trait C: + type T + def foo: T +inline def makro(inline x: C): C#T = ${ impl('x) } +def impl[U: Type](xp: Expr[C { def foo: U }])(using Quotes): Expr[U] = + '{ $xp.foo } From fb235681f5ff9b8fc96e3bc064d37b2ea61d94b1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 May 2023 09:22:03 +0200 Subject: [PATCH 496/657] Add regression test Fix #14123 --- tests/neg-macros/i14123a.scala | 4 ++++ tests/neg-macros/i14123b.scala | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/neg-macros/i14123a.scala create mode 100644 tests/neg-macros/i14123b.scala diff --git a/tests/neg-macros/i14123a.scala b/tests/neg-macros/i14123a.scala new file mode 100644 index 000000000000..29978f85102c --- /dev/null +++ b/tests/neg-macros/i14123a.scala @@ -0,0 +1,4 @@ +import scala.quoted._ + +def f(foo: Any => Any)(using Quotes): Expr[Any] = + '{ println(${ foo[Int]('{???}); ??? }) } // error diff --git a/tests/neg-macros/i14123b.scala b/tests/neg-macros/i14123b.scala new file mode 100644 index 000000000000..80cadf518766 --- /dev/null +++ b/tests/neg-macros/i14123b.scala @@ -0,0 +1,23 @@ +package x + +import scala.quoted._ + +object Impl { + + sealed trait UpdateOp[+T] + case class Assignment[T](value:Expr[T]) extends UpdateOp[T] + case class Update(operation:Expr[Unit]) extends UpdateOp[Nothing] + + def genRead[B:Type](newBuilder: Expr[B], + readVal: (Expr[B]) => UpdateOp[B] + )(using Quotes): Expr[B] = + '{ + var x = $newBuilder + ${readVal[B]('x) match { // error: method apply in trait Function1 does not take type parameters + case Assignment(value) => '{ x = $value } // error + case Update(operation) => operation // error + }} + x + } + +} From 4baffce739f7f6674a58539a435575636b25b549 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 May 2023 10:23:11 +0200 Subject: [PATCH 497/657] Add regression test Closes #9360 --- tests/pos-macros/i9360.scala | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/pos-macros/i9360.scala diff --git a/tests/pos-macros/i9360.scala b/tests/pos-macros/i9360.scala new file mode 100644 index 000000000000..699ef5f38bee --- /dev/null +++ b/tests/pos-macros/i9360.scala @@ -0,0 +1,22 @@ +package a + +import scala.quoted._ + +trait CPM[F[_]] + +def fun[M[_],T](t:T)(using m:CPM[M]):M[T] = ??? + +object M { + + inline def transform[F[_],T](t:T): F[T] = + ${ transformImpl[F,T]('t) } + + def transformImpl[F[_]:Type,T:Type](t:Expr[T])(using Quotes):Expr[F[T]] = { + import quotes.reflect._ + t match { + case '{ type mt[_]; a.fun[`mt`, tt]($t)(using $m) } => ??? + } + + } + +} From 3a20cd48ea17ff6cf4a41ca1f7bf2eabb2fbaaed Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 21 Dec 2022 11:51:04 +0100 Subject: [PATCH 498/657] Add reflect Flags.AbsOverride --- .../src/scala/quoted/runtime/impl/QuotesImpl.scala | 1 + library/src/scala/quoted/Quotes.scala | 12 ++++++++++++ .../stdlibExperimentalDefinitions.scala | 1 + 3 files changed, 14 insertions(+) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index cf967242a715..30be3120bc2e 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2800,6 +2800,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Flags extends FlagsModule: def Abstract: Flags = dotc.core.Flags.Abstract + def AbsOverride: Flags = dotc.core.Flags.AbsOverride def Artifact: Flags = dotc.core.Flags.Artifact def Case: Flags = dotc.core.Flags.Case def CaseAccessor: Flags = dotc.core.Flags.CaseAccessor diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 2ead4a8607a3..7cb681f66c51 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -4373,6 +4373,18 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Is this symbol `abstract` */ def Abstract: Flags + /** Is this symbol is labeled with of abstract & override + * + * The override modifier has an additional significance when combined with the abstract modifier. + * That modifier combination is only allowed for value members of traits. + * + * We call a member MM of a template incomplete if it is either abstract (i.e. defined by a declaration), or it is labeled abstract and override and every member overridden by MM is again incomplete. + * + * Note that the abstract override modifier combination does not influence the concept whether a member is concrete or abstract. + * A member is abstract if only a declaration is given for it; it is concrete if a full definition is given. + */ + @experimental def AbsOverride: Flags + /** Is this generated by Scala compiler. * Corresponds to ACC_SYNTHETIC in the JVM. */ diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index c278a87bfcde..644efb54c32e 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -65,6 +65,7 @@ val experimentalDefinitionInLibrary = Set( //// New APIs: Quotes // Should be stabilized in 3.4.0 "scala.quoted.Quotes.reflectModule.defnModule.FunctionClass", + "scala.quoted.Quotes.reflectModule.FlagsModule.AbsOverride", // Can be stabilized in 3.4.0 (unsure) or later "scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings", "scala.quoted.Quotes.reflectModule.FlagsModule.JavaAnnotation", From 3c3c0fbb54f6dfd4141ba7be014d092ed2f293be Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 21 Dec 2022 11:42:58 +0100 Subject: [PATCH 499/657] Check flags for method, val and bind symbols Co-authored-by: Guillaume Martres --- .../quoted/runtime/impl/QuotesImpl.scala | 45 ++++++++++++++----- library/src/scala/quoted/Quotes.scala | 20 ++++----- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 30be3120bc2e..d334a1566d52 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -9,7 +9,6 @@ import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.core.Annotations import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Decorators._ -import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.NameKinds import dotty.tools.dotc.core.NameOps._ import dotty.tools.dotc.core.StdNames._ @@ -276,12 +275,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object DefDef extends DefDefModule: def apply(symbol: Symbol, rhsFn: List[List[Tree]] => Option[Term]): DefDef = - assert(symbol.isTerm, s"expected a term symbol but received $symbol") + xCheckMacroAssert(symbol.isTerm, s"expected a term symbol but received $symbol") + xCheckMacroAssert(symbol.flags.is(Flags.Method), "expected a symbol with `Method` flag set") withDefaultPos(tpd.DefDef(symbol.asTerm, prefss => - xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(prefss)), symbol).getOrElse(tpd.EmptyTree) + xCheckedMacroOwners(xCheckMacroValidExpr(rhsFn(prefss)), symbol).getOrElse(tpd.EmptyTree) )) def copy(original: Tree)(name: String, paramss: List[ParamClause], tpt: TypeTree, rhs: Option[Term]): DefDef = - tpd.cpy.DefDef(original)(name.toTermName, paramss, tpt, xCheckMacroedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree)) + tpd.cpy.DefDef(original)(name.toTermName, paramss, tpt, xCheckedMacroOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree)) def unapply(ddef: DefDef): (String, List[ParamClause], TypeTree, Option[Term]) = (ddef.name.toString, ddef.paramss, ddef.tpt, optional(ddef.rhs)) end DefDef @@ -307,9 +307,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object ValDef extends ValDefModule: def apply(symbol: Symbol, rhs: Option[Term]): ValDef = - withDefaultPos(tpd.ValDef(symbol.asTerm, xCheckMacroedOwners(xCheckMacroValidExpr(rhs), symbol).getOrElse(tpd.EmptyTree))) + xCheckMacroAssert(!symbol.flags.is(Flags.Method), "expected a symbol without `Method` flag set") + withDefaultPos(tpd.ValDef(symbol.asTerm, xCheckedMacroOwners(xCheckMacroValidExpr(rhs), symbol).getOrElse(tpd.EmptyTree))) def copy(original: Tree)(name: String, tpt: TypeTree, rhs: Option[Term]): ValDef = - tpd.cpy.ValDef(original)(name.toTermName, tpt, xCheckMacroedOwners(xCheckMacroValidExpr(rhs), original.symbol).getOrElse(tpd.EmptyTree)) + tpd.cpy.ValDef(original)(name.toTermName, tpt, xCheckedMacroOwners(xCheckMacroValidExpr(rhs), original.symbol).getOrElse(tpd.EmptyTree)) def unapply(vdef: ValDef): (String, TypeTree, Option[Term]) = (vdef.name.toString, vdef.tpt, optional(vdef.rhs)) @@ -398,7 +399,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def etaExpand(owner: Symbol): Term = self.tpe.widen match { case mtpe: Types.MethodType if !mtpe.isParamDependent => val closureResType = mtpe.resType match { - case t: Types.MethodType => t.toFunctionType(isJava = self.symbol.is(JavaDefined)) + case t: Types.MethodType => t.toFunctionType(isJava = self.symbol.is(dotc.core.Flags.JavaDefined)) case t => t } val closureTpe = Types.MethodType(mtpe.paramNames, mtpe.paramInfos, closureResType) @@ -811,7 +812,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Lambda extends LambdaModule: def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block = val meth = dotc.core.Symbols.newAnonFun(owner, tpe) - withDefaultPos(tpd.Closure(meth, tss => xCheckMacroedOwners(xCheckMacroValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth))) + withDefaultPos(tpd.Closure(meth, tss => xCheckedMacroOwners(xCheckMacroValidExpr(rhsFn(meth, tss.head.map(withDefaultPos))), meth))) def unapply(tree: Block): Option[(List[ValDef], Term)] = tree match { case Block((ddef @ DefDef(_, tpd.ValDefs(params) :: Nil, _, Some(body))) :: Nil, Closure(meth, _)) @@ -1482,6 +1483,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object Bind extends BindModule: def apply(sym: Symbol, pattern: Tree): Bind = + xCheckMacroAssert(sym.flags.is(Flags.Case), "expected a symbol with `Case` flag set") withDefaultPos(tpd.Bind(sym, pattern)) def copy(original: Tree)(name: String, pattern: Tree): Bind = withDefaultPos(tpd.cpy.Bind(original)(name.toTermName, pattern)) @@ -2522,14 +2524,23 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler newMethod(owner, name, tpe, Flags.EmptyFlags, noSymbol) def newMethod(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol = assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`") + checkValidFlags(flags.toTermFlags, Flags.validMethodFlags) dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin) def newVal(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol = assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`") + checkValidFlags(flags.toTermFlags, Flags.validValFlags) dotc.core.Symbols.newSymbol(owner, name.toTermName, flags, tpe, privateWithin) def newBind(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol = - dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | Case, tpe) + checkValidFlags(flags.toTermFlags, Flags.validBindFlags) + dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Case, tpe) def noSymbol: Symbol = dotc.core.Symbols.NoSymbol + private inline def checkValidFlags(inline flags: Flags, inline valid: Flags): Unit = + xCheckMacroAssert( + flags <= valid, + s"Received invalid flags. Expected flags ${flags.show} to only contain a subset of ${valid.show}." + ) + def freshName(prefix: String): String = NameKinds.MacroNames.fresh(prefix.toTermName).toString end Symbol @@ -2602,7 +2613,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler self.isTerm && !self.is(dotc.core.Flags.Method) && !self.is(dotc.core.Flags.Case/*, FIXME add this check and fix sourcecode butNot = Enum | Module*/) def isDefDef: Boolean = self.is(dotc.core.Flags.Method) def isBind: Boolean = - self.is(dotc.core.Flags.Case, butNot = Enum | Module) && !self.isClass + self.is(dotc.core.Flags.Case, butNot = dotc.core.Flags.Enum | dotc.core.Flags.Module) && !self.isClass def isNoSymbol: Boolean = self == Symbol.noSymbol def exists: Boolean = self != Symbol.noSymbol @@ -2846,6 +2857,13 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def Synthetic: Flags = dotc.core.Flags.Synthetic def Trait: Flags = dotc.core.Flags.Trait def Transparent: Flags = dotc.core.Flags.Transparent + + // Keep: aligned with Quotes's `newMethod` doc + private[QuotesImpl] def validMethodFlags: Flags = Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | AbsOverride | JavaStatic // Flags that could be allowed: Synthetic | ExtensionMethod | Exported | Erased | Infix | Invisible + // Keep: aligned with Quotes's `newVal` doc + private[QuotesImpl] def validValFlags: Flags = Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | AbsOverride | JavaStatic // Flags that could be added: Synthetic | Erased | Invisible + // Keep: aligned with Quotes's `newBind` doc + private[QuotesImpl] def validBindFlags: Flags = Case // Flags that could be allowed: Implicit | Given | Erased end Flags given FlagsMethods: FlagsMethods with @@ -2966,7 +2984,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler /** Checks that all definitions in this tree have the expected owner. * Nested definitions are ignored and assumed to be correct by construction. */ - private def xCheckMacroedOwners(tree: Option[Tree], owner: Symbol): tree.type = + private def xCheckedMacroOwners(tree: Option[Tree], owner: Symbol): tree.type = if xCheckMacro then tree match case Some(tree) => @@ -2977,7 +2995,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler /** Checks that all definitions in this tree have the expected owner. * Nested definitions are ignored and assumed to be correct by construction. */ - private def xCheckMacroedOwners(tree: Tree, owner: Symbol): tree.type = + private def xCheckedMacroOwners(tree: Tree, owner: Symbol): tree.type = if xCheckMacro then xCheckMacroOwners(tree, owner) tree @@ -3048,6 +3066,9 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler "Reference to a method must be eta-expanded before it is used as an expression: " + term.show) term + private inline def xCheckMacroAssert(inline cond: Boolean, inline msg: String): Unit = + assert(!xCheckMacro || cond, msg) + object Printer extends PrinterModule: lazy val TreeCode: Printer[Tree] = new Printer[Tree]: diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 7cb681f66c51..c7d5719b0e1f 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -3785,9 +3785,10 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * @param parent The owner of the method * @param name The name of the method * @param tpe The type of the method (MethodType, PolyType, ByNameType) - * @param flags extra flags to with which the symbol should be constructed + * @param flags extra flags to with which the symbol should be constructed. `Method` flag will be added. Can be `Private | Protected | Override | Deferred | Final | Method | Implicit | Given | Local | JavaStatic` * @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol. */ + // Keep: `flags` doc aligned with QuotesImpl's `validMethodFlags` def newMethod(parent: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol /** Generates a new val/var/lazy val symbol with the given parent, name and type. @@ -3801,11 +3802,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * @param parent The owner of the val/var/lazy val * @param name The name of the val/var/lazy val * @param tpe The type of the val/var/lazy val - * @param flags extra flags to with which the symbol should be constructed + * @param flags extra flags to with which the symbol should be constructed. Can be `Private | Protected | Override | Deferred | Final | Param | Implicit | Lazy | Mutable | Local | ParamAccessor | Module | Package | Case | CaseAccessor | Given | Enum | JavaStatic` * @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol. * @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be * direct or indirect children of the reflection context's owner. */ + // Keep: `flags` doc aligned with QuotesImpl's `validValFlags` def newVal(parent: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol /** Generates a pattern bind symbol with the given parent, name and type. @@ -3816,11 +3818,12 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * * @param parent The owner of the binding * @param name The name of the binding - * @param flags extra flags to with which the symbol should be constructed + * @param flags extra flags to with which the symbol should be constructed. `Case` flag will be added. Can be `Case` * @param tpe The type of the binding * @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be * direct or indirect children of the reflection context's owner. */ + // Keep: `flags` doc aligned with QuotesImpl's `validBindFlags` def newBind(parent: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol /** Definition not available */ @@ -4373,15 +4376,10 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Is this symbol `abstract` */ def Abstract: Flags - /** Is this symbol is labeled with of abstract & override + /** Is this an abstract override method? * - * The override modifier has an additional significance when combined with the abstract modifier. - * That modifier combination is only allowed for value members of traits. - * - * We call a member MM of a template incomplete if it is either abstract (i.e. defined by a declaration), or it is labeled abstract and override and every member overridden by MM is again incomplete. - * - * Note that the abstract override modifier combination does not influence the concept whether a member is concrete or abstract. - * A member is abstract if only a declaration is given for it; it is concrete if a full definition is given. + * This corresponds to a definition declared as "abstract override def" in the source. + * See https://stackoverflow.com/questions/23645172/why-is-abstract-override-required-not-override-alone-in-subtrait for examples. */ @experimental def AbsOverride: Flags From 0cf8b5182ddd9dba487d0e1430cec60710ec0d78 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 28 Apr 2023 12:00:02 +0200 Subject: [PATCH 500/657] apply enums/adts.md --- docs/_spec/05-classes-and-objects.md | 49 ++++++ docs/_spec/APPLIEDreference/enums/adts.md | 90 +++++++++++ docs/_spec/TODOreference/enums/adts.md | 173 ---------------------- 3 files changed, 139 insertions(+), 173 deletions(-) create mode 100644 docs/_spec/APPLIEDreference/enums/adts.md delete mode 100644 docs/_spec/TODOreference/enums/adts.md diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index 5de7048ba00e..e3fa8fe3e613 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -801,3 +801,52 @@ Generally, a _companion module_ of a class is an object which has the same name Conversely, the class is called the _companion class_ of the module. Very much like a concrete class definition, an object definition may still contain declarations of abstract type members, but not of abstract term members. + +## Enum Definitions + + +```ebnf +TmplDef ::= ‘enum’ EnumDef +EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody +EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’ +EnumStat ::= TemplateStat + | {Annotation [nl]} {Modifier} EnumCase +``` + +### Enum Cases + +```ebnf +EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps] | ids) +``` + + + +### Variance for Type Parameters + +A parameterized enum case ´C´ of enum ´E´ with _inferred_ type parameters will copy variance annotations. +e.g. type parameter ´T_{i}´ from ´E´ will have the same variance as type parameter `´T'_{i}´` in ´C´. + +###### Example + +The following enum `View` has a contravariant type parameter ´T´ and a single case `Refl`, representing a function mapping a type `T` to itself: + +```scala +enum View[-´T´]: + case Refl(f: ´T´ => ´T´) +``` + +`Refl` expands to the following enum: + +```scala +enum View[-´T´]: + case Refl[-´T'´](f: ´T'´ => ´T'´) extends View[´T'´] +``` + +The definition of `Refl` is incorrectly typed, as it uses contravariant type `´T'´` in the covariant result position of a function type. + +A correctly typed version would use an _explicit_, _invariant_ type parameter `´R´` on case `Refl`: + +```scala +enum View[-´T´]: + case Refl[´R´](f: ´R´ => ´R´) extends View[´R´] +``` \ No newline at end of file diff --git a/docs/_spec/APPLIEDreference/enums/adts.md b/docs/_spec/APPLIEDreference/enums/adts.md new file mode 100644 index 000000000000..23599e49dc5b --- /dev/null +++ b/docs/_spec/APPLIEDreference/enums/adts.md @@ -0,0 +1,90 @@ +--- +layout: doc-page +title: "Algebraic Data Types" +nightlyOf: https://docs.scala-lang.org/scala3/reference/enums/adts.html +--- + +The [`enum` concept](./enums.md) is general enough to also support algebraic data types (ADTs) and their generalized version (GADTs). +Here is an example how an `Option` type can be represented as an ADT: + +```scala +enum Option[+T]: + case Some(x: T) + case None +``` + +This example introduces an `Option` enum with a covariant type parameter `T` consisting of two cases, `Some` and `None`. +`Some` is parameterized with a value parameter `x`. +It is a shorthand for writing a case class that extends `Option`. +Since `None` is not parameterized, it is treated as a normal enum value. + +The `extends` clauses that were omitted in the example above can also be given explicitly: + +```scala +enum Option[+T]: + case Some(x: T) extends Option[T] + case None extends Option[Nothing] +``` + +Note that the parent type of the `None` value is inferred as `Option[Nothing]`. +Generally, all covariant type parameters of the enum class are minimized in a compiler-generated `extends` clause whereas all contravariant type parameters are maximized. +If `Option` was non-variant, you would need to give the extends clause of `None` explicitly. + +As for normal enum values, the cases of an `enum` are all defined in the `enum`'s companion object. +So it's `Option.Some` and `Option.None` unless the definitions are "pulled out" with an import. + + +## Widening of Constructor Application + +Observe here the inferred result types of the following expressions: +```scala +scala> Option.Some("hello") +val res1: t2.Option[String] = Some(hello) + +scala> Option.None +val res2: t2.Option[Nothing] = None +``` + +Note that the type of the expressions above is always `Option`. +Generally, the type of a enum case constructor application will be widened to the underlying enum type, unless a more specific type is expected. +This is a subtle difference with respect to normal case classes. +The classes making up the cases do exist, and can be unveiled, either by constructing them directly with a `new`, or by explicitly providing an expected type. + +```scala +scala> new Option.Some(2) +val res3: Option.Some[Int] = Some(2) +scala> val x: Option.Some[Int] = Option.Some(3) +val res4: Option.Some[Int] = Some(3) +``` + +As all other enums, ADTs can define methods. +For instance, here is `Option` again, with an `isDefined` method and an `Option(...)` constructor in its companion object. + +```scala +enum Option[+T]: + case Some(x: T) + case None + + def isDefined: Boolean = this match + case None => false + case _ => true + +object Option: + + def apply[T >: Null](x: T): Option[T] = + if x == null then None else Some(x) + +end Option +``` + +Enumerations and ADTs have been presented as two different concepts. +But since they share the same syntactic construct, they can be seen simply as two ends of a spectrum and it is perfectly possible to construct hybrids. +For instance, the code below gives an implementation of `Color` either with three enum values or with a parameterized case that takes an RGB value. + +```scala +enum Color(val rgb: Int): + case Red extends Color(0xFF0000) + case Green extends Color(0x00FF00) + case Blue extends Color(0x0000FF) + case Mix(mix: Int) extends Color(mix) +``` diff --git a/docs/_spec/TODOreference/enums/adts.md b/docs/_spec/TODOreference/enums/adts.md deleted file mode 100644 index 3ab8c9f3b45b..000000000000 --- a/docs/_spec/TODOreference/enums/adts.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -layout: doc-page -title: "Algebraic Data Types" -nightlyOf: https://docs.scala-lang.org/scala3/reference/enums/adts.html ---- - -The [`enum` concept](./enums.md) is general enough to also support algebraic data -types (ADTs) and their generalized version (GADTs). Here is an example -how an `Option` type can be represented as an ADT: - -```scala -enum Option[+T]: - case Some(x: T) - case None -``` - -This example introduces an `Option` enum with a covariant type -parameter `T` consisting of two cases, `Some` and `None`. `Some` is -parameterized with a value parameter `x`. It is a shorthand for writing a -case class that extends `Option`. Since `None` is not parameterized, it -is treated as a normal enum value. - -The `extends` clauses that were omitted in the example above can also -be given explicitly: - -```scala -enum Option[+T]: - case Some(x: T) extends Option[T] - case None extends Option[Nothing] -``` - -Note that the parent type of the `None` value is inferred as -`Option[Nothing]`. Generally, all covariant type parameters of the enum -class are minimized in a compiler-generated `extends` clause whereas all -contravariant type parameters are maximized. If `Option` was non-variant, -you would need to give the extends clause of `None` explicitly. - -As for normal enum values, the cases of an `enum` are all defined in -the `enum`s companion object. So it's `Option.Some` and `Option.None` -unless the definitions are "pulled out" with an import: - -```scala -scala> Option.Some("hello") -val res1: t2.Option[String] = Some(hello) - -scala> Option.None -val res2: t2.Option[Nothing] = None -``` - -Note that the type of the expressions above is always `Option`. Generally, the type of a enum case constructor application will be widened to the underlying enum type, unless a more specific type is expected. This is a subtle difference with respect to normal case classes. The classes making up the cases do exist, and can be unveiled, either by constructing them directly with a `new`, or by explicitly providing an expected type. - -```scala -scala> new Option.Some(2) -val res3: Option.Some[Int] = Some(2) -scala> val x: Option.Some[Int] = Option.Some(3) -val res4: Option.Some[Int] = Some(3) -``` - -As all other enums, ADTs can define methods. For instance, here is `Option` again, with an -`isDefined` method and an `Option(...)` constructor in its companion object. - -```scala -enum Option[+T]: - case Some(x: T) - case None - - def isDefined: Boolean = this match - case None => false - case _ => true - -object Option: - - def apply[T >: Null](x: T): Option[T] = - if x == null then None else Some(x) - -end Option -``` - -Enumerations and ADTs have been presented as two different -concepts. But since they share the same syntactic construct, they can -be seen simply as two ends of a spectrum and it is perfectly possible -to construct hybrids. For instance, the code below gives an -implementation of `Color` either with three enum values or with a -parameterized case that takes an RGB value. - -```scala -enum Color(val rgb: Int): - case Red extends Color(0xFF0000) - case Green extends Color(0x00FF00) - case Blue extends Color(0x0000FF) - case Mix(mix: Int) extends Color(mix) -``` - -## Parameter Variance of Enums - -By default, parameterized cases of enums with type parameters will copy the type parameters of their parent, along -with any variance notations. As usual, it is important to use type parameters carefully when they are variant, as shown -below: - -The following `View` enum has a contravariant type parameter `T` and a single case `Refl`, representing a function -mapping a type `T` to itself: - -```scala -enum View[-T]: - case Refl(f: T => T) -``` - -The definition of `Refl` is incorrect, as it uses contravariant type `T` in the covariant result position of a -function type, leading to the following error: - -```scala --- Error: View.scala:2:12 -------- -2 | case Refl(f: T => T) - | ^^^^^^^^^ - |contravariant type T occurs in covariant position in type T => T of value f - |enum case Refl requires explicit declaration of type T to resolve this issue. -``` - -Because `Refl` does not declare explicit parameters, it looks to the compiler like the following: - -```scala -enum View[-T]: - case Refl[/*synthetic*/-T1](f: T1 => T1) extends View[T1] -``` - -The compiler has inferred for `Refl` the contravariant type parameter `T1`, following `T` in `View`. -We can now clearly see that `Refl` needs to declare its own non-variant type parameter to correctly type `f`, -and can remedy the error by the following change to `Refl`: - -```diff -enum View[-T]: -- case Refl(f: T => T) -+ case Refl[R](f: R => R) extends View[R] -``` - -Above, type `R` is chosen as the parameter for `Refl` to highlight that it has a different meaning to -type `T` in `View`, but any name will do. - -After some further changes, a more complete implementation of `View` can be given as follows and be used -as the function type `T => U`: - -```scala -enum View[-T, +U] extends (T => U): - case Refl[R](f: R => R) extends View[R, R] - - final def apply(t: T): U = this match - case refl: Refl[r] => refl.f(t) -``` - -## Syntax of Enums - -Changes to the syntax fall in two categories: enum definitions and cases inside enums. -The changes are specified below as deltas with respect to the Scala syntax given [here](../syntax.md) - - 1. Enum definitions are defined as follows: - - ``` - TmplDef ::= `enum' EnumDef - EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumBody - EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’ - EnumStat ::= TemplateStat - | {Annotation [nl]} {Modifier} EnumCase - ``` - - 2. Cases of enums are defined as follows: - - ``` - EnumCase ::= `case' (id ClassConstr [`extends' ConstrApps]] | ids) - ``` - -## Reference - -For more information, see [Issue #1970](https://github.com/lampepfl/dotty/issues/1970). From 8be388a1b227f9525ac57212697a57879714f1a4 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 28 Apr 2023 12:01:11 +0200 Subject: [PATCH 501/657] apply enums/enums-index.md --- .../{TODOreference => APPLIEDreference}/enums/enums-index.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/enums/enums-index.md (100%) diff --git a/docs/_spec/TODOreference/enums/enums-index.md b/docs/_spec/APPLIEDreference/enums/enums-index.md similarity index 100% rename from docs/_spec/TODOreference/enums/enums-index.md rename to docs/_spec/APPLIEDreference/enums/enums-index.md From 471755329dfe276a3413da2d329e0e62d6771170 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 28 Apr 2023 12:32:10 +0200 Subject: [PATCH 502/657] apply enums/enums.md --- docs/_spec/05-classes-and-objects.md | 52 +++++++++++ docs/_spec/12-the-scala-standard-library.md | 4 + .../enums/enums.md | 88 +++++-------------- 3 files changed, 80 insertions(+), 64 deletions(-) rename docs/_spec/{TODOreference => APPLIEDreference}/enums/enums.md (64%) diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index e3fa8fe3e613..ea8c7bbb43fd 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -821,6 +821,58 @@ EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps] | ids) +### Lowering of Enum Definitions + +Enums are represented as `sealed` classes that extend the `scala.reflect.Enum` trait. +This trait defines a single public method, `ordinal`: + +```scala +package scala.reflect + +transparent trait Enum extends Any, Product, Serializable: + + def ordinal: Int +``` + +Simple enum cases all share a single implementation class. + +Value enum cases will each be implemented by a unique class. + +###### Example +Consider the enumeration `RGB`, consisting of simple enum cases: +```scala +enum RGB: + case Red, Green, Blue +``` + +Each simple case `Red`, `Green`, and `Blue` will be instantiated using a private method that takes a tag and a name as arguments. + +For instance, the first definition of value `Color.Red` above would expand to: + +```scala +val Red: Color = $new(0, "Red") +``` + + +###### Example + +Consider the more complex enumeration `Color`, consisting of value enum cases: +```scala +enum Color(val rgb: Int): + case Red extends Color(0xFF0000) + case Green extends Color(0x00FF00) + case Blue extends Color(0x0000FF) +``` + +Each value case will expand as follows, for instance `Green` expands to + +```scala +val Green: Color = new Color(0x00FF00): + def ordinal: Int = 1 + override def productPrefix: String = "Green" + override def toString: String = "Green" +``` + ### Variance for Type Parameters A parameterized enum case ´C´ of enum ´E´ with _inferred_ type parameters will copy variance annotations. diff --git a/docs/_spec/12-the-scala-standard-library.md b/docs/_spec/12-the-scala-standard-library.md index 14d1fb82aea4..441955df9b4f 100644 --- a/docs/_spec/12-the-scala-standard-library.md +++ b/docs/_spec/12-the-scala-standard-library.md @@ -345,6 +345,10 @@ class PartialFunction[-A, +B] extends Function1[A, B] { All case classes automatically extend the `Product` trait (and generate synthetic methods to conform to it) (but not `Product´n´`), and define a `_´n´` method for each of their arguments. +### Trait `Enum` + +All enum definitions automatically extend the `reflect.Enum` trait (and generate synthetic methods to conform to it). + ### Class `Array` All operations on arrays desugar to the corresponding operations of the underlying platform. diff --git a/docs/_spec/TODOreference/enums/enums.md b/docs/_spec/APPLIEDreference/enums/enums.md similarity index 64% rename from docs/_spec/TODOreference/enums/enums.md rename to docs/_spec/APPLIEDreference/enums/enums.md index 65051bdfb39f..bcab50d3a36d 100644 --- a/docs/_spec/TODOreference/enums/enums.md +++ b/docs/_spec/APPLIEDreference/enums/enums.md @@ -11,9 +11,8 @@ enum Color: case Red, Green, Blue ``` -This defines a new `sealed` class, `Color`, with three values, `Color.Red`, -`Color.Green`, `Color.Blue`. The color values are members of `Color`s -companion object. +This defines a new `sealed` class, `Color`, with three values, `Color.Red`, `Color.Green`, `Color.Blue`. +The color values are members of `Color`s companion object. ## Parameterized enums @@ -26,13 +25,12 @@ enum Color(val rgb: Int): case Blue extends Color(0x0000FF) ``` -As the example shows, you can define the parameter value by using an -explicit extends clause. +As the example shows, you can define the parameter value by using an explicit extends clause. ## Methods defined for enums -The values of an enum correspond to unique integers. The integer -associated with an enum value is returned by its `ordinal` method: +The values of an enum correspond to unique integers. +The integer associated with an enum value is returned by its `ordinal` method: ```scala scala> val red = Color.Red @@ -42,10 +40,9 @@ val res0: Int = 0 ``` The companion object of an enum also defines three utility methods. -The `valueOf` method obtains an enum value -by its name. The `values` method returns all enum values -defined in an enumeration in an `Array`. The `fromOrdinal` -method obtains an enum value from its ordinal (`Int`) value. +The `valueOf` method obtains an enum value by its name. +The `values` method returns all enum values defined in an enumeration in an `Array`. +The `fromOrdinal` method obtains an enum value from its ordinal (`Int`) value. ```scala scala> Color.valueOf("Blue") @@ -58,7 +55,8 @@ val res2: Color = Red ## User-defined members of enums -It is possible to add your own definitions to an enum. Example: +It is possible to add your own definitions to an enum. +For example: ```scala enum Planet(mass: Double, radius: Double): @@ -94,11 +92,10 @@ end Planet Enum case declarations are similar to secondary constructors: they are scoped outside of the enum template, despite being declared within it. -This means that enum case declarations cannot access inner members of the -enum class. +This means that enum case declarations cannot access inner members of the enum class. -Similarly, enum case declarations may not directly reference members of the enum's companion object, -even if they are imported (directly, or by renaming). For example: +Similarly, enum case declarations may not directly reference members of the enum's companion object, even if they are imported (directly, or by renaming). +For example: ```scala import Planet.* @@ -113,13 +110,13 @@ object Planet: private final val (earthMass, earthRadius) = (5.976e+24, 6.37814e6) end Planet ``` -The fields referenced by `Mercury` are not visible, and the fields referenced by `Venus` may not -be referenced directly (using `import Planet.*`). You must use an indirect reference, -such as demonstrated with `Earth`. +The fields referenced by `Mercury` are not visible, and the fields referenced by `Venus` may not be referenced directly (using `import Planet.*`). +You must use an indirect reference, such as demonstrated with `Earth`. ## Deprecation of Enum Cases -As a library author, you may want to signal that an enum case is no longer intended for use. However you could still want to gracefully handle the removal of a case from your public API, such as special casing deprecated cases. +As a library author, you may want to signal that an enum case is no longer intended for use. +However you could still want to gracefully handle the removal of a case from your public API, such as special casing deprecated cases. To illustrate, say that the `Planet` enum originally had an additional case: @@ -131,7 +128,8 @@ To illustrate, say that the `Planet` enum originally had an additional case: end Planet ``` -We now want to deprecate the `Pluto` case. First we add the `scala.deprecated` annotation to `Pluto`: +We now want to deprecate the `Pluto` case. +First we add the `scala.deprecated` annotation to `Pluto`: ```diff enum Planet(mass: Double, radius: Double): @@ -144,7 +142,8 @@ We now want to deprecate the `Pluto` case. First we add the `scala.deprecated` a end Planet ``` -Outside the lexical scopes of `enum Planet` or `object Planet`, references to `Planet.Pluto` will produce a deprecation warning, but within those scopes we can still reference it to implement introspection over the deprecated cases: +Outside the lexical scopes of `enum Planet` or `object Planet`, references to `Planet.Pluto` will produce a deprecation warning. +Within those scopes however, we can still reference it to implement introspection over the deprecated cases: ```scala trait Deprecations[T <: reflect.Enum] { @@ -163,8 +162,7 @@ We could imagine that a library may use [type class derivation](../contextual/de ## Compatibility with Java Enums -If you want to use the Scala-defined enums as [Java enums](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html), you can do so by extending -the class `java.lang.Enum`, which is imported by default, as follows: +If you want to use the Scala-defined enums as [Java enums](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html), you can do so by extending the class `java.lang.Enum`, which is imported by default, as follows: ```scala enum Color extends Enum[Color] { case Red, Green, Blue } @@ -180,43 +178,5 @@ scala> Color.Red.compareTo(Color.Green) val res15: Int = -1 ``` -For a more in-depth example of using Scala 3 enums from Java, see [this test](https://github.com/lampepfl/dotty/tree/main/tests/run/enum-java). In the test, the enums are defined in the `MainScala.scala` file and used from a Java source, `Test.java`. - -## Implementation - -Enums are represented as `sealed` classes that extend the `scala.reflect.Enum` trait. -This trait defines a single public method, `ordinal`: - -```scala -package scala.reflect - -/** A base trait of all Scala enum definitions */ -transparent trait Enum extends Any, Product, Serializable: - - /** A number uniquely identifying a case of an enum */ - def ordinal: Int -``` - -Enum values with `extends` clauses get expanded to anonymous class instances. -For instance, the `Venus` value above would be defined like this: - -```scala -val Venus: Planet = new Planet(4.869E24, 6051800.0): - def ordinal: Int = 1 - override def productPrefix: String = "Venus" - override def toString: String = "Venus" -``` - -Enum values without `extends` clauses all share a single implementation -that can be instantiated using a private method that takes a tag and a name as arguments. -For instance, the first -definition of value `Color.Red` above would expand to: - -```scala -val Red: Color = $new(0, "Red") -``` - -## Reference - -For more information, see [Issue #1970](https://github.com/lampepfl/dotty/issues/1970) and -[PR #4003](https://github.com/lampepfl/dotty/pull/4003). +For a more in-depth example of using Scala 3 enums from Java, see [this test](https://github.com/lampepfl/dotty/tree/main/tests/run/enum-java). +In the test, the enums are defined in the `MainScala.scala` file and used from a Java source, `Test.java`. From 94a0fcd1e6ebb75dc6dfa95147661abaf3c7503c Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 28 Apr 2023 16:03:13 +0200 Subject: [PATCH 503/657] apply enums/desugarEnums.md --- docs/_spec/05-classes-and-objects.md | 270 +++++++++++++++++- .../_spec/TODOreference/enums/desugarEnums.md | 215 -------------- 2 files changed, 255 insertions(+), 230 deletions(-) delete mode 100644 docs/_spec/TODOreference/enums/desugarEnums.md diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index ea8c7bbb43fd..634df1fa4bf6 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -41,6 +41,8 @@ The list of parents of a template must be well-formed. This means that the class denoted by the superclass constructor ´sc´ must be a subclass of the superclasses of all the traits ´mt_1, ..., mt_n´. In other words, the non-trait classes inherited by a template form a chain in the inheritance hierarchy which starts with the template's superclass. +It is forbidden for a template's superclass constructor ´sc´ to be an [enum class](#enum-definitions), unless the template is the implementation of an [enum case](#enum-definitions) of ´sc´. + The _least proper supertype_ of a template is the class type or [compound type](03-types.html#compound-types) consisting of all its parent class types. The statement sequence ´\mathit{stats}´ contains member definitions that define new members or overwrite members in the parent classes. @@ -811,21 +813,78 @@ EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody EnumBody ::= [nl] ‘{’ [SelfType] EnumStat {semi EnumStat} ‘}’ EnumStat ::= TemplateStat | {Annotation [nl]} {Modifier} EnumCase +EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps] | ids) ``` -### Enum Cases - -```ebnf -EnumCase ::= ‘case’ (id ClassConstr [‘extends’ ConstrApps] | ids) +An _enum definition_ implies the definition of an _enum class_, a companion object, and one or more _enum cases_. + +Enum definitions are useful to encode both Generalised Algebraic Data Types and Enumerated Types. + +The compiler expands enum definitions to code that only uses Scala's other language features. +As such, enum definitions in Scala are convenient _syntactic sugar_, but they are not essential to understand Scala's core. + +We now explain the expansion of enum definitions in detail. +First, some terminology and notational conventions: + +- We use ´E´ as a name of an enum definition, and ´C´ as a name of an enum case that appears in ´E´. +- We use `<...>` for syntactic constructs that in some circumstances might be empty. +For instance, `` represents one or more parameter lists `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` or nothing at all. +- Enum classes fall into two categories: + - _parameterized_ enum classes have at least one of the following: + - a type parameter section, denoted as `[´\mathit{tps}\,´]`; + - one or more (possibly empty) parameter sections, denoted as `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)`. + - _unparameterized_ enum classes have no type parameter sections and no parameter sections. +- Enum cases fall into three categories: + + - _Class cases_ are those cases that are parameterized, either with a type parameter section `[´\mathit{tps}\,´]` or with one or more (possibly empty) parameter sections `(´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)`. + - _Simple cases_ are cases of an unparameterized enum that have neither parameters nor an extends clause or body. + That is, they consist of a name only. + - _Value cases_ are all cases that do not have a parameter section but that do have a (possibly generated) `extends` clause and/or a body. + +- Simple cases and value cases are collectively called _singleton cases_. + +###### Example + +An example enum for a `Planet` enumeration can be given as +```scala +enum Planet(mass: Double, radius: Double): + case Mercury extends Planet(3.303e+23, 2.4397e6) + case Venus extends Planet(4.869e+24, 6.0518e6) + case Earth extends Planet(5.976e+24, 6.37814e6) + case Mars extends Planet(6.421e+23, 3.3972e6) + case Jupiter extends Planet(1.9e+27, 7.1492e7) + case Saturn extends Planet(5.688e+26, 6.0268e7) + case Uranus extends Planet(8.686e+25, 2.5559e7) + case Neptune extends Planet(1.024e+26, 2.4746e7) + + private inline val G = 6.67300E-11 + def surfaceGravity = G * mass / (radius * radius) + def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity +end Planet ``` - +###### Example + +An example enum for the Option ADT can be given as +```scala +enum Option[+T]: + case Some(x: T) + case None +``` ### Lowering of Enum Definitions -Enums are represented as `sealed` classes that extend the `scala.reflect.Enum` trait. -This trait defines a single public method, `ordinal`: +###### Summary +An enum class is represented as a `sealed` class that extends the `scala.reflect.Enum` trait. + +Enum cases are represented as follows: +- a class case is mapped to a `case class`, +- a singleton case is mapped to a `val` definition, where + - Simple cases all share a single implementation class. + - Value cases will each be implemented by a unique class. +###### Precise rules +The `scala.reflect.Enum` trait defines a single public method, `ordinal`: ```scala package scala.reflect @@ -833,10 +892,128 @@ transparent trait Enum extends Any, Product, Serializable: def ordinal: Int ``` +There are nine desugaring rules. +Rule (1) desugars enum definitions. +Rules (2) and (3) desugar simple cases. +Rules (4) to (6) define `extends` clauses for cases that are missing them. +Rules (7) to (9) define how such cases with `extends` clauses map into `case class`es or `val`s. -Simple enum cases all share a single implementation class. +1. An `enum` definition + ```scala + enum ´E´ ... { } + ``` + expands to a `sealed abstract` class that extends the `scala.reflect.Enum` trait and an associated companion object that contains the defined cases, expanded according to rules (2 - 8). + The enum class starts with a compiler-generated import that imports the names `` of all cases so that they can be used without prefix in the class. + ```scala + sealed abstract class ´E´ ... extends with scala.reflect.Enum { + import ´E´.{ } + + } + object ´E´ { } + ``` + +2. A singleton case consisting of a comma-separated list of enum names + ```scala + case ´C_1´, ..., ´C_n´ + ``` + expands to + ```scala + case ´C_1´; ...; case ´C_n´ + ``` + Any modifiers or annotations on the original case extend to all expanded cases. + This result is then further rewritten by either (3 or 4). + +3. A singleton case without an extends clause + ```scala + case ´C´ + ``` + of an unparameterized enum `´E´` expands to the following simple enum case in `´E´`'s companion object: + ```scala + val ´C´ = $new(n, "C") + ``` + Here, `$new` is a private method that creates an instance of ´E´ (see below). + +4. A singleton case without an extends clause + ```scala + case ´C´ + ``` + of an enum `´E´` with type parameters + ```scala + ´\mathit{v}_1´ ´T_1´ >: ´L_1´ <: ´U_1´ , ... , ´\mathit{v}_n´ ´T_n´ >: ´L_n´ <: ´U_n´ (n > 0) + ``` + where each of the variances `´\mathit{v}_i´` is either `'+'` or `'-'`, expands to the following value enum case: + ```scala + case ´C´ extends ´E´[´B_1´, ..., ´B_n´] + ``` + where `´B_i´` is `´L_i´` if `´\mathit{v}_i´ = '+'` and `´U_i´` if `´\mathit{v}_i´ = '-'`. + This result is then further rewritten with rule (8). + **NOTE:** It is not permitted for enums with non-variant type parameters to have singleton cases without an extends clause. + +5. A class case without an extends clause + ```scala + case ´C´ + ``` + of an enum `´E´` that does not take type parameters expands to + ```scala + case ´C´ extends ´E´ + ``` + This result is then further rewritten with rule (9). + +6. If `´E´` is an enum with type parameters `´\mathit{tps}´`, a class case with neither type parameters nor an extends clause + ```scala + case ´C´ + ``` + expands to + ```scala + case ´C´[´\mathit{tps}´] extends ´E´[´\mathit{tps}´] + ``` + This result is then further rewritten with rule (9). + For class cases that have type parameters themselves, an extends clause needs to be given explicitly. + + +7. If `´E´` is an enum with type parameters `´\mathit{tps}´`, a class case without type parameters but with an extends clause + ```scala + case ´C´ extends + ``` + expands to + ```scala + case ´C´[´\mathit{tps}´] extends + ``` + provided at least one of the parameters `´\mathit{tps}´` is mentioned in a parameter type in `` or in a type argument in ``. + +8. A value case + ```scala + case ´C´ extends + ``` + expands to the following `val` definition in `´E´`'s companion object: + ```scala + val ´C´ = new { ; def ordinal = ´\mathit{n}´ } + ``` + where `´\mathit{n}´` is the ordinal number of the case in the companion object, starting from 0. + The anonymous class also implements the abstract `Product` methods that it inherits from `Enum`. + **NOTE:** It is an error if a value case refers to a type parameter of `´E´` in a type argument within ``. + +9. A class case + ```scala + case ´C´ extends + ``` + expands analogous to a final case class in `´E´`'s companion object: + ```scala + final case class ´C´ extends { + def ordinal = ´\mathit{n}´ + } + ``` + where `´\mathit{n}´` is the ordinal number of the case in the companion object, starting from 0. + **NOTE:** It is an error if a class case refers to a type parameter of `´E´` in a parameter type in `` or `` or in a type argument of ``, unless that parameter is already a type parameter of the case, i.e. the parameter name is defined in ``. + +###### Superclass of an enum case + +an enum case (singleton or class) with explicit extends clause +```scala +case ´C´ extends +``` -Value enum cases will each be implemented by a unique class. +must extend the parent enum `´E´` as the first parent of ``. ###### Example Consider the enumeration `RGB`, consisting of simple enum cases: @@ -845,12 +1022,18 @@ enum RGB: case Red, Green, Blue ``` -Each simple case `Red`, `Green`, and `Blue` will be instantiated using a private method that takes a tag and a name as arguments. - -For instance, the first definition of value `Color.Red` above would expand to: +The three simple cases will expand as follows in the companion of `RGB`: ```scala -val Red: Color = $new(0, "Red") +val Red = $new(0, "Red") +val Green = $new(1, "Green") +val Blue = $new(2, "Blue") + +private def $new(_$ordinal: Int, $name: String) = + new RGB with scala.runtime.EnumValue: + def ordinal = _$ordinal + override def productPrefix = $name + override def toString = $name ``` @@ -864,15 +1047,72 @@ enum Color(val rgb: Int): case Blue extends Color(0x0000FF) ``` -Each value case will expand as follows, for instance `Green` expands to +The three value cases will expand as follows in the companion of `Color`: ```scala -val Green: Color = new Color(0x00FF00): +val Red = new Color(0xFF0000): + def ordinal: Int = 0 + override def productPrefix: String = "Red" + override def toString: String = "Red" +val Green = new Color(0x00FF00): def ordinal: Int = 1 override def productPrefix: String = "Green" override def toString: String = "Green" +val Blue = new Color(0x0000FF): + def ordinal: Int = 2 + override def productPrefix: String = "Blue" + override def toString: String = "Blue" ``` +### Widening of enum cases post-construction +The compiler-generated `apply` and `copy` methods of an class enum case +```scala +case ´C´[´\mathit{tps}\,´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´) extends ´P_1´, ..., ´P_n´ +``` +are treated specially. +A call `´C´[´\mathit{tps}\,´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` of the `apply` method is ascribed the underlying type `´P_1´ & ... & ´P_n´` (dropping any [transparent traits](../other-new-features/transparent-traits.md)) as long as that type is still compatible with the expected type at the point of application. +A call `t.copy[´\mathit{tps}\,´](´\mathit{ps}_1\,´)...(´\mathit{ps}_n´)` of `´C´`'s `copy` method is treated in the same way. + +### Translation of enums with only singleton cases + +An enum `´E´` (possibly generic) that defines one or more singleton cases, and no class cases will define the following additional synthetic members in its companion object (where `´E'´` denotes `´E´` with any type parameters replaced by wildcards): + + - A method `valueOf(name: String): ´E'´`. + It returns the singleton case value whose identifier is `name`. + - A method `values` which returns an `Array[´E'´]` of all singleton case values defined by `E`, in the order of their definitions. + +### Factory method for simple enum cases + +If an enum `´E´` contains at least one simple case, its companion object will define in addition: + + - A private method `$new` which defines a new simple case value with given ordinal number and name. + This method can be thought as being defined as follows. + + ```scala + private def $new(_$ordinal: Int, $name: String): ´E´ with runtime.EnumValue + ``` + - `$new` returns a new instance of an anonymous class which implements the abstract `Product` methods that it inherits from `Enum`. + - if `´E´` inherits from `java.lang.Enum` the anonymous class does not override the `ordinal` or `toString` methods, as these are final in `java.lang.Enum`. + Additionally `productPrefix` will delegate to `this.name`. + +### Translation of Java-compatible enums + +A Java-compatible enum is an enum that extends `java.lang.Enum`. +The translation rules are the same as above, with the reservations defined in this section. + +- It is a compile-time error for a Java-compatible enum to have class cases. + +- Cases such as `case C` expand to a `@static val` as opposed to a `val`. +This allows them to be generated as static fields of the enum type, thus ensuring they are represented the same way as Java enums. + +### Scopes for Enum Cases + +A case in an `enum` is treated similarly to a secondary constructor. +It can access neither the enclosing `enum` using `this`, nor its value parameters or instance members using simple identifiers. + +Even though translated enum cases are located in the enum's companion object, referencing this object or its members via `this` or a simple identifier is also illegal. +The compiler typechecks enum cases in the scope of the enclosing companion object but flags any such illegal accesses as errors. + ### Variance for Type Parameters A parameterized enum case ´C´ of enum ´E´ with _inferred_ type parameters will copy variance annotations. diff --git a/docs/_spec/TODOreference/enums/desugarEnums.md b/docs/_spec/TODOreference/enums/desugarEnums.md deleted file mode 100644 index 477653d670bb..000000000000 --- a/docs/_spec/TODOreference/enums/desugarEnums.md +++ /dev/null @@ -1,215 +0,0 @@ ---- -layout: doc-page -title: "Translation of Enums and ADTs" -nightlyOf: https://docs.scala-lang.org/scala3/reference/enums/desugarEnums.html ---- - -The compiler expands enums and their cases to code that only uses -Scala's other language features. As such, enums in Scala are -convenient _syntactic sugar_, but they are not essential to understand -Scala's core. - -We now explain the expansion of enums in detail. First, -some terminology and notational conventions: - - - We use `E` as a name of an enum, and `C` as a name of a case that appears in `E`. - - We use `<...>` for syntactic constructs that in some circumstances might be empty. For instance, - `` represents one or more parameter lists `(...)` or nothing at all. - - - Enum cases fall into three categories: - - - _Class cases_ are those cases that are parameterized, either with a type parameter section `[...]` or with one or more (possibly empty) parameter sections `(...)`. - - _Simple cases_ are cases of a non-generic enum that have neither parameters nor an extends clause or body. That is, they consist of a name only. - - _Value cases_ are all cases that do not have a parameter section but that do have a (possibly generated) `extends` clause and/or a body. - - Simple cases and value cases are collectively called _singleton cases_. - -The desugaring rules imply that class cases are mapped to case classes, and singleton cases are mapped to `val` definitions. - -There are nine desugaring rules. Rule (1) desugars enum definitions. Rules -(2) and (3) desugar simple cases. Rules (4) to (6) define `extends` clauses for cases that -are missing them. Rules (7) to (9) define how such cases with `extends` clauses -map into `case class`es or `val`s. - -1. An `enum` definition - ```scala - enum E ... { } - ``` - expands to a `sealed abstract` class that extends the `scala.reflect.Enum` trait and - an associated companion object that contains the defined cases, expanded according - to rules (2 - 8). The enum class starts with a compiler-generated import that imports - the names `` of all cases so that they can be used without prefix in the class. - ```scala - sealed abstract class E ... extends with scala.reflect.Enum { - import E.{ } - - } - object E { } - ``` - -2. A simple case consisting of a comma-separated list of enum names - ```scala - case C_1, ..., C_n - ``` - expands to - ```scala - case C_1; ...; case C_n - ``` - Any modifiers or annotations on the original case extend to all expanded - cases. - -3. A simple case - ```scala - case C - ``` - of an enum `E` that does not take type parameters expands to - ```scala - val C = $new(n, "C") - ``` - Here, `$new` is a private method that creates an instance of `E` (see - below). - -4. If `E` is an enum with type parameters - ```scala - V1 T1 >: L1 <: U1 , ... , Vn Tn >: Ln <: Un (n > 0) - ``` - where each of the variances `Vi` is either `'+'` or `'-'`, then a simple case - ```scala - case C - ``` - expands to - ```scala - case C extends E[B1, ..., Bn] - ``` - where `Bi` is `Li` if `Vi = '+'` and `Ui` if `Vi = '-'`. This result is then further - rewritten with rule (8). Simple cases of enums with non-variant type - parameters are not permitted (however value cases with explicit `extends` clause are) - -5. A class case without an extends clause - ```scala - case C - ``` - of an enum `E` that does not take type parameters expands to - ```scala - case C extends E - ``` - This result is then further rewritten with rule (9). - -6. If `E` is an enum with type parameters `Ts`, a class case with neither type parameters nor an extends clause - ```scala - case C - ``` - expands to - ```scala - case C[Ts] extends E[Ts] - ``` - This result is then further rewritten with rule (9). For class cases that have type parameters themselves, an extends clause needs to be given explicitly. - -7. If `E` is an enum with type parameters `Ts`, a class case without type parameters but with an extends clause - ```scala - case C extends - ``` - expands to - ```scala - case C[Ts] extends - ``` - provided at least one of the parameters `Ts` is mentioned in a parameter type in - `` or in a type argument in ``. - -8. A value case - ```scala - case C extends - ``` - expands to a value definition in `E`'s companion object: - ```scala - val C = new { ; def ordinal = n } - ``` - where `n` is the ordinal number of the case in the companion object, - starting from 0. The anonymous class also - implements the abstract `Product` methods that it inherits from `Enum`. - - It is an error if a value case refers to a type parameter of the enclosing `enum` - in a type argument of ``. - -9. A class case - ```scala - case C extends - ``` - expands analogous to a final case class in `E`'s companion object: - ```scala - final case class C extends - ``` - The enum case defines an `ordinal` method of the form - ```scala - def ordinal = n - ``` - where `n` is the ordinal number of the case in the companion object, - starting from 0. - - It is an error if a value case refers to a type parameter of the enclosing `enum` - in a parameter type in `` or in a type argument of ``, unless that parameter is already - a type parameter of the case, i.e. the parameter name is defined in ``. - - The compiler-generated `apply` and `copy` methods of an enum case - ```scala - case C(ps) extends P1, ..., Pn - ``` - are treated specially. A call `C(ts)` of the apply method is ascribed the underlying type - `P1 & ... & Pn` (dropping any [transparent traits](../other-new-features/transparent-traits.md)) - as long as that type is still compatible with the expected type at the point of application. - A call `t.copy(ts)` of `C`'s `copy` method is treated in the same way. - -## Translation of Enums with Singleton Cases - -An enum `E` (possibly generic) that defines one or more singleton cases -will define the following additional synthetic members in its companion object (where `E'` denotes `E` with -any type parameters replaced by wildcards): - - - A method `valueOf(name: String): E'`. It returns the singleton case value whose identifier is `name`. - - A method `values` which returns an `Array[E']` of all singleton case - values defined by `E`, in the order of their definitions. - -If `E` contains at least one simple case, its companion object will define in addition: - - - A private method `$new` which defines a new simple case value with given - ordinal number and name. This method can be thought as being defined as - follows. - - ```scala - private def $new(_$ordinal: Int, $name: String) = - new E with runtime.EnumValue: - def ordinal = _$ordinal - override def productPrefix = $name // if not overridden in `E` - override def toString = $name // if not overridden in `E` - ``` - -The anonymous class also implements the abstract `Product` methods that it inherits from `Enum`. -The `ordinal` method is only generated if the enum does not extend from `java.lang.Enum` (as Scala enums do not extend -`java.lang.Enum`s unless explicitly specified). In case it does, there is no need to generate `ordinal` as -`java.lang.Enum` defines it. Similarly there is no need to override `toString` as that is defined in terms of `name` in -`java.lang.Enum`. Finally, `productPrefix` will call `this.name` when `E` extends `java.lang.Enum`. - -## Scopes for Enum Cases - -A case in an `enum` is treated similarly to a secondary constructor. It can access neither the enclosing `enum` using `this`, nor its value parameters or instance members using simple -identifiers. - -Even though translated enum cases are located in the enum's companion object, referencing -this object or its members via `this` or a simple identifier is also illegal. The compiler typechecks enum cases in the scope of the enclosing companion object but flags any such illegal accesses as errors. - -## Translation of Java-compatible enums - -A Java-compatible enum is an enum that extends `java.lang.Enum`. The translation rules are the same as above, with the reservations defined in this section. - -It is a compile-time error for a Java-compatible enum to have class cases. - -Cases such as `case C` expand to a `@static val` as opposed to a `val`. This allows them to be generated as static fields of the enum type, thus ensuring they are represented the same way as Java enums. - -## Other Rules - -- A normal case class which is not produced from an enum case is not allowed to extend - `scala.reflect.Enum`. This ensures that the only cases of an enum are the ones that are - explicitly declared in it. - -- If an enum case has an `extends` clause, the enum class must be one of the - classes that's extended. From 0f031cdf2f6cefd7f3e3480c91b67dcb4ce287cf Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 27 Feb 2023 13:03:43 +0100 Subject: [PATCH 504/657] Add the CSS import in the layout Add import CSS Correct import --- docs/_layouts/base.html | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/_layouts/base.html b/docs/_layouts/base.html index feb79d1590a0..bc8b3ab26bb0 100644 --- a/docs/_layouts/base.html +++ b/docs/_layouts/base.html @@ -1,5 +1,6 @@ + {{ content }} From af6af313339d5b754e41c206d530be6de25e0a7b Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 27 Mar 2023 12:07:11 +0200 Subject: [PATCH 505/657] Correct the CSS files for the anchors, the warning and the images - Anchors: Remove the color of the anchors, the file content css is enough - Warning: Remove the color, banners.css is enough - Images: Add a width 100% and height auto to not overlaps --- docs/_assets/css/dottydoc.css | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/docs/_assets/css/dottydoc.css b/docs/_assets/css/dottydoc.css index ca7613835ff0..38990ed82a09 100644 --- a/docs/_assets/css/dottydoc.css +++ b/docs/_assets/css/dottydoc.css @@ -14,6 +14,11 @@ main.container { height: auto; } +.site-container img{ + width: 100%; + height: auto; +} + /* headers */ main header { border-bottom: 1px solid rgba(0,0,0,.1); @@ -45,21 +50,6 @@ ul.post-list { margin-bottom: 0; } -/* headings anchors */ -a.anchor { - color: transparent; - margin-left: -23px; - padding-right: 3px; - transition: color .4s ease-out; -} - -a.anchor::before { - content: "\f0c1"; - font-family: "Font Awesome 5 Free"; - font-weight: 900; - font-size: 20px; -} - h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, @@ -191,15 +181,6 @@ blockquote { border-left: 0.25em solid #ddd; } -aside { - padding: 15px; - margin: 10px 0; -} - -aside.warning { - border-left: 3px solid var(--red500); - background-color: var(--aside-warning-bg); -} aside.notice { border-left: 3px solid #4c97e4; From cc055745a3bff92194f2d6e282ec00bea131b2c6 Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 17 Apr 2023 13:42:41 +0200 Subject: [PATCH 506/657] Remove obsolete CSS, Corrections and improvements - I removed the obsolete CSS in dottydoc.css - The compliant CSS codes have been moved to folder dotty_res/styles/theme/layout - I improved the UI for the author of a blog post --- docs/_assets/css/dottydoc.css | 221 +----------------- docs/_layouts/blog-page.html | 37 +-- .../styles/theme/layout/container.css | 5 + .../dotty_res/styles/theme/layout/content.css | 47 +++- 4 files changed, 71 insertions(+), 239 deletions(-) diff --git a/docs/_assets/css/dottydoc.css b/docs/_assets/css/dottydoc.css index 38990ed82a09..6408fac3fab4 100644 --- a/docs/_assets/css/dottydoc.css +++ b/docs/_assets/css/dottydoc.css @@ -1,224 +1,23 @@ -html, body { - font-weight: 300; - height: 100%; -} - -main.container { - min-height: 100vh; - padding: 15px 15px; - padding-bottom: 45px; /* prevents the content to be hidden by the gitter sidecar */ -} - -.container img { - width: 100%; - height: auto; -} - -.site-container img{ - width: 100%; - height: auto; -} - -/* headers */ -main header { - border-bottom: 1px solid rgba(0,0,0,.1); - margin-bottom: 16px; - padding-bottom: 16px; -} - -main > h1 { - margin-bottom: 20px; -} - .byline { font-size: 14px; + display: flex; + margin-top: 10px; } -.byline, .byline a { - color: grey; -} -.byline .author { - display: block; -} - -/* indexes */ -ul.post-list { - list-style: none; - padding-left: 0; -} -.post-list h2 { - margin-bottom: 0; -} - -h1:hover a.anchor, -h2:hover a.anchor, -h3:hover a.anchor, -h4:hover a.anchor, -h5:hover a.anchor { - color: lightgrey; - text-decoration: none; -} - -h1:hover a.anchor:hover, -h2:hover a.anchor:hover, -h3:hover a.anchor:hover, -h4:hover a.anchor:hover, -h5:hover a.anchor:hover { - color: var(--secondary); -} - -/* blog footer */ -.blog-author { - color: gray; -} - -.blog-author img#author-img { +.byline img#author-img { width: auto; height: auto; - max-width:100px; - max-height:100px; - border-radius: 50%; + max-width: 50px; + border-radius: 10px; } -/* api docs */ -.api span.letter-anchor { - float: left; - width: 50px; - height: 50px; - border-radius: 50px; - color: white; - margin-top: 6px; - margin-right: 8px; - line-height: 50px; - text-align: center; - text-decoration: none; - font-size: 43px; - font-family: var(--font-family-sans-serif); -} -.api span.letter-anchor.object { - line-height: 48px; -} -.api span.letter-anchor.class { - line-height: 48px; - padding-right: 3px; -} -.letter-anchor.object { - background: #2c6c8d; -} -.letter-anchor.class { - background: #44ad7d; -} -.letter-anchor.trait { - background: #19aacf; -} -.letter-anchor.enum { - background: #7803fc; -} -.letter-anchor.package { - background: #2c6c8d; -} - -.api header { - font-family: var(--font-family-sans-serif); +.byline, .byline a { + color: grey; } -.api header .name-prefix { +.byline .author { display: block; } -.api header .name-suffix { - display: inline-block; -} - -.api header h1 { - margin: -13px 8px 0 0; - display: inline-block; -} -.api h2 { - margin-top: 1rem; -} -.api h3 { - display: inline; - margin: 0; - font: inherit; - font-weight: bold; -} - -/* improved display and wrapping of parameters */ -.api .params, .api .type-params { - display: inline-flex; - flex-flow: wrap; -} - -/* api layout */ -.wide-table { - display: table; - width: 100%; -} -.api .member:hover { - background: var(--doc-bg); - cursor: pointer; -} -.api .left-column { - white-space: nowrap; - padding-left: 1em; - border-left: 3px solid transparent;/* table rows cannot have borders*/ - font-family: var(--font-family-monospace); - text-align: right; - width: 1px; -} -.api .member:hover .left-column { - border-left: 3px solid var(--secondary); -} -.api .right-column { - display: inline; - text-align: right; - font-family: var(--font-family-monospace); -} - -/* admonitions */ -blockquote { - padding: 0 1em; - color: #777; - border-left: 0.25em solid #ddd; -} - - -aside.notice { - border-left: 3px solid #4c97e4; - background-color: #e4ebff; -} - -aside.success { - border-left: 3px solid #36bf1d; - background-color: #ebfddd; -} - -/* media queries for bigger screens (dottydoc is mobile-first) */ -@media (min-width: 576px) { - .byline .author { - display: inline; - margin-left: 1em; - } - main.container { - padding: 15px 30px; - } -} -@media (min-width: 768px) { - .api .member { - display: table-row; - } - .api .left-column { - display: table-cell; - } - .api .right-column { - display: flex; - flex-flow: wrap; - } - main.container { - padding: 15px 45px; - } -} -header { - position: static !important; - width: 100% !important; +.byline .secondary-infos{ + margin-left: 10px; } diff --git a/docs/_layouts/blog-page.html b/docs/_layouts/blog-page.html index c5d0fe8875e7..7d1a7439f68a 100644 --- a/docs/_layouts/blog-page.html +++ b/docs/_layouts/blog-page.html @@ -5,14 +5,25 @@

      {{ page.title }}

      {% if page.subTitle %}
      @@ -22,14 +33,4 @@

      {{ page.title }}

      {{ content }} - - {% if page.author and page.authorImg %} -
      -
      - - - {{ page.author }} - -
      - {% endif %} diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/container.css b/scaladoc/resources/dotty_res/styles/theme/layout/container.css index 849235e2fa82..53ede0e3dfff 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/container.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/container.css @@ -19,6 +19,11 @@ p { --header-height: calc(8 * var(--base-spacing)); } +.site-container img{ + max-width: 100%; + height: auto; +} + /* Scrollbar */ ::-webkit-scrollbar { diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/content.css b/scaladoc/resources/dotty_res/styles/theme/layout/content.css index e46e66cba9e5..8ebc3459c7ed 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/content.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/content.css @@ -8,6 +8,19 @@ scroll-behavior: smooth; } +/* blog footer */ +.blog-author { + color: gray; +} + +.blog-author img#author-img { + width: auto; + height: auto; + max-width:100px; + max-height:100px; + border-radius: 50%; +} + #content { display: flex; flex-flow: row; @@ -144,9 +157,9 @@ } #content h2 { - color: var(--text-primary); - margin-block-start: calc(6* var(--base-spacing)); - margin-block-end: calc(3* var(--base-spacing)); + color: var(--text-primary); + margin-block-start: calc(6* var(--base-spacing)); + margin-block-end: calc(3* var(--base-spacing)); } #content .cover > h2 { @@ -167,7 +180,7 @@ /* content first paragraph */ .first-p { - margin-block-start: calc(2* var(--base-spacing)); + margin-block-start: calc(2* var(--base-spacing)); } #content .first-p { @@ -218,18 +231,32 @@ /* content link */ #content a { - color: var(--text-primary); - text-decoration: underline solid 1px; - -webkit-text-decoration-line: underline; /* Safari */ - text-decoration-line: underline; - text-underline-offset: 2px; - transition: text-decoration-color .2s ease-in-out; + color: var(--text-primary); + text-decoration: underline solid 1px; + -webkit-text-decoration-line: underline; /* Safari */ + text-decoration-line: underline; + text-underline-offset: 2px; + transition: text-decoration-color .2s ease-in-out; } #content a:hover { text-decoration-color: transparent; } +#content a.anchor { + color: transparent; + margin-left: -23px; + padding-right: 3px; + transition: color .4s ease-out; +} + +#content a.anchor::before { + content: "\f0c1"; + font-family: "Font Awesome 5 Free"; + font-weight: 900; + font-size: 20px; +} + #content .cover-header { margin-block-end: calc(2 * var(--base-spacing)); } From 4d53eab1fb4a94dd8c8ab0fafc383a7b3339ead9 Mon Sep 17 00:00:00 2001 From: Lucas Leblanc <44496264+Dedelweiss@users.noreply.github.com> Date: Tue, 2 May 2023 12:37:29 +0200 Subject: [PATCH 507/657] deps(scaladoc): update flexmark from 0.42.12 to 0.62.2 (#17347) This pr updates the flexmark dependencies used in Scaladoc from 0.42.12, which is from 2019, up to 0.62.2. This is mainly done to tackle a bunch of CVEs that are attached to the old versions of flexmark. fixes https://github.com/lampepfl/dotty/issues/16223 --------- Co-authored-by: Chris Kipp --- dist/bin/scaladoc | 24 ++++++++++++++----- project/Dependencies.scala | 9 ++++--- .../src/dotty/tools/scaladoc/DocContext.scala | 5 ---- scaladoc/src/dotty/tools/scaladoc/Main.scala | 12 +--------- .../src/dotty/tools/scaladoc/Scaladoc.scala | 9 ++----- .../tools/scaladoc/ScaladocCommand.scala | 13 +--------- .../tools/scaladoc/ScaladocSettings.scala | 13 ---------- .../dotty/tools/scaladoc/SocialLinks.scala | 4 ---- .../dotty/tools/scaladoc/SourceLinks.scala | 1 - .../src/dotty/tools/scaladoc/compat.scala | 3 +-- .../parsers/WikiCodeBlockParser.scala | 17 ++++++------- .../scaladoc/renderers/HtmlRenderer.scala | 9 ------- .../tools/scaladoc/renderers/Locations.scala | 8 ------- .../scaladoc/renderers/MarkdownRenderer.scala | 11 --------- .../scaladoc/renderers/MemberRenderer.scala | 3 --- .../tools/scaladoc/renderers/Renderer.scala | 7 ------ .../tools/scaladoc/renderers/Resources.scala | 7 ------ .../site/FlexmarkSectionWrapper.scala | 1 - .../dotty/tools/scaladoc/site/common.scala | 3 ++- .../dotty/tools/scaladoc/site/templates.scala | 1 - .../snippets/FlexmarkSnippetProcessor.scala | 1 - .../scaladoc/tasty/comments/Comments.scala | 1 - .../tasty/comments/MarkdownParser.scala | 7 +++--- .../markdown/DocFlexmarkExtension.scala | 7 ++++-- .../markdown/SectionRenderingExtension.scala | 17 +++++++++---- .../markdown/SnippetRenderingExtension.scala | 5 +++- .../scaladoc/site/TemplateFileTests.scala | 7 +++--- .../scaladoc/snippets/SnippetsE2eTest.scala | 1 - 28 files changed, 69 insertions(+), 137 deletions(-) diff --git a/dist/bin/scaladoc b/dist/bin/scaladoc index 67210f0d6b4f..8b9ec41a7f8c 100755 --- a/dist/bin/scaladoc +++ b/dist/bin/scaladoc @@ -61,15 +61,31 @@ classpathArgs () { CLASS_PATH+="$(find_lib "*tasty-core*")$PSEP" CLASS_PATH+="$(find_lib "*scala3-tasty-inspector*")$PSEP" CLASS_PATH+="$(find_lib "*flexmark-0*")$PSEP" - CLASS_PATH+="$(find_lib "*flexmark-html-parser*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark*")$PSEP" CLASS_PATH+="$(find_lib "*flexmark-ext-anchorlink*")$PSEP" CLASS_PATH+="$(find_lib "*flexmark-ext-autolink*")$PSEP" CLASS_PATH+="$(find_lib "*flexmark-ext-emoji*")$PSEP" CLASS_PATH+="$(find_lib "*flexmark-ext-gfm-strikethrough*")$PSEP" - CLASS_PATH+="$(find_lib "*flexmark-ext-gfm-tables*")$PSEP" CLASS_PATH+="$(find_lib "*flexmark-ext-gfm-tasklist*")$PSEP" CLASS_PATH+="$(find_lib "*flexmark-ext-wikilink*")$PSEP" CLASS_PATH+="$(find_lib "*flexmark-ext-yaml-front-matter*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-ext-tables*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-ext-ins*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-ext-superscript*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-ast*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-data*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-dependency*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-misc*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-format*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-sequence*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-builder*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-collection*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-visitor*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-options*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-util-html*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-formatter*")$PSEP" + CLASS_PATH+="$(find_lib "*flexmark-ast*")$PSEP" CLASS_PATH+="$(find_lib "*liqp*")$PSEP" CLASS_PATH+="$(find_lib "*jsoup*")$PSEP" CLASS_PATH+="$(find_lib "*jackson-dataformat-yaml*")$PSEP" @@ -80,7 +96,6 @@ classpathArgs () { CLASS_PATH+="$(find_lib "*jline-reader*")$PSEP" CLASS_PATH+="$(find_lib "*jline-terminal-3*")$PSEP" CLASS_PATH+="$(find_lib "*jline-terminal-jna*")$PSEP" - CLASS_PATH+="$(find_lib "*flexmark-util*")$PSEP" CLASS_PATH+="$(find_lib "*flexmark-formatter*")$PSEP" CLASS_PATH+="$(find_lib "*autolink-0.6*")$PSEP" CLASS_PATH+="$(find_lib "*flexmark-jira-converter*")$PSEP" @@ -93,9 +108,6 @@ classpathArgs () { CLASS_PATH+="$(find_lib "*protobuf-java*")$PSEP" CLASS_PATH+="$(find_lib "*util-interface*")$PSEP" CLASS_PATH+="$(find_lib "*jna-5*")$PSEP" - CLASS_PATH+="$(find_lib "*flexmark-ext-tables*")$PSEP" - CLASS_PATH+="$(find_lib "*flexmark-ext-ins*")$PSEP" - CLASS_PATH+="$(find_lib "*flexmark-ext-superscript*")$PSEP" CLASS_PATH+="$(find_lib "*antlr4-runtime*")$PSEP" jvm_cp_args="-classpath \"$CLASS_PATH\"" diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 1dbf732a5b6e..1ac5090b7a6e 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -10,18 +10,21 @@ object Dependencies { val `jackson-dataformat-yaml` = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-yaml" % jacksonVersion - private val flexmarkVersion = "0.42.12" + // Freeze on 0.62.x as 0.64.0 requires Java 11 + private val flexmarkVersion = "0.62.2" val flexmarkDeps = Seq( "com.vladsch.flexmark" % "flexmark" % flexmarkVersion, - "com.vladsch.flexmark" % "flexmark-html-parser" % flexmarkVersion, + "com.vladsch.flexmark" % "flexmark-util-ast" % flexmarkVersion, + "com.vladsch.flexmark" % "flexmark-util-data" % flexmarkVersion, + "com.vladsch.flexmark" % "flexmark-util-html" % flexmarkVersion, "com.vladsch.flexmark" % "flexmark-ext-anchorlink" % flexmarkVersion, "com.vladsch.flexmark" % "flexmark-ext-autolink" % flexmarkVersion, "com.vladsch.flexmark" % "flexmark-ext-emoji" % flexmarkVersion, "com.vladsch.flexmark" % "flexmark-ext-gfm-strikethrough" % flexmarkVersion, - "com.vladsch.flexmark" % "flexmark-ext-gfm-tables" % flexmarkVersion, "com.vladsch.flexmark" % "flexmark-ext-gfm-tasklist" % flexmarkVersion, "com.vladsch.flexmark" % "flexmark-ext-wikilink" % flexmarkVersion, + "com.vladsch.flexmark" % "flexmark-ext-tables" % flexmarkVersion, "com.vladsch.flexmark" % "flexmark-ext-yaml-front-matter" % flexmarkVersion, ) diff --git a/scaladoc/src/dotty/tools/scaladoc/DocContext.scala b/scaladoc/src/dotty/tools/scaladoc/DocContext.scala index 7f208daff29a..acc93ccb332f 100644 --- a/scaladoc/src/dotty/tools/scaladoc/DocContext.scala +++ b/scaladoc/src/dotty/tools/scaladoc/DocContext.scala @@ -1,11 +1,9 @@ package dotty.tools.scaladoc import java.io.File -import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -import scala.jdk.CollectionConverters._ import dotty.tools.scaladoc.site.StaticSiteContext import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.util.SourceFile @@ -13,9 +11,6 @@ import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans import java.io.ByteArrayOutputStream import java.io.PrintStream -import scala.io.Codec -import java.net.URL -import scala.util.Try import scala.collection.mutable import dotty.tools.scaladoc.util.Check.checkJekyllIncompatPath diff --git a/scaladoc/src/dotty/tools/scaladoc/Main.scala b/scaladoc/src/dotty/tools/scaladoc/Main.scala index da35e63561fd..36b8b1daf4c4 100644 --- a/scaladoc/src/dotty/tools/scaladoc/Main.scala +++ b/scaladoc/src/dotty/tools/scaladoc/Main.scala @@ -1,16 +1,6 @@ package dotty.tools.scaladoc -import java.util.ServiceLoader -import java.io.File -import java.util.jar._ -import scala.jdk.CollectionConverters._ -import collection.immutable.ArraySeq - -import java.nio.file.Files - -import dotty.tools.dotc.config.Settings._ -import dotty.tools.dotc.config.CommonScalaSettings -import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Contexts.ContextBase /** Main class for the doctool when used from cli. */ class Main: diff --git a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala index da34e97efdf5..fa02e87548e6 100644 --- a/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala +++ b/scaladoc/src/dotty/tools/scaladoc/Scaladoc.scala @@ -1,20 +1,15 @@ package dotty.tools.scaladoc -import java.util.ServiceLoader import java.io.File import java.io.FileWriter -import java.util.jar._ -import scala.jdk.CollectionConverters._ -import collection.immutable.ArraySeq +import java.nio.file.Paths -import java.nio.file.{ Files, Paths } +import collection.immutable.ArraySeq import dotty.tools.dotc.config.Settings._ import dotty.tools.dotc.config.{ CommonScalaSettings, AllScalaSettings } import dotty.tools.dotc.reporting.Reporter import dotty.tools.dotc.core.Contexts._ - -import dotty.tools.scaladoc.Inkuire import dotty.tools.scaladoc.Inkuire._ object Scaladoc: diff --git a/scaladoc/src/dotty/tools/scaladoc/ScaladocCommand.scala b/scaladoc/src/dotty/tools/scaladoc/ScaladocCommand.scala index b91b8307208b..8b438a27f33e 100644 --- a/scaladoc/src/dotty/tools/scaladoc/ScaladocCommand.scala +++ b/scaladoc/src/dotty/tools/scaladoc/ScaladocCommand.scala @@ -1,20 +1,9 @@ package dotty.tools.scaladoc -import java.util.ServiceLoader -import java.io.File -import java.util.jar._ -import scala.jdk.CollectionConverters._ -import collection.immutable.ArraySeq - -import java.nio.file.Files - import dotty.tools.dotc.config.Settings._ -import dotty.tools.dotc.config.CommonScalaSettings -import dotty.tools.scaladoc.Scaladoc._ -import dotty.tools.dotc.config.Settings.Setting.value import dotty.tools.dotc.config.Properties._ import dotty.tools.dotc.config.CliCommand -import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Contexts.Context object ScaladocCommand extends CliCommand: type ConcreteSettings = ScaladocSettings diff --git a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala index ee7c6cd4980f..96e7854b45cf 100644 --- a/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala +++ b/scaladoc/src/dotty/tools/scaladoc/ScaladocSettings.scala @@ -1,20 +1,7 @@ package dotty.tools.scaladoc -import java.util.ServiceLoader -import java.io.File -import java.util.jar._ -import scala.jdk.CollectionConverters._ -import collection.immutable.ArraySeq - -import java.nio.file.Files - import dotty.tools.dotc.config.Settings._ import dotty.tools.dotc.config.AllScalaSettings -import dotty.tools.scaladoc.Scaladoc._ -import dotty.tools.dotc.config.Settings.Setting.value -import dotty.tools.dotc.config.Properties._ -import dotty.tools.dotc.config.CliCommand -import dotty.tools.dotc.core.Contexts._ class ScaladocSettings extends SettingGroup with AllScalaSettings: val unsupportedSettings = Seq( diff --git a/scaladoc/src/dotty/tools/scaladoc/SocialLinks.scala b/scaladoc/src/dotty/tools/scaladoc/SocialLinks.scala index f4fe674dbcb4..a07029d06c50 100644 --- a/scaladoc/src/dotty/tools/scaladoc/SocialLinks.scala +++ b/scaladoc/src/dotty/tools/scaladoc/SocialLinks.scala @@ -1,9 +1,5 @@ package dotty.tools.scaladoc -import java.nio.file.Path -import java.nio.file.Paths -import dotty.tools.dotc.core.Contexts.Context - enum SocialLinks(val url: String, val className: String): case Github(ghUrl: String) extends SocialLinks(ghUrl, "gh") case Twitter(tUrl: String) extends SocialLinks(tUrl, "twitter") diff --git a/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala b/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala index a9e300040fb8..b3732bcbc946 100644 --- a/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala +++ b/scaladoc/src/dotty/tools/scaladoc/SourceLinks.scala @@ -2,7 +2,6 @@ package dotty.tools.scaladoc import java.nio.file.Path import java.nio.file.Paths -import dotty.tools.dotc.core.Contexts.Context import scala.util.matching.Regex def pathToString(p: Path) = diff --git a/scaladoc/src/dotty/tools/scaladoc/compat.scala b/scaladoc/src/dotty/tools/scaladoc/compat.scala index fc660d97cb5d..d2095b9cc98c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/compat.scala +++ b/scaladoc/src/dotty/tools/scaladoc/compat.scala @@ -3,7 +3,6 @@ package dotty.tools.scaladoc import java.util.stream.Stream // comment out - wrong error! import java.util.stream.Collectors import java.util.Collections -import java.nio.file.Path import com.vladsch.flexmark.util.ast.{Node => MdNode} import dotty.tools.scaladoc.tasty.comments.wiki.WikiDocElement import scala.jdk.CollectionConverters._ @@ -37,4 +36,4 @@ extension [V](jlist: JList[V]) extension [V](jset: JSet[V]) def ++ (other: JSet[V]): JSet[V] = - Stream.of(jset, other).flatMap(_.stream).collect(Collectors.toSet()) \ No newline at end of file + Stream.of(jset, other).flatMap(_.stream).collect(Collectors.toSet()) diff --git a/scaladoc/src/dotty/tools/scaladoc/parsers/WikiCodeBlockParser.scala b/scaladoc/src/dotty/tools/scaladoc/parsers/WikiCodeBlockParser.scala index 865d78193886..4201cae4e2e6 100644 --- a/scaladoc/src/dotty/tools/scaladoc/parsers/WikiCodeBlockParser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/parsers/WikiCodeBlockParser.scala @@ -6,13 +6,14 @@ import com.vladsch.flexmark.parser.core._ import com.vladsch.flexmark.parser.block._ import com.vladsch.flexmark.util.ast.Block import com.vladsch.flexmark.util.ast.BlockContent -import com.vladsch.flexmark.util.options.DataHolder +import com.vladsch.flexmark.util.data.DataHolder import com.vladsch.flexmark.util.sequence.BasedSequence import com.vladsch.flexmark.util.sequence.SegmentedSequence import java.{util => ju} import ju.regex.Matcher import ju.regex.Pattern +import scala.jdk.CollectionConverters._ /** Copied from FencedCodeBlockParser. */ @@ -21,8 +22,11 @@ object WikiCodeBlockParser { private val CLOSING_FENCE = Pattern.compile("^(\\}{3})(?=[ \t]*$)$") class Factory extends CustomBlockParserFactory { + override def apply(options: DataHolder): BlockParserFactory = + new WikiCodeBlockParser.BlockFactory(options) + override def getAfterDependents = - new ju.HashSet[Class[_ <: CustomBlockParserFactory]](ju.Arrays.asList( + new ju.HashSet[Class[?]](ju.Arrays.asList( classOf[BlockQuoteParser.Factory], classOf[HeadingParser.Factory], //FencedCodeBlockParser.Factory.class, @@ -33,7 +37,7 @@ object WikiCodeBlockParser { )) override def getBeforeDependents = - new ju.HashSet[Class[_ <: CustomBlockParserFactory]](ju.Arrays.asList( + new ju.HashSet[Class[?]](ju.Arrays.asList( //BlockQuoteParser.Factory.class, //HeadingParser.Factory.class, //FencedCodeBlockParser.Factory.class, @@ -44,9 +48,6 @@ object WikiCodeBlockParser { )) override def affectsGlobalScope = false - - override def create(options: DataHolder) = - new WikiCodeBlockParser.BlockFactory(options) } private[WikiCodeBlockParser] class BlockFactory (val options: DataHolder) @@ -83,7 +84,7 @@ class WikiCodeBlockParser( final private val block = new FencedCodeBlock() private var content = new BlockContent - private val codeContentBlock = options.get(Parser.FENCED_CODE_CONTENT_BLOCK) + private val codeContentBlock = Parser.FENCED_CODE_CONTENT_BLOCK.get(options) def getBlock: Block = block def getFenceIndent: Int = fenceIndent @@ -141,7 +142,7 @@ class WikiCodeBlockParser( codeBlock.setCharsFromContent block.appendChild(codeBlock) } else { - val codeBlock = new Text(SegmentedSequence.of(segments)) + val codeBlock = new Text(SegmentedSequence.create(segments.asScala.toSeq:_*)) block.appendChild(codeBlock) } } diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala index cc0a0e197d26..93b86ce0bc51 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/HtmlRenderer.scala @@ -2,18 +2,9 @@ package dotty.tools.scaladoc package renderers import util.HTML._ -import scala.jdk.CollectionConverters._ -import java.net.URI -import java.net.URL import dotty.tools.scaladoc.site._ -import scala.util.Try import org.jsoup.Jsoup -import java.nio.file.Paths -import java.nio.file.Path import java.nio.file.Files -import java.nio.file.FileVisitOption -import java.io.File -import dotty.tools.scaladoc.staticFileSymbolUUID class HtmlRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx: DocContext) extends Renderer(rootPackage, members, extension = "html"): diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala index deb676e812c8..689234cdd29c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Locations.scala @@ -1,18 +1,10 @@ package dotty.tools.scaladoc package renderers -import util.HTML._ import scala.jdk.CollectionConverters._ import java.net.URI -import java.net.URL import dotty.tools.scaladoc.site._ -import scala.util.Try -import org.jsoup.Jsoup import java.nio.file.Paths -import java.nio.file.Path -import java.nio.file.Files -import java.io.File -import scala.util.matching._ import dotty.tools.scaladoc.util.Escape._ val UnresolvedLocationLink = "#" diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MarkdownRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MarkdownRenderer.scala index 12d41ac86218..6f20276e907e 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MarkdownRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MarkdownRenderer.scala @@ -2,17 +2,6 @@ package dotty.tools.scaladoc package renderers import util.HTML._ -import scala.jdk.CollectionConverters._ -import java.net.URI -import java.net.URL -import dotty.tools.scaladoc.site._ -import scala.util.Try -import org.jsoup.Jsoup -import java.nio.file.Paths -import java.nio.file.Path -import java.nio.file.Files -import java.nio.file.FileVisitOption -import java.io.File class MarkdownRenderer(rootPackage: Member, members: Map[DRI, Member])(using ctx: DocContext) extends Renderer(rootPackage, members, extension = "md"): diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index 47f6bb4b9728..996b422b44fd 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -7,9 +7,6 @@ import util.HTML.{div, *} import scala.jdk.CollectionConverters.* import dotty.tools.scaladoc.translators.FilterAttributes -import dotty.tools.scaladoc.tasty.comments.markdown.DocFlexmarkRenderer -import com.vladsch.flexmark.util.ast.Node as MdNode -import dotty.tools.scaladoc.tasty.comments.wiki.WikiDocElement import org.jsoup.Jsoup import translators.* diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala index dc2157131e0b..1a43ea8648a8 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Renderer.scala @@ -2,18 +2,11 @@ package dotty.tools.scaladoc package renderers import util.HTML._ -import scala.jdk.CollectionConverters._ import collection.mutable.ListBuffer -import java.net.URI -import java.net.URL import dotty.tools.scaladoc.site._ -import scala.util.Try -import org.jsoup.Jsoup import java.nio.file.Paths import java.nio.file.Path import java.nio.file.Files -import java.nio.file.FileVisitOption -import java.io.File case class Page(link: Link, content: Member | ResolvedTemplate | String, children: Seq[Page], hidden: Boolean = false): def withNewChildren(newChildren: Seq[Page]) = copy(children = children ++ newChildren) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala index 43b4440bce2c..27af3ac5dee6 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala @@ -2,16 +2,10 @@ package dotty.tools.scaladoc package renderers import util.HTML._ -import scala.jdk.CollectionConverters._ -import java.net.URI import java.net.URL -import dotty.tools.scaladoc.site._ -import scala.util.Try -import org.jsoup.Jsoup import java.nio.file.Paths import java.nio.file.Path import java.nio.file.Files -import java.io.File import dotty.tools.scaladoc.translators.FilterAttributes import util._ import translators._ @@ -190,7 +184,6 @@ trait Resources(using ctx: DocContext) extends Locations, Writer: def docPartRenderPlain(d: DocPart): String = import dotty.tools.scaladoc.tasty.comments.wiki._ - import com.vladsch.flexmark.util.ast.{Node => MdNode} def renderPlain(wd: WikiDocElement): String = wd match case Paragraph(text) => renderPlain(text) diff --git a/scaladoc/src/dotty/tools/scaladoc/site/FlexmarkSectionWrapper.scala b/scaladoc/src/dotty/tools/scaladoc/site/FlexmarkSectionWrapper.scala index 12e93505ab59..ec0bd241602a 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/FlexmarkSectionWrapper.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/FlexmarkSectionWrapper.scala @@ -4,7 +4,6 @@ package site import com.vladsch.flexmark.util.{ast => mdu, sequence} import com.vladsch.flexmark.{ast => mda} import com.vladsch.flexmark.formatter.Formatter -import com.vladsch.flexmark.util.options.MutableDataSet import scala.jdk.CollectionConverters._ import dotty.tools.scaladoc.tasty.comments.markdown.Section diff --git a/scaladoc/src/dotty/tools/scaladoc/site/common.scala b/scaladoc/src/dotty/tools/scaladoc/site/common.scala index 6c4852961fec..c0c959cf205c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/common.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/common.scala @@ -12,12 +12,13 @@ import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension import com.vladsch.flexmark.ext.tables.TablesExtension import com.vladsch.flexmark.ext.yaml.front.matter.{AbstractYamlFrontMatterVisitor, YamlFrontMatterExtension} import com.vladsch.flexmark.parser.{Parser, ParserEmulationProfile} -import com.vladsch.flexmark.util.options.{DataHolder, MutableDataSet} import com.vladsch.flexmark.ext.wikilink.WikiLinkExtension import com.vladsch.flexmark.formatter.Formatter import com.vladsch.flexmark.html.HtmlRenderer import scala.jdk.CollectionConverters._ +import com.vladsch.flexmark.util.data.DataHolder +import com.vladsch.flexmark.util.data.MutableDataSet val docsRootDRI: DRI = DRI(location = "_docs/index", symbolUUID = staticFileSymbolUUID) val apiPageDRI: DRI = DRI(location = "api/index") diff --git a/scaladoc/src/dotty/tools/scaladoc/site/templates.scala b/scaladoc/src/dotty/tools/scaladoc/site/templates.scala index fe51bbe0614d..92e0096e5af1 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/templates.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/templates.scala @@ -11,7 +11,6 @@ import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension import com.vladsch.flexmark.ext.tables.TablesExtension import com.vladsch.flexmark.ext.yaml.front.matter.{AbstractYamlFrontMatterVisitor, YamlFrontMatterExtension} import com.vladsch.flexmark.parser.{Parser, ParserEmulationProfile} -import com.vladsch.flexmark.util.options.{DataHolder, MutableDataSet} import com.vladsch.flexmark.html.HtmlRenderer import com.vladsch.flexmark.formatter.Formatter import liqp.Template diff --git a/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala b/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala index 69e7c7764985..33f0e089053a 100644 --- a/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala +++ b/scaladoc/src/dotty/tools/scaladoc/snippets/FlexmarkSnippetProcessor.scala @@ -4,7 +4,6 @@ package snippets import com.vladsch.flexmark.util.{ast => mdu, sequence} import com.vladsch.flexmark.{ast => mda} import com.vladsch.flexmark.formatter.Formatter -import com.vladsch.flexmark.util.options.MutableDataSet import scala.jdk.CollectionConverters._ import dotty.tools.scaladoc.tasty.comments.markdown.ExtendedFencedCodeBlock diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala index 66844f5049d3..ff4405d3ec71 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/Comments.scala @@ -7,7 +7,6 @@ import scala.util.Try import com.vladsch.flexmark.util.{ast => mdu, sequence} import com.vladsch.flexmark.{ast => mda} import com.vladsch.flexmark.formatter.Formatter -import com.vladsch.flexmark.util.options.MutableDataSet import com.vladsch.flexmark.util.sequence.BasedSequence import scala.quoted._ diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/MarkdownParser.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/MarkdownParser.scala index f5dd0ea88528..edf9051c0ed7 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/MarkdownParser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/MarkdownParser.scala @@ -9,7 +9,6 @@ import com.vladsch.flexmark.formatter.Formatter import com.vladsch.flexmark.parser.Parser import com.vladsch.flexmark.util.sequence.CharSubSequence import com.vladsch.flexmark.parser.ParserEmulationProfile -import com.vladsch.flexmark.ext.gfm.tables.TablesExtension import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension import com.vladsch.flexmark.ext.gfm.tasklist.TaskListExtension import com.vladsch.flexmark.ext.emoji.EmojiExtension @@ -17,10 +16,12 @@ import com.vladsch.flexmark.ext.autolink.AutolinkExtension import com.vladsch.flexmark.ext.anchorlink.AnchorLinkExtension import com.vladsch.flexmark.ext.yaml.front.matter.YamlFrontMatterExtension import com.vladsch.flexmark.ext.wikilink.WikiLinkExtension -import com.vladsch.flexmark.util.options.{ DataHolder, MutableDataSet } -import com.vladsch.flexmark.util.builder.Extension import scala.jdk.CollectionConverters._ +import com.vladsch.flexmark.util.misc.Extension +import com.vladsch.flexmark.ext.tables.TablesExtension +import com.vladsch.flexmark.util.data.MutableDataSet +import com.vladsch.flexmark.util.data.DataHolder object MarkdownParser { diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/DocFlexmarkExtension.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/DocFlexmarkExtension.scala index ad5533d634ad..d797eaed7fbf 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/DocFlexmarkExtension.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/DocFlexmarkExtension.scala @@ -15,6 +15,9 @@ import com.vladsch.flexmark._ import dotty.tools.scaladoc.snippets._ import scala.jdk.CollectionConverters._ +import com.vladsch.flexmark.util.data.MutableDataHolder +import com.vladsch.flexmark.util.data.DataHolder +import com.vladsch.flexmark.html.renderer.NodeRenderingHandler.CustomNodeRenderer class DocLinkNode( val target: DocLink, @@ -40,7 +43,7 @@ class DocFlexmarkParser(resolveLink: String => DocLink) extends Parser.ParserExt class Factory extends LinkRefProcessorFactory: override def getBracketNestingLevel(options: DataHolder) = 1 override def getWantExclamationPrefix(options: DataHolder) = false - override def create(doc: Document): LinkRefProcessor = + override def apply(doc: Document): LinkRefProcessor = new WikiLinkLinkRefProcessor(doc): override def createNode(nodeChars: BasedSequence): Node = val chars = nodeChars.toString.substring(2, nodeChars.length - 2) @@ -75,7 +78,7 @@ case class DocFlexmarkRenderer(renderLink: (DocLink, String) => String) ) object Factory extends NodeRendererFactory: - override def create(options: DataHolder): NodeRenderer = Render + override def apply(options: DataHolder): NodeRenderer = Render def extend(htmlRendererBuilder: HtmlRenderer.Builder, tpe: String): Unit = htmlRendererBuilder.nodeRendererFactory(Factory) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SectionRenderingExtension.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SectionRenderingExtension.scala index 1fa1a604c85a..421c7eaab76f 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SectionRenderingExtension.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SectionRenderingExtension.scala @@ -3,17 +3,23 @@ package tasty.comments.markdown import com.vladsch.flexmark.html.* import com.vladsch.flexmark.html.renderer.* +import com.vladsch.flexmark.html.renderer.NodeRenderingHandler.CustomNodeRenderer import com.vladsch.flexmark.parser.* import com.vladsch.flexmark.ext.wikilink.* import com.vladsch.flexmark.ext.wikilink.internal.WikiLinkLinkRefProcessor import com.vladsch.flexmark.util.ast.* import com.vladsch.flexmark.util.options.* import com.vladsch.flexmark.util.sequence.BasedSequence -import com.vladsch.flexmark.util.html.{AttributeImpl, Attributes} import com.vladsch.flexmark.* import com.vladsch.flexmark.ast.FencedCodeBlock import scala.collection.mutable +import com.vladsch.flexmark.util.data.MutableDataHolder +import com.vladsch.flexmark.util.html.Attributes +import com.vladsch.flexmark.util.html.AttributeImpl +import com.vladsch.flexmark.util.data.DataHolder +import com.vladsch.flexmark.util.html.Attribute +import com.vladsch.flexmark.util.html.MutableAttributes object SectionRenderingExtension extends HtmlRenderer.HtmlRendererExtension: @@ -30,18 +36,18 @@ object SectionRenderingExtension extends HtmlRenderer.HtmlRendererExtension: repeatedIds.update((c, header.getText), repeatedIds((c, header.getText)) + 1) val id = idGenerator.getId(header.getText.append(ifSuffixStr)) val anchor = AnchorLink(s"#$id") - val attributes = Attributes() val headerClass: String = header.getLevel match case 1 => "h500" case 2 => "h500" case 3 => "h400" case 4 => "h300" case _ => "h50" - attributes.addValue(AttributeImpl.of("class", headerClass)) + val attributes = MutableAttributes() + attributes.addValue("class", headerClass) val embeddedAttributes = EmbeddedAttributeProvider.EmbeddedNodeAttributes(header, attributes) header.prependChild(embeddedAttributes) header.prependChild(anchor) - html.attr(AttributeImpl.of("id", id)).withAttr.tag("section", false, false, () => { + html.attr("id", id).withAttr.tag("section", false, false, () => { c.render(header) body.foreach(c.render) }) @@ -59,7 +65,8 @@ object SectionRenderingExtension extends HtmlRenderer.HtmlRendererExtension: ) object Factory extends NodeRendererFactory: - override def create(options: DataHolder): NodeRenderer = Render + override def apply(options: DataHolder): NodeRenderer = Render + def extend(htmlRendererBuilder: HtmlRenderer.Builder, tpe: String): Unit = htmlRendererBuilder.nodeRendererFactory(Factory) diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SnippetRenderingExtension.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SnippetRenderingExtension.scala index e70b0883a31e..e980c5fc44ef 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SnippetRenderingExtension.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/comments/markdown/SnippetRenderingExtension.scala @@ -13,6 +13,9 @@ import com.vladsch.flexmark.util.options._ import com.vladsch.flexmark.util.sequence.BasedSequence import com.vladsch.flexmark._ import com.vladsch.flexmark.ast.FencedCodeBlock +import com.vladsch.flexmark.util.data.MutableDataHolder +import com.vladsch.flexmark.html.renderer.NodeRenderingHandler.CustomNodeRenderer +import com.vladsch.flexmark.util.data.DataHolder /** * SnippetRenderingExtension is responsible for running an analysis for scala codeblocks in the static documentation/scaladoc comments. @@ -39,7 +42,7 @@ object SnippetRenderingExtension extends HtmlRenderer.HtmlRendererExtension: ) object Factory extends NodeRendererFactory: - override def create(options: DataHolder): NodeRenderer = Render + override def apply(options: DataHolder): NodeRenderer = Render def extend(htmlRendererBuilder: HtmlRenderer.Builder, tpe: String): Unit = htmlRendererBuilder.nodeRendererFactory(Factory) diff --git a/scaladoc/test/dotty/tools/scaladoc/site/TemplateFileTests.scala b/scaladoc/test/dotty/tools/scaladoc/site/TemplateFileTests.scala index 203ab9cf5ed1..f07868ad4f44 100644 --- a/scaladoc/test/dotty/tools/scaladoc/site/TemplateFileTests.scala +++ b/scaladoc/test/dotty/tools/scaladoc/site/TemplateFileTests.scala @@ -202,6 +202,7 @@ class TemplateFileTests: content -> "md" ) ) + @Test def markdown(): Unit = testTemplate( @@ -222,10 +223,10 @@ class TemplateFileTests: ext = "md" ) { t => assertEquals( - """
      - |

      Hello there!

      + """
      + |

      Hello there2!

      |
      """.stripMargin, - t.resolveInner(RenderingContext(Map("msg" -> "there"))).code.trim()) + t.resolveInner(RenderingContext(Map("msg" -> "there2"))).code.trim()) } @Test diff --git a/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTest.scala b/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTest.scala index 1f28c938033d..616f7ae7f35e 100644 --- a/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/snippets/SnippetsE2eTest.scala @@ -13,7 +13,6 @@ import dotty.tools.dotc.reporting.{ Diagnostic, StoreReporter } import com.vladsch.flexmark.util.{ast => mdu, sequence} import com.vladsch.flexmark.{ast => mda} import com.vladsch.flexmark.formatter.Formatter -import com.vladsch.flexmark.util.options.MutableDataSet import scala.jdk.CollectionConverters._ import dotty.tools.scaladoc.tasty.comments.markdown.ExtendedFencedCodeBlock From d9fa877b02fede0447d0b3b94a9d369d2f3f6d93 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 28 Apr 2023 18:21:43 +0200 Subject: [PATCH 508/657] apply other-new-features/trait-parameters.md --- docs/_spec/05-classes-and-objects.md | 82 +++++++++++++++-- .../other-new-features/trait-parameters.md | 34 +++++++ .../other-new-features/trait-parameters.md | 88 ------------------- 3 files changed, 110 insertions(+), 94 deletions(-) create mode 100644 docs/_spec/APPLIEDreference/other-new-features/trait-parameters.md delete mode 100644 docs/_spec/TODOreference/other-new-features/trait-parameters.md diff --git a/docs/_spec/05-classes-and-objects.md b/docs/_spec/05-classes-and-objects.md index 5de7048ba00e..30e177e843ee 100644 --- a/docs/_spec/05-classes-and-objects.md +++ b/docs/_spec/05-classes-and-objects.md @@ -92,8 +92,10 @@ If this is not a template of a trait, then its _evaluation_ consists of the foll - First, the superclass constructor ´sc´ is [evaluated](#constructor-invocations). -- Then, all base classes in the template's [linearization](#class-linearization) up to the template's superclass denoted by ´sc´ are mixin-evaluated. -Mixin-evaluation happens in reverse order of occurrence in the linearization. +- Then, all base classes in the template's [linearization](#class-linearization) up to the template's superclass denoted by ´sc´ are evaluated. +evaluation happens in reverse order of occurrence in the linearization. Each evaluation occurs as follows: + - First, arguments to ´mt_i´ are evaluated from left to right, and set as parameters of ´mt_i´. + - ´mt_i´ is then mixin-evaluated. - Finally, the statement sequence ´\mathit{stats}\,´ is evaluated. ### Constructor Invocations @@ -662,13 +664,10 @@ This form of extensibility can be excluded by declaring the base class `Expr` `s ## Traits ```ebnf -TmplDef ::= ‘trait’ TraitDef -TraitDef ::= id [TypeParamClause] TraitTemplateOpt -TraitTemplateOpt ::= ‘extends’ TraitTemplate | [[‘extends’] TemplateBody] +TmplDef ::= ‘trait’ ClassDef ``` A _trait_ is a class that is meant to be added to some other class as a mixin. -Unlike normal classes, traits cannot have constructor parameters. Furthermore, no constructor arguments are passed to the superclass of the trait. This is not necessary as traits are initialized after the superclass is initialized. @@ -743,9 +742,80 @@ object MyTable extends ListTable[String, Int](0) with SynchronizedTable[String, The object `MyTable` inherits its `get` and `set` method from `SynchronizedTable`. The `super` calls in these methods are re-bound to refer to the corresponding implementations in `ListTable`, which is the actual supertype of `SynchronizedTable` in `MyTable`. +### Extending parameterized traits + +Extra rules apply for extending a trait with parameters: + +1. If a class `´C´` extends a parameterized trait `´T´`, and its superclass does not, `´C´` _must_ pass arguments to `´T´`. + +2. If a class `´C´` extends a parameterized trait `´T´`, and its superclass does as well, `´C´` _must not_ pass arguments to `´T´`. + +3. Traits must never pass arguments to parent traits. + +4. If a class `´C´` extends an unparameterized trait `´T_i´` and the base types of `´T_i´` include parameterized trait `´T_j´`, and the superclass of `´C´` does not extend `´T_j´`, then `´C´` _must_ also explicitly extend `´T_j´` and pass arguments. +This rule is relaxed if the missing trait contains only context parameters. In that case the trait reference is implicitly inserted as an additional parent with inferred arguments. + +###### Example - Preventing ambiguities + +The following listing tries to extend `Greeting` twice, with different parameters. + +```scala +trait Greeting(val name: String): + def msg = s"How are you, $name" + +class C extends Greeting("Bob") + +class D extends C, Greeting("Bill") // error + +@main def greet = println(D().msg) +``` + +Should this program print "Bob" or "Bill"? In fact this program is illegal, because it violates rule 2 above. +Instead, `D` can extend `Greeting` without passing arguments. + +###### Example - Overriding + +Here's a variant of `Greeting` that overrides `msg`: +```scala +trait FormalGreeting extends Greeting: + override def msg = s"How do you do, $name" +``` + +Due to rule 4, the following class extending `FormalGreeting` is required to also extend `Greeting` with arguments: +```scala +class GreetBobFormally extends FormalGreeting, Greeting("Bob") +``` + +###### Example - Inferred context parameters + +Here's a variant of `Greeting` where the addressee is a context parameter of type `ImpliedName`: + +```scala +trait ImpliedGreeting(using val iname: ImpliedName): + def msg = s"How are you, $iname" + +case class ImpliedName(name: String): + override def toString = name + +trait ImpliedFormalGreeting extends ImpliedGreeting: + override def msg = s"How do you do, $iname" + +class F(using iname: ImpliedName) extends ImpliedFormalGreeting +``` + +The definition of `F` in the last line is implicitly expanded to +```scala +class F(using iname: ImpliedName) extends + Object, // implicitly inserted + ImpliedGreeting(using iname), // implicitly inserted + ImpliedFormalGreeting +``` +Due to rule 4, `F` is required to also extend `ImpliedGreeting` and pass arguments to it, however note that because `ImpliedGreeting` has only context parameters the extension was added implicitly. + ## Object Definitions ```ebnf +TmplDef ::= ‘object’ ObjectDef ObjectDef ::= id ClassTemplate ``` diff --git a/docs/_spec/APPLIEDreference/other-new-features/trait-parameters.md b/docs/_spec/APPLIEDreference/other-new-features/trait-parameters.md new file mode 100644 index 000000000000..7924224ddc74 --- /dev/null +++ b/docs/_spec/APPLIEDreference/other-new-features/trait-parameters.md @@ -0,0 +1,34 @@ +--- +layout: doc-page +title: "Trait Parameters" +nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/trait-parameters.html +--- + +Scala 3 enables traits to have parameters, just like a class. + +For example, here is a trait `Greeting`: +```scala +trait Greeting(val name: String): + def msg = s"How are you, $name" +``` + +A class, enum, or object can extend `Greeting` as follows: + +```scala +class Greet extends Greeting("Bob"): + println(msg) +``` + +However if another trait extends `Greeting` then it must not pass arguments: + +```scala +trait FormalGreeting extends Greeting: + override def msg = s"How do you do, $name" +``` + +If you want a class to greet Bob formally, then you should extend both `FormalGreeting` and `Greeting`: + +```scala +class GreetFormally extends FormalGreeting, Greeting("Bob"): + println(msg) +``` diff --git a/docs/_spec/TODOreference/other-new-features/trait-parameters.md b/docs/_spec/TODOreference/other-new-features/trait-parameters.md deleted file mode 100644 index c704e73ce9b8..000000000000 --- a/docs/_spec/TODOreference/other-new-features/trait-parameters.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -layout: doc-page -title: "Trait Parameters" -nightlyOf: https://docs.scala-lang.org/scala3/reference/other-new-features/trait-parameters.html ---- - -Scala 3 allows traits to have parameters, just like classes have parameters. - -```scala -trait Greeting(val name: String): - def msg = s"How are you, $name" - -class C extends Greeting("Bob"): - println(msg) -``` - -Arguments to a trait are evaluated immediately before the trait is initialized. - -One potential issue with trait parameters is how to prevent -ambiguities. For instance, you might try to extend `Greeting` twice, -with different parameters. - -```scala -class D extends C, Greeting("Bill") // error: parameter passed twice -``` - -Should this print "Bob" or "Bill"? In fact this program is illegal, -because it violates the second rule of the following for trait parameters: - - 1. If a class `C` extends a parameterized trait `T`, and its superclass does not, `C` _must_ pass arguments to `T`. - - 2. If a class `C` extends a parameterized trait `T`, and its superclass does as well, `C` _must not_ pass arguments to `T`. - - 3. Traits must never pass arguments to parent traits. - -Here's a trait extending the parameterized trait `Greeting`. - -```scala -trait FormalGreeting extends Greeting: - override def msg = s"How do you do, $name" -``` -As is required, no arguments are passed to `Greeting`. However, this poses an issue -when defining a class that extends `FormalGreeting`: - -```scala -class E extends FormalGreeting // error: missing arguments for `Greeting`. -``` - -The correct way to write `E` is to extend both `Greeting` and -`FormalGreeting` (in either order): - -```scala -class E extends Greeting("Bob"), FormalGreeting -``` - -## Traits With Context Parameters - -This "explicit extension required" rule is relaxed if the missing trait contains only -[context parameters](../contextual/using-clauses.md). In that case the trait reference is -implicitly inserted as an additional parent with inferred arguments. For instance, -here's a variant of greetings where the addressee is a context parameter of type -`ImpliedName`: - -```scala -case class ImpliedName(name: String): - override def toString = name - -trait ImpliedGreeting(using val iname: ImpliedName): - def msg = s"How are you, $iname" - -trait ImpliedFormalGreeting extends ImpliedGreeting: - override def msg = s"How do you do, $iname" - -class F(using iname: ImpliedName) extends ImpliedFormalGreeting -``` - -The definition of `F` in the last line is implicitly expanded to -```scala -class F(using iname: ImpliedName) extends - Object, - ImpliedGreeting(using iname), - ImpliedFormalGreeting(using iname) -``` -Note the inserted reference to the super trait `ImpliedGreeting`, which was not mentioned explicitly. - -## Reference - -For more information, see [Scala SIP 25](http://docs.scala-lang.org/sips/pending/trait-parameters.html). From 1f12656e75819f549b40ee3a8306773b037cb355 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Tue, 2 May 2023 21:08:46 +0200 Subject: [PATCH 509/657] Fix logic in isCheckDefinitelyFalse --- .../tools/dotc/transform/TypeTestsCasts.scala | 11 ++++++++--- tests/neg-custom-args/isInstanceOf/i5826.scala | 18 +++++++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index ef0fefecd49e..979538b56fc5 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -157,8 +157,12 @@ object TypeTestsCasts { val xClass = effectiveClass(x) val pClass = effectiveClass(p) - !xClass.derivesFrom(pClass) - && (xClass.is(Final) || pClass.is(Final) || !xClass.is(Trait) && !pClass.is(Trait)) + val bothAreClasses = !xClass.is(Trait) && !pClass.is(Trait) + val notXsubP = !xClass.derivesFrom(pClass) + val notPsubX = !pClass.derivesFrom(xClass) + bothAreClasses && notXsubP && notPsubX + || xClass.is(Final) && notXsubP + || pClass.is(Final) && notPsubX else false } @@ -183,7 +187,8 @@ object TypeTestsCasts { val res1 = recur(tp1, P) val res2 = recur(tp2, P) - if res1.isEmpty && res2.isEmpty then res1 + if res1.isEmpty && res2.isEmpty then + res1 else if res2.isEmpty then if isCheckDefinitelyFalse(tp1, P) then res2 else res1 diff --git a/tests/neg-custom-args/isInstanceOf/i5826.scala b/tests/neg-custom-args/isInstanceOf/i5826.scala index e14142e974a8..c63bf3ab4aef 100644 --- a/tests/neg-custom-args/isInstanceOf/i5826.scala +++ b/tests/neg-custom-args/isInstanceOf/i5826.scala @@ -19,7 +19,23 @@ class Foo { } def test4[A](x: List[Int] | (A => Int)) = x match { - case ls: List[Int] => ls.head // ok, List decomposes to Some and None + case ls: List[Int] => ls.head // error, List extends Int => T + case _ => 0 + } + + final class C[T] extends A[T] + + def test5[T](x: A[T] | B[T] | Option[T]): Boolean = x.isInstanceOf[C[String]] // error + + def test6[T](x: A[T] | B[T] | Option[T]): Boolean = x.isInstanceOf[C[T]] + + def test7[A](x: Option[Int] | (A => Int)) = x match { + case ls: Option[Int] => ls.head // OK, Option decomposes to Some and None + case _ => 0 + } + + def test8(x: List[Int] | A[String]) = x match { + case ls: List[Int] => ls.head // OK, List decomposes to :: and Nil case _ => 0 } } From 3347eb4fd3c9e0c13131ff83166a0359fcd91ed1 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 29 Mar 2023 15:04:33 +0200 Subject: [PATCH 510/657] Feat: Add a blog configuration with yaml - Add input config, who allow to define the path import - Add output config, who allow to define the path destination - Add hidden, who allow to not generate the blog --- .../scaladoc/site/StaticSiteContext.scala | 4 +++ .../scaladoc/site/StaticSiteLoader.scala | 32 +++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala index de3f511c8e67..7a90a462cba0 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteContext.scala @@ -23,6 +23,10 @@ class StaticSiteContext( val docsPath = root.toPath.resolve("_docs") val blogPath = root.toPath.resolve("_blog") + def resolveNewBlogPath(stringPath: String): Path = + if stringPath.nonEmpty then root.toPath.resolve(stringPath) + else blogPath + def relativize(path: Path): Path = if args.apiSubdirectory then docsPath.relativize(path) diff --git a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala index c9ace108c9b2..8277cd2a5d01 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala @@ -113,11 +113,37 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite StaticSiteRoot(withBlog, mappings) } + var hiddenBlog = false + + def readYml: (Option[Boolean], Option[Boolean], Option[Boolean], String) = + val ymlPath = root.toPath.resolve("blog.yml") + if (Files.exists(ymlPath)) then + val yamlContent = Source.fromFile(ymlPath.toString).getLines().mkString("\n") + val hidden = if (yamlContent.contains("hidden: true")) Some(true) else None + val input = if (yamlContent.contains("input:")) Some(true) else None + val output = if (yamlContent.contains("output:")) Some(true) else None + (hidden, input, output, yamlContent) + else + (None, None, None, "") + def loadBlog(): Option[LoadedTemplate] = { + val (hidden, input, output, yamlContent) = readYml + val lines = yamlContent.split("\n") + val rootPath = input.collect { + case true => + lines.collectFirst { case line if line.contains("input:") => line.replaceFirst("input:", "").trim } + .map(ctx.resolveNewBlogPath) + .getOrElse(ctx.blogPath) + }.getOrElse(ctx.blogPath) + val defaultDirectory = output.collect { + case true => + lines + .collectFirst { case line if line.contains("output:") => line.replaceFirst("output:", "").trim } + .getOrElse("blog") + }.getOrElse("blog") + hidden.collect { case true => hiddenBlog = true } type Date = (String, String, String) - val rootPath = ctx.blogPath - val defaultDirectory = "blog" - if (!Files.exists(rootPath)) None + if (!Files.exists(rootPath) || hiddenBlog) None else { val indexPageOpt = Seq( rootPath.resolve("index.md"), From e243b322475c5d0f9bd682c0bfbb8eb6b6dec13b Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Tue, 25 Apr 2023 16:00:05 +0200 Subject: [PATCH 511/657] deps: update sbt version to 1.8.2 --- community-build/src/scala/dotty/communitybuild/projects.scala | 2 +- project/build.properties | 2 +- sbt-test/sbt-dotty/dotty-knowledge.i17/project/build.properties | 2 +- semanticdb/project/build.properties | 2 +- .../sourcepath-with-inline-api-hash/project/build.properties | 2 +- .../sourcepath-with-inline/project/build.properties | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/community-build/src/scala/dotty/communitybuild/projects.scala b/community-build/src/scala/dotty/communitybuild/projects.scala index fe3f5cfed5a2..1349c3adc3b9 100644 --- a/community-build/src/scala/dotty/communitybuild/projects.scala +++ b/community-build/src/scala/dotty/communitybuild/projects.scala @@ -140,7 +140,7 @@ final case class SbtCommunityProject( case Some(ivyHome) => List(s"-Dsbt.ivy.home=$ivyHome") case _ => Nil extraSbtArgs ++ sbtProps ++ List( - "-sbt-version", "1.8.0", + "-sbt-version", "1.8.2", "-Dsbt.supershell=false", s"-Ddotty.communitybuild.dir=$communitybuildDir", s"--addPluginSbtFile=$sbtPluginFilePath" diff --git a/project/build.properties b/project/build.properties index 8b9a0b0ab037..46e43a97ed86 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.0 +sbt.version=1.8.2 diff --git a/sbt-test/sbt-dotty/dotty-knowledge.i17/project/build.properties b/sbt-test/sbt-dotty/dotty-knowledge.i17/project/build.properties index 8b9a0b0ab037..46e43a97ed86 100644 --- a/sbt-test/sbt-dotty/dotty-knowledge.i17/project/build.properties +++ b/sbt-test/sbt-dotty/dotty-knowledge.i17/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.0 +sbt.version=1.8.2 diff --git a/semanticdb/project/build.properties b/semanticdb/project/build.properties index 8b9a0b0ab037..46e43a97ed86 100644 --- a/semanticdb/project/build.properties +++ b/semanticdb/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.0 +sbt.version=1.8.2 diff --git a/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties b/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties index 8b9a0b0ab037..46e43a97ed86 100644 --- a/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties +++ b/tests/cmdTest-sbt-tests/sourcepath-with-inline-api-hash/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.0 +sbt.version=1.8.2 diff --git a/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties b/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties index 8b9a0b0ab037..46e43a97ed86 100644 --- a/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties +++ b/tests/cmdTest-sbt-tests/sourcepath-with-inline/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.0 +sbt.version=1.8.2 From 0daeabed7c204bd883b7846ba3d98ca7375e39a6 Mon Sep 17 00:00:00 2001 From: Lucas Date: Wed, 3 May 2023 13:56:56 +0200 Subject: [PATCH 512/657] Fix: Refactor the function using jackson --- .../tools/scaladoc/site/BlogParser.scala | 29 +++++++++++++++ .../scaladoc/site/StaticSiteLoader.scala | 35 ++++--------------- 2 files changed, 35 insertions(+), 29 deletions(-) create mode 100644 scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala diff --git a/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala b/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala new file mode 100644 index 000000000000..6fd3d3db0c8d --- /dev/null +++ b/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala @@ -0,0 +1,29 @@ +package dotty.tools.scaladoc +package site + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.databind.DeserializationFeature +import java.io.File +import scala.beans._ + +case class BlogConfig( + @BeanProperty var input: String, + @BeanProperty var output: String, + @BooleanBeanProperty var hidden: Boolean +): + def this() = this(null, null, false) + +object BlogParser: + def readYml(root: File): BlogConfig = + val ymlFile = root.toPath + .resolve("blog.yml") + .toFile + + if ymlFile.exists then + val mapper = new ObjectMapper(new YAMLFactory()) + mapper.findAndRegisterModules(); + + val blogConfig: BlogConfig = mapper.readValue(ymlFile, classOf[BlogConfig]) + blogConfig + else new BlogConfig diff --git a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala index 8277cd2a5d01..489720cc5936 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/StaticSiteLoader.scala @@ -5,6 +5,7 @@ import java.io.File import java.nio.file.Files import java.nio.file.{ Paths, Path } import scala.io._ +import dotty.tools.scaladoc.site.BlogParser class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSiteContext, CompilerContext): val ctx: StaticSiteContext = summon[StaticSiteContext] @@ -113,37 +114,13 @@ class StaticSiteLoader(val root: File, val args: Scaladoc.Args)(using StaticSite StaticSiteRoot(withBlog, mappings) } - var hiddenBlog = false - - def readYml: (Option[Boolean], Option[Boolean], Option[Boolean], String) = - val ymlPath = root.toPath.resolve("blog.yml") - if (Files.exists(ymlPath)) then - val yamlContent = Source.fromFile(ymlPath.toString).getLines().mkString("\n") - val hidden = if (yamlContent.contains("hidden: true")) Some(true) else None - val input = if (yamlContent.contains("input:")) Some(true) else None - val output = if (yamlContent.contains("output:")) Some(true) else None - (hidden, input, output, yamlContent) - else - (None, None, None, "") - def loadBlog(): Option[LoadedTemplate] = { - val (hidden, input, output, yamlContent) = readYml - val lines = yamlContent.split("\n") - val rootPath = input.collect { - case true => - lines.collectFirst { case line if line.contains("input:") => line.replaceFirst("input:", "").trim } - .map(ctx.resolveNewBlogPath) - .getOrElse(ctx.blogPath) - }.getOrElse(ctx.blogPath) - val defaultDirectory = output.collect { - case true => - lines - .collectFirst { case line if line.contains("output:") => line.replaceFirst("output:", "").trim } - .getOrElse("blog") - }.getOrElse("blog") - hidden.collect { case true => hiddenBlog = true } + val blogConfig = BlogParser.readYml(root) + val rootPath = Option(blogConfig.input).map(input => ctx.resolveNewBlogPath(input)).getOrElse(ctx.blogPath) + val defaultDirectory = Option(blogConfig.output).getOrElse("blog") + type Date = (String, String, String) - if (!Files.exists(rootPath) || hiddenBlog) None + if (!Files.exists(rootPath) || blogConfig.hidden) None else { val indexPageOpt = Seq( rootPath.resolve("index.md"), From afbc53b3fc75a7494a00f4d392b776b5f7414ed7 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 3 May 2023 09:32:23 -0700 Subject: [PATCH 513/657] Avoid URL constructor deprecated on JDK20 --- .../dotty/tools/dotc/classpath/DirectoryClassPath.scala | 4 ++-- .../tools/dotc/classpath/VirtualDirectoryClassPath.scala | 8 +++----- compiler/src/dotty/tools/io/ClassPath.scala | 7 +++---- .../src/dotty/tools/repl/AbstractFileClassLoader.scala | 3 +++ project/DocumentationWebsite.scala | 3 ++- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala index 7f20d7c7d9ea..1411493bcbfd 100644 --- a/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/DirectoryClassPath.scala @@ -6,7 +6,7 @@ package dotty.tools.dotc.classpath import scala.language.unsafeNulls import java.io.{File => JFile} -import java.net.URL +import java.net.{URI, URL} import java.nio.file.{FileSystems, Files} import dotty.tools.dotc.classpath.PackageNameUtils.{packageContains, separatePkgAndClassNames} @@ -194,7 +194,7 @@ final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with No if (inPackage.isRoot) ClassPathEntries(packages(inPackage), Nil) else ClassPathEntries(packages(inPackage), classes(inPackage)) - def asURLs: Seq[URL] = Seq(new URL("jrt:/")) + def asURLs: Seq[URL] = Seq(new URI("jrt:/").toURL) // We don't yet have a scheme to represent the JDK modules in our `-classpath`. // java models them as entries in the new "module path", we'll probably need to follow this. def asClassPathStrings: Seq[String] = Nil diff --git a/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala b/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala index 0cb0ba59c52e..e750d9ccacc0 100644 --- a/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala +++ b/compiler/src/dotty/tools/dotc/classpath/VirtualDirectoryClassPath.scala @@ -2,12 +2,10 @@ package dotty.tools.dotc.classpath import scala.language.unsafeNulls -import dotty.tools.io.ClassRepresentation +import dotty.tools.io.{ClassPath, ClassRepresentation} import dotty.tools.io.{AbstractFile, VirtualDirectory} import FileUtils._ -import java.net.URL - -import dotty.tools.io.ClassPath +import java.net.{URI, URL} case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath with DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { type F = AbstractFile @@ -37,7 +35,7 @@ case class VirtualDirectoryClassPath(dir: VirtualDirectory) extends ClassPath wi def isPackage(f: AbstractFile): Boolean = f.isPackage // mimic the behavior of the old nsc.util.DirectoryClassPath - def asURLs: Seq[URL] = Seq(new URL(dir.name)) + def asURLs: Seq[URL] = Seq(new URI(dir.name).toURL) def asClassPathStrings: Seq[String] = Seq(dir.path) override def findClass(className: String): Option[ClassRepresentation] = findClassFile(className) map ClassFileEntryImpl.apply diff --git a/compiler/src/dotty/tools/io/ClassPath.scala b/compiler/src/dotty/tools/io/ClassPath.scala index 754c2bae3597..b45de57f9850 100644 --- a/compiler/src/dotty/tools/io/ClassPath.scala +++ b/compiler/src/dotty/tools/io/ClassPath.scala @@ -9,8 +9,7 @@ package io import scala.language.unsafeNulls -import java.net.MalformedURLException -import java.net.URL +import java.net.{MalformedURLException, URI, URISyntaxException, URL} import java.util.regex.PatternSyntaxException import File.pathSeparator @@ -182,8 +181,8 @@ object ClassPath { } def specToURL(spec: String): Option[URL] = - try Some(new URL(spec)) - catch { case _: MalformedURLException => None } + try Some(new URI(spec).toURL) + catch case _: MalformedURLException | _: URISyntaxException => None def manifests: List[java.net.URL] = { import scala.jdk.CollectionConverters.EnumerationHasAsScala diff --git a/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala b/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala index 89fd290f7286..7a457a1d7546 100644 --- a/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala +++ b/compiler/src/dotty/tools/repl/AbstractFileClassLoader.scala @@ -23,6 +23,9 @@ import java.util.Collections class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) extends ClassLoader(parent): private def findAbstractFile(name: String) = root.lookupPath(name.split('/').toIndexedSeq, directory = false) + // on JDK 20 the URL constructor we're using is deprecated, + // but the recommended replacement, URL.of, doesn't exist on JDK 8 + @annotation.nowarn("cat=deprecation") override protected def findResource(name: String) = findAbstractFile(name) match case null => null diff --git a/project/DocumentationWebsite.scala b/project/DocumentationWebsite.scala index e24917a60803..5f8e499af62f 100644 --- a/project/DocumentationWebsite.scala +++ b/project/DocumentationWebsite.scala @@ -1,4 +1,5 @@ import java.io.File +import java.net.URI import java.nio.file.Paths import sbt._ import Build._ @@ -48,7 +49,7 @@ object DocumentationWebsite { sbt.IO.touch(inkuireDestinationFile) def tryFetch(retries: Int, timeout: Duration): Unit = { - val downloadProcess = (new java.net.URL(inkuireLink) #> inkuireDestinationFile).run() + val downloadProcess = (new URI(inkuireLink).toURL #> inkuireDestinationFile).run() val result: Future[Int] = Future(blocking(downloadProcess.exitValue())) try { Await.result(result, timeout) match { From 0975d29fed6cef938513c332ea2d4ec054ee2d7a Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 29 Apr 2023 09:33:56 -0700 Subject: [PATCH 514/657] Retry waiting for javac If running a single test, the pool shutdown is likely to subvert `waitFor` the javac process. Catch the `InterruptedException` and try again with a timed wait of generous but finite duration, to accommodate testing by developers. This quick fix does not correct the race, which presumably does not matter because of the order in which tests are ordinarily submitted. --- .../dotty/tools/vulpix/ParallelTesting.scala | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 3799a2335a78..bccbcbee29e1 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -12,7 +12,7 @@ import java.nio.file.{Files, NoSuchFileException, Path, Paths} import java.nio.charset.{Charset, StandardCharsets} import java.text.SimpleDateFormat import java.util.{HashMap, Timer, TimerTask} -import java.util.concurrent.{TimeUnit, TimeoutException, Executors => JExecutors} +import java.util.concurrent.{ExecutionException, TimeUnit, TimeoutException, Executors => JExecutors} import scala.collection.mutable import scala.io.{Codec, Source} @@ -494,6 +494,12 @@ trait ParallelTesting extends RunnerOrchestration { self => .and("-d", targetDir.getPath) .withClasspath(targetDir.getPath) + def waitForJudiciously(process: Process): Int = + try process.waitFor() + catch case _: InterruptedException => + try if process.waitFor(5L, TimeUnit.MINUTES) then process.exitValue() else -2 + finally Thread.currentThread.interrupt() + def compileWithJavac(fs: Array[String]) = if (fs.nonEmpty) { val fullArgs = Array( "javac", @@ -503,7 +509,7 @@ trait ParallelTesting extends RunnerOrchestration { self => val process = Runtime.getRuntime.exec(fullArgs) val output = Source.fromInputStream(process.getErrorStream).mkString - if (process.waitFor() != 0) Some(output) + if waitForJudiciously(process) != 0 then Some(output) else None } else None @@ -676,7 +682,11 @@ trait ParallelTesting extends RunnerOrchestration { self => for fut <- eventualResults do try fut.get() - catch case ex: Exception => + catch + case ee: ExecutionException if ee.getCause.isInstanceOf[InterruptedException] => + System.err.println("Interrupted (probably running after shutdown)") + ee.printStackTrace() + case ex: Exception => System.err.println(ex.getMessage) ex.printStackTrace() From f4c49017d7d1a663ba4da6e016793c67d1e0bf36 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 May 2023 08:20:40 +0200 Subject: [PATCH 515/657] Make arguments order in quote hole deterministic --- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 4b288349fc33..71bcaa4fcec0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -177,7 +177,7 @@ class Splicing extends MacroTransform: * ``` */ private class SpliceTransformer(spliceOwner: Symbol, isCaptured: Symbol => Boolean) extends Transformer: - private var refBindingMap = mutable.Map.empty[Symbol, (Tree, Symbol)] + private var refBindingMap = mutable.LinkedHashMap.empty[Symbol, (Tree, Symbol)] /** Reference to the `Quotes` instance of the current level 1 splice */ private var quotes: Tree | Null = null // TODO: add to the context private var healedTypes: QuoteTypeTags | Null = null // TODO: add to the context From 82236ae16ef774dde7d87ced576cdb4335541b0e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 May 2023 10:09:14 +0200 Subject: [PATCH 516/657] Do not remove inline method implementations until PruneErasedDefs --- .../src/dotty/tools/dotc/staging/CrossStageSafety.scala | 5 +++-- .../src/dotty/tools/dotc/transform/FirstTransform.scala | 1 + compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala | 4 +--- compiler/src/dotty/tools/dotc/transform/Staging.scala | 6 ++++-- .../dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala | 2 ++ tests/pos-macros/macro-deprecation.scala | 4 ++++ tests/pos-macros/macro-experimental.scala | 5 +++++ 7 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 tests/pos-macros/macro-deprecation.scala create mode 100644 tests/pos-macros/macro-experimental.scala diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index 032067eca482..feb93c3c6319 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -90,6 +90,9 @@ class CrossStageSafety extends TreeMapWithStages { if level != 0 then cpy.Apply(tree)(cpy.TypeApply(tree.fun)(fun, transformedBody :: Nil), quotes :: Nil) else tpd.Quote(transformedBody).select(nme.apply).appliedTo(quotes).withSpan(tree.span) + case _: DefDef if tree.symbol.isInlineMethod => + tree + case _ if !inQuoteOrSpliceScope => checkAnnotations(tree) // Check quotes in annotations super.transform(tree) @@ -117,8 +120,6 @@ class CrossStageSafety extends TreeMapWithStages { // propagate healed types tree1.withType(tree1.tpt.tpe.appliedTo(tree1.args.map(_.tpe))) case tree1 => tree1 - case tree: DefDef if tree.symbol.is(Inline) && level > 0 => - EmptyTree // Remove inline defs in quoted code. Already fully inlined. case tree: ValOrDefDef => checkAnnotations(tree) healInfo(tree, tree.tpt.srcPos) diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index a7e0795ce195..03639c8af689 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -18,6 +18,7 @@ import NameKinds.OuterSelectName import StdNames._ import TypeUtils.isErasedValueType import config.Feature +import inlines.Inlines.inInlineMethod object FirstTransform { val name: String = "firstTransform" diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 0c3a94e7d9cd..581b320ed9df 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -103,9 +103,7 @@ class PickleQuotes extends MacroTransform { val pickled = PickleQuotes.pickle(quote1, quotes, contents) transform(pickled) // pickle quotes that are in the contents case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod => - // Shrink size of the tree. The methods have already been inlined. - // TODO move to FirstTransform to trigger even without quotes - cpy.DefDef(tree)(rhs = defaultValue(tree.rhs.tpe)) + tree case _ => super.transform(tree) } diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 581fd2753504..35a67e670457 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -8,6 +8,7 @@ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.inlines.Inlines import dotty.tools.dotc.util.SrcPos import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.staging.StagingLevel.* @@ -56,7 +57,8 @@ class Staging extends MacroTransform { checker.transform(tree) case _ => } - + } + if !Inlines.inInlineMethod then tree match { case tree: RefTree => assert(level != 0 || tree.symbol != defn.QuotedTypeModule_of, @@ -72,7 +74,7 @@ class Staging extends MacroTransform { case _ => // OK } - } + end checkPostCondition override def run(using Context): Unit = if (ctx.compilationUnit.needsStaging) super.run diff --git a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala index 8c8f0079e868..2a665c478932 100644 --- a/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/BootstrappedOnlyCompilationTests.scala @@ -110,6 +110,8 @@ class BootstrappedOnlyCompilationTests { aggregateTests( compileFilesInDir("tests/neg-macros", defaultOptions.and("-Xcheck-macros")), compileFile("tests/pos-macros/i9570.scala", defaultOptions.and("-Xfatal-warnings")), + compileFile("tests/pos-macros/macro-deprecation.scala", defaultOptions.and("-Xfatal-warnings", "-deprecation")), + compileFile("tests/pos-macros/macro-experimental.scala", defaultOptions.and("-Yno-experimental")), ).checkExpectedErrors() } diff --git a/tests/pos-macros/macro-deprecation.scala b/tests/pos-macros/macro-deprecation.scala new file mode 100644 index 000000000000..ff14f96ac7fa --- /dev/null +++ b/tests/pos-macros/macro-deprecation.scala @@ -0,0 +1,4 @@ +import scala.quoted.* + +inline def f = ${ impl } // error +@deprecated def impl(using Quotes) = '{1} diff --git a/tests/pos-macros/macro-experimental.scala b/tests/pos-macros/macro-experimental.scala new file mode 100644 index 000000000000..dc011f4e45b9 --- /dev/null +++ b/tests/pos-macros/macro-experimental.scala @@ -0,0 +1,5 @@ +import scala.quoted.* +import scala.annotation.experimental + +inline def f = ${ impl } // error +@experimental def impl(using Quotes) = '{1} From d02ecd02b7acde957600649ad41a1a1b33572f4d Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Thu, 4 May 2023 13:42:32 +0200 Subject: [PATCH 517/657] test: add in a check file for rewrite issue (#17410) This adds in a test to ensure that the issue reported in #17399 is indeed fixed with #12954. This case is a bit different, so best to also just have it covered with a test. Closes #17399 --- compiler/test/dotty/tools/dotc/CompilationTests.scala | 1 + tests/rewrites/i17399.check | 7 +++++++ tests/rewrites/i17399.scala | 5 +++++ 3 files changed, 13 insertions(+) create mode 100644 tests/rewrites/i17399.check create mode 100644 tests/rewrites/i17399.scala diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index d75afc330f5f..c782c78f8eb7 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -82,6 +82,7 @@ class CompilationTests { compileFile("tests/rewrites/i11895.scala", defaultOptions.and("-indent", "-rewrite")), compileFile("tests/rewrites/i12340.scala", unindentOptions.and("-rewrite")), compileFile("tests/rewrites/i17187.scala", unindentOptions.and("-rewrite")), + compileFile("tests/rewrites/i17399.scala", unindentOptions.and("-rewrite")), ).checkRewrites() } diff --git a/tests/rewrites/i17399.check b/tests/rewrites/i17399.check new file mode 100644 index 000000000000..0b7eef78db4f --- /dev/null +++ b/tests/rewrites/i17399.check @@ -0,0 +1,7 @@ +def test[T](body:T):T = body + +object Test { + test { + println("test 1") + } +} diff --git a/tests/rewrites/i17399.scala b/tests/rewrites/i17399.scala new file mode 100644 index 000000000000..965d17c4f718 --- /dev/null +++ b/tests/rewrites/i17399.scala @@ -0,0 +1,5 @@ +def test[T](body:T):T = body + +object Test: + test: + println("test 1") From 23409794d310a41be7eba4fa5060afa5f9c65f87 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 May 2023 17:28:26 +0200 Subject: [PATCH 518/657] Add regression test Closes #17409 --- tests/pos-macros/i17409.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/pos-macros/i17409.scala diff --git a/tests/pos-macros/i17409.scala b/tests/pos-macros/i17409.scala new file mode 100644 index 000000000000..449e0576d84b --- /dev/null +++ b/tests/pos-macros/i17409.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +transparent inline def thing = + ${ thingImpl } + +def thingImpl(using Quotes): Expr[Any] = + '{ + def makeThing: { def me: this.type } = ??? + makeThing + } From e434e5f4d94816028581d0262edb0d169e9d3c9d Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 30 Apr 2023 11:11:02 +0200 Subject: [PATCH 519/657] Allow new capture set syntax - `^{xs}` in postfix - `->{xs}` after arrow - `any` instead of `*` --- .../src/dotty/tools/dotc/ast/Desugar.scala | 8 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 14 ++ .../src/dotty/tools/dotc/core/StdNames.scala | 2 + .../dotty/tools/dotc/parsing/Parsers.scala | 24 ++- .../tools/dotc/printing/RefinedPrinter.scala | 2 + .../src/dotty/tools/dotc/typer/Typer.scala | 16 +- tests/pos-custom-args/captures/byname.scala | 4 +- .../pos-custom-args/captures/capt-test.scala | 8 +- tests/pos-custom-args/captures/capt1.scala | 16 +- tests/pos-custom-args/captures/classes.scala | 18 +- .../colltest5/CollectionStrawManCC5_1.scala | 170 +++++++++--------- .../captures/colltest5/Test_2.scala | 26 +-- 12 files changed, 174 insertions(+), 134 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index d6f9e0d24c91..e0cba60950fa 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1827,13 +1827,11 @@ object desugar { case CapturingTypeTree(refs, parent) => // convert `{refs} T` to `T @retains refs` // `{refs}-> T` to `-> (T @retainsByName refs)` - def annotate(annotName: TypeName, tp: Tree) = - Annotated(tp, New(scalaAnnotationDot(annotName), List(refs))) parent match case ByNameTypeTree(restpt) => - cpy.ByNameTypeTree(parent)(annotate(tpnme.retainsByName, restpt)) + cpy.ByNameTypeTree(parent)(makeRetaining(restpt, refs, tpnme.retainsByName)) case _ => - annotate(tpnme.retains, parent) + makeRetaining(parent, refs, tpnme.retains) case f: FunctionWithMods if f.hasErasedParams => makeFunctionWithValDefs(f, pt) } desugared.withSpan(tree.span) @@ -1927,7 +1925,7 @@ object desugar { } tree match case tree: FunctionWithMods => - untpd.FunctionWithMods(applyVParams, tree.body, tree.mods, tree.erasedParams) + untpd.FunctionWithMods(applyVParams, result, tree.mods, tree.erasedParams) case _ => untpd.Function(applyVParams, result) } } diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 30f58fba44ec..910b0f84758a 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -150,6 +150,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { /** {x1, ..., xN} T (only relevant under captureChecking) */ case class CapturingTypeTree(refs: List[Tree], parent: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree + /** {x1, ..., xN} T (only relevant under captureChecking) */ + case class CapturesAndResult(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 @@ -501,6 +504,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def captureRoot(using Context): Select = Select(scalaDot(nme.caps), nme.CAPTURE_ROOT) + def makeRetaining(parent: Tree, refs: List[Tree], annotName: TypeName)(using Context): Annotated = + Annotated(parent, New(scalaAnnotationDot(annotName), List(refs))) + def makeConstructor(tparams: List[TypeDef], vparamss: List[List[ValDef]], rhs: Tree = EmptyTree)(using Context): DefDef = DefDef(nme.CONSTRUCTOR, joinParams(tparams, vparamss), TypeTree(), rhs) @@ -658,6 +664,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 CapturesAndResult(tree: Tree)(refs: List[Tree], parent: Tree)(using Context): Tree = tree match + case tree: CapturesAndResult if (refs eq tree.refs) && (parent eq tree.parent) => tree + case _ => finalize(tree, untpd.CapturesAndResult(refs, parent)) + 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)) @@ -723,6 +733,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { tree case MacroTree(expr) => cpy.MacroTree(tree)(transform(expr)) + case CapturesAndResult(refs, parent) => + cpy.CapturesAndResult(tree)(transform(refs), transform(parent)) case CapturingTypeTree(refs, parent) => cpy.CapturingTypeTree(tree)(transform(refs), transform(parent)) case _ => @@ -782,6 +794,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(x, splice) case MacroTree(expr) => this(x, expr) + case CapturesAndResult(refs, parent) => + this(this(x, refs), parent) case CapturingTypeTree(refs, parent) => this(this(x, refs), parent) case _ => diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 27e97a92b48e..e5ca0710447d 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -287,6 +287,7 @@ object StdNames { // Compiler-internal val CAPTURE_ROOT: N = "*" + val CAPTURE_ROOT_ALT: N = "any" val CONSTRUCTOR: N = "" val STATIC_CONSTRUCTOR: N = "" val EVT2U: N = "evt2u$" @@ -301,6 +302,7 @@ object StdNames { val THROWS: N = "$throws" val U2EVT: N = "u2evt$" val ALLARGS: N = "$allArgs" + val UPARROW: N = "^" final val Nil: N = "Nil" final val Predef: N = "Predef" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 7fecdd2c5eae..c36e2238662f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1467,6 +1467,7 @@ object Parsers { if in.token == THIS then simpleRef() else termIdent() match case Ident(nme.CAPTURE_ROOT) => captureRoot + case Ident(nme.CAPTURE_ROOT_ALT) => captureRoot case id => id /** CaptureSet ::= `{` CaptureRef {`,` CaptureRef} `}` -- under captureChecking @@ -1475,6 +1476,11 @@ object Parsers { if in.token == RBRACE then Nil else commaSeparated(captureRef) } + def capturesAndResult(core: () => Tree): Tree = + if in.token == LBRACE && in.offset == in.lastOffset + then CapturesAndResult(captureSet(), core()) + else core() + /** Type ::= FunType * | HkTypeParamClause ‘=>>’ Type * | FunParamClause ‘=>>’ Type @@ -1519,7 +1525,7 @@ object Parsers { else accept(ARROW) - val resultType = typ() + val resultType = capturesAndResult(typ) if token == TLARROW then for case ValDef(_, tpt, _) <- params do if isByNameType(tpt) then @@ -1690,7 +1696,7 @@ object Parsers { infixOps(t, canStartInfixTypeTokens, refinedTypeFn, Location.ElseWhere, ParseKind.Type, isOperator = !followingIsVararg() && !isPureArrow) - /** RefinedType ::= WithType {[nl] Refinement} + /** RefinedType ::= WithType {[nl] (Refinement} [`^` CaptureSet] */ val refinedTypeFn: Location => Tree = _ => refinedType() @@ -1698,11 +1704,19 @@ object Parsers { def refinedTypeRest(t: Tree): Tree = { argumentStart() - if (in.isNestedStart) + if in.isNestedStart then refinedTypeRest(atSpan(startOffset(t)) { RefinedTypeTree(rejectWildcardType(t), refinement(indentOK = true)) }) - else t + else if in.isIdent(nme.UPARROW) then + val upArrowStart = in.offset + in.nextToken() + def cs = + if in.token == LBRACE then captureSet() + else atSpan(upArrowStart)(captureRoot) :: Nil + makeRetaining(t, cs, tpnme.retains) + else + t } /** WithType ::= AnnotType {`with' AnnotType} (deprecated) @@ -1929,7 +1943,7 @@ object Parsers { def paramTypeOf(core: () => Tree): Tree = if in.token == ARROW || isPureArrow(nme.PUREARROW) then val isImpure = in.token == ARROW - val tp = atSpan(in.skipToken()) { ByNameTypeTree(core()) } + val tp = atSpan(in.skipToken()) { ByNameTypeTree(capturesAndResult(core)) } if isImpure && Feature.pureFunsEnabled then ImpureByNameTypeTree(tp) else tp else if in.token == LBRACE && followingIsCaptureSet() then val start = in.offset diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 93de33778750..34374fe24998 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -730,6 +730,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val contentText = toTextGlobal(content) val tptText = toTextGlobal(tpt) prefix ~~ idx.toString ~~ "|" ~~ tptText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix + case CapturesAndResult(refs, parent) => + changePrec(GlobalPrec)("^{" ~ Text(refs.map(toText), ", ") ~ "} " ~ toText(parent)) case CapturingTypeTree(refs, parent) => parent match case ImpureByNameTypeTree(bntpt) => diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 7eb8519739c6..9f13071ae4b7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1389,6 +1389,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def typedFunctionType(tree: untpd.Function, pt: Type)(using Context): Tree = { val untpd.Function(args, body) = tree + body match + case untpd.CapturesAndResult(refs, result) => + return typedUnadapted(untpd.makeRetaining( + cpy.Function(tree)(args, result), refs, tpnme.retains), pt) + case _ => var (funFlags, erasedParams) = tree match { case tree: untpd.FunctionWithMods => (tree.mods.flags, tree.erasedParams) case _ => (EmptyFlags, args.map(_ => false)) @@ -2274,10 +2279,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer assignType(cpy.MatchTypeTree(tree)(bound1, sel1, cases1), bound1, sel1, cases1) } - def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(using Context): ByNameTypeTree = { - val result1 = typed(tree.result) - assignType(cpy.ByNameTypeTree(tree)(result1), result1) - } + def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(using Context): ByNameTypeTree = tree.result match + case untpd.CapturesAndResult(refs, tpe) => + typedByNameTypeTree( + cpy.ByNameTypeTree(tree)(untpd.makeRetaining(tpe, refs, tpnme.retainsByName))) + case _ => + val result1 = typed(tree.result) + assignType(cpy.ByNameTypeTree(tree)(result1), result1) def typedTypeBoundsTree(tree: untpd.TypeBoundsTree, pt: Type)(using Context): Tree = val TypeBoundsTree(lo, hi, alias) = tree diff --git a/tests/pos-custom-args/captures/byname.scala b/tests/pos-custom-args/captures/byname.scala index 35b8876d0058..e54a67660172 100644 --- a/tests/pos-custom-args/captures/byname.scala +++ b/tests/pos-custom-args/captures/byname.scala @@ -4,9 +4,9 @@ type Cap = {*} CC class I -def test(cap1: Cap, cap2: Cap): {cap1} I = +def test(cap1: Cap, cap2: Cap): I{ref cap1} = def f() = if cap1 == cap1 then I() else I() - def h(x: {cap1}-> I) = x + def h(x: ->{ref any} I) = x h(f()) // OK def hh(x: -> I @retainsByName(cap1)) = x h(f()) diff --git a/tests/pos-custom-args/captures/capt-test.scala b/tests/pos-custom-args/captures/capt-test.scala index c61577e96eb1..081dbd8a3aa1 100644 --- a/tests/pos-custom-args/captures/capt-test.scala +++ b/tests/pos-custom-args/captures/capt-test.scala @@ -19,10 +19,10 @@ def map[A, B](f: A => B)(xs: LIST[A]): LIST[B] = xs.map(f) class C -type Cap = {*} C +type Cap = C{ref any} class Foo(x: Cap): - this: {x} Foo => + this: Foo{ref x} => def test(c: Cap, d: Cap) = def f(x: Cap): Unit = if c == x then () @@ -32,7 +32,7 @@ def test(c: Cap, d: Cap) = val zs = val z = g CONS(z, ys) - val zsc: LIST[{d, y} Cap -> Unit] = zs + val zsc: LIST[Cap ->{ref d, y} Unit] = zs val a4 = zs.map(identity) - val a4c: LIST[{d, y} Cap -> Unit] = a4 + val a4c: LIST[Cap ->{ref d, y} Unit] = a4 diff --git a/tests/pos-custom-args/captures/capt1.scala b/tests/pos-custom-args/captures/capt1.scala index cc39790623d4..865b382f4e9f 100644 --- a/tests/pos-custom-args/captures/capt1.scala +++ b/tests/pos-custom-args/captures/capt1.scala @@ -1,9 +1,9 @@ class C -type Cap = {*} C -def f1(c: Cap): {c} () -> c.type = () => c // ok +type Cap = C^ +def f1(c: Cap): () ->{c} c.type = () => c // ok def f2: Int = - val g: {*} Boolean -> Int = ??? + val g: Boolean ->{any} Int = ??? val x = g(true) x @@ -13,11 +13,11 @@ def f3: Int = val x = g.apply(true) x -def foo(): {*} C = - val x: {*} C = ??? - val y: {x} C = x - val x2: {x} () -> C = ??? - val y2: {x} () -> {x} C = x2 +def foo(): C^ = + val x: C^ = ??? + val y: C^{x} = x + val x2: () ->{x} C = ??? + val y2: () ->{x} C^{x} = x2 val z1: () => Cap = f1(x) def h[X](a: X)(b: X) = a diff --git a/tests/pos-custom-args/captures/classes.scala b/tests/pos-custom-args/captures/classes.scala index f14a7e6dd84e..80971512e25e 100644 --- a/tests/pos-custom-args/captures/classes.scala +++ b/tests/pos-custom-args/captures/classes.scala @@ -1,22 +1,22 @@ import annotation.retains class B -type Cap = {*} B +type Cap = B{ref any} class C(val n: Cap): - this: {n} C => - def foo(): {n} B = n + this: C{ref n} => + def foo(): B{ref n} = n def test(x: Cap, y: Cap, z: Cap) = val c0 = C(x) - val c1: {x} C {val n: {x} B} = c0 + val c1: C{ref x}{val n: B{ref x}} = c0 val d = c1.foo() - d: {x} B + d: B{ref x} val c2 = if ??? then C(x) else C(y) val c2a = identity(c2) - val c3: {x, y} C { val n: {x, y} B } = c2 + val c3: C{ref x, y}{ val n: B{ref x, y} } = c2 val d1 = c3.foo() - d1: B @retains(x, y) + d1: B{ref x, y} class Local: @@ -29,7 +29,7 @@ def test(x: Cap, y: Cap, z: Cap) = end Local val l = Local() - val l1: {x, y} Local = l + val l1: Local{ref x, y} = l val l2 = Local(x) - val l3: {x, y, z} Local = l2 + val l3: Local{ref x, y, z} = l2 diff --git a/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala b/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala index 796dc62bdf22..63aee49f8454 100644 --- a/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala +++ b/tests/run-custom-args/captures/colltest5/CollectionStrawManCC5_1.scala @@ -18,19 +18,21 @@ object CollectionStrawMan5 { /* ------------ Base Traits -------------------------------- */ + type AnyIterableOnce[A] = IterableOnce[A]^ + /** Iterator can be used only once */ trait IterableOnce[+A] { - this: {*} IterableOnce[A] => - def iterator: {this} Iterator[A] + this: AnyIterableOnce[A] => + def iterator: Iterator[A]^{this} } /** Base trait for instances that can construct a collection from an iterable */ trait FromIterable { - type C[X] <: {*} Iterable[X] - def fromIterable[B](it: {*} Iterable[B]): {it} C[B] + type C[X] <: Iterable[X]^ + def fromIterable[B](it: Iterable[B]^): C[B]^{it} } - type FromIterableOf[+CC[X] <: {*} Iterable[X]] = FromIterable { + type FromIterableOf[+CC[X] <: Iterable[X]^] = FromIterable { type C[X] <: CC[X] } @@ -42,9 +44,9 @@ object CollectionStrawMan5 { /** Base trait for generic collections */ trait Iterable[+A] extends IterableOnce[A] with IterableLike[A] { - this: {*} Iterable[A] => - type C[X] <: {*} Iterable[X] - protected def coll: {this} Iterable[A] = this + this: Iterable[A]^ => + type C[X] <: Iterable[X]^ + protected def coll: Iterable[A]^{this} = this def knownLength: Int = -1 } @@ -58,7 +60,7 @@ object CollectionStrawMan5 { trait SeqFactory extends IterableFactory { type C[X] <: Seq[X] - def fromIterable[B](it: {*} Iterable[B]): C[B] + def fromIterable[B](it: Iterable[B]^): C[B] } /** Base trait for strict collections */ @@ -78,7 +80,7 @@ object CollectionStrawMan5 { def +=(x: A): this.type def result: To - def ++=(xs: {*} IterableOnce[A]): this.type = { + def ++=(xs: IterableOnce[A]^): this.type = { xs.iterator.foreach(+=) this } @@ -103,7 +105,7 @@ object CollectionStrawMan5 { with IterablePolyTransforms[A] with IterableMonoTransforms[A] { // sound bcs of VarianceNote type Repr = C[A] @uncheckedVariance - protected[this] def fromLikeIterable(coll: {*} Iterable[A] @uncheckedVariance): {coll} Repr @uncheckedVariance = + protected[this] def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^): Repr @uncheckedVariance ^{coll} = fromIterable(coll) } @@ -112,50 +114,50 @@ object CollectionStrawMan5 { extends IterableLike[A], SeqMonoTransforms[A], SeqPolyTransforms[A]: // sound bcs of VarianceNote this: SeqLike[A] => type C[X] <: Seq[X] - def fromIterable[B](coll: {*} Iterable[B]): C[B] - override protected[this] def fromLikeIterable(coll: {*} Iterable[A] @uncheckedVariance): Repr = + def fromIterable[B](coll: Iterable[B]^): C[B] + override protected[this] def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^): Repr = fromIterable(coll) trait IterableOps[+A] extends Any { - this: {*} IterableOps[A] => - def iterator: {this} Iterator[A] + this: IterableOps[A]^ => + def iterator: Iterator[A]^{this} def foreach(f: A => Unit): Unit = iterator.foreach(f) def foldLeft[B](z: B)(op: (B, A) => B): B = iterator.foldLeft(z)(op) def foldRight[B](z: B)(op: (A, B) => B): B = iterator.foldRight(z)(op) def indexWhere(p: A => Boolean): Int = iterator.indexWhere(p) def isEmpty: Boolean = !iterator.hasNext def head: A = iterator.next() - def view: {this} View[A] = View.fromIterator(iterator) + def view: View[A]^{this} = View.fromIterator(iterator) } trait IterableMonoTransforms[+A] extends Any { - this: {*} IterableMonoTransforms[A] => + this: IterableMonoTransforms[A]^ => type Repr - protected def coll: {this} Iterable[A] - protected[this] def fromLikeIterable(coll: {*} Iterable[A] @uncheckedVariance): {coll} Repr - def filter(p: A => Boolean): {this, p} Repr = fromLikeIterable(View.Filter(coll, p)) + protected def coll: Iterable[A]^{this} + protected[this] def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^): Repr^{coll} + def filter(p: A => Boolean): Repr^{this, p} = fromLikeIterable(View.Filter(coll, p)) - def partition(p: A => Boolean): ({this, p} Repr, {this, p} Repr) = { + def partition(p: A => Boolean): (Repr^{this, p}, Repr^{this, p}) = { val pn = View.Partition(coll, p) (fromLikeIterable(pn.left), fromLikeIterable(pn.right)) } - def drop(n: Int): {this} Repr = fromLikeIterable(View.Drop(coll, n)) + def drop(n: Int): Repr^{this} = fromLikeIterable(View.Drop(coll, n)) - def to[C[X] <: Iterable[X]](fi: FromIterableOf[C]): {this} C[A @uncheckedVariance] = + def to[C[X] <: Iterable[X]](fi: FromIterableOf[C]): C[A @uncheckedVariance]^{this} = // variance seems sound because `to` could just as well have been added // as a decorator. We should investigate this further to be sure. fi.fromIterable(coll) } trait IterablePolyTransforms[+A] extends Any { - this: {*} IterablePolyTransforms[A] => + this: IterablePolyTransforms[A]^ => type C[A] - protected def coll: {this} Iterable[A] - def fromIterable[B](coll: {*} Iterable[B]): {coll} C[B] - def map[B](f: A => B): {this, f} C[B] = fromIterable(View.Map(coll, f)) - def flatMap[B](f: A => {*} IterableOnce[B]): {this, f} C[B] = fromIterable(View.FlatMap(coll, f)) - def ++[B >: A](xs: {*} IterableOnce[B]): {this, xs} C[B] = fromIterable(View.Concat(coll, xs)) - def zip[B](xs: {*} IterableOnce[B]): {this, xs} C[(A @uncheckedVariance, B)] = fromIterable(View.Zip(coll, xs)) + protected def coll: Iterable[A]^{this} + def fromIterable[B](coll: Iterable[B]^): C[B]^{coll} + def map[B](f: A => B): C[B]^{this, f} = fromIterable(View.Map(coll, f)) + def flatMap[B](f: A => IterableOnce[B]^): C[B]^{this, f} = fromIterable(View.FlatMap(coll, f)) + def ++[B >: A](xs: IterableOnce[B]^): C[B]^{this, xs} = fromIterable(View.Concat(coll, xs)) + def zip[B](xs: IterableOnce[B]^): C[(A @uncheckedVariance, B)]^{this, xs} = fromIterable(View.Zip(coll, xs)) // sound bcs of VarianceNote } @@ -167,7 +169,7 @@ object CollectionStrawMan5 { while (it.hasNext) xs = new Cons(it.next(), xs) fromLikeIterable(xs) - override protected[this] def fromLikeIterable(coll: {*} Iterable[A] @uncheckedVariance): Repr + override protected[this] def fromLikeIterable(coll: Iterable[A] @uncheckedVariance ^): Repr override def filter(p: A => Boolean): Repr = fromLikeIterable(View.Filter(coll, p)) @@ -186,11 +188,11 @@ object CollectionStrawMan5 { trait SeqPolyTransforms[+A] extends Any, IterablePolyTransforms[A]: this: SeqPolyTransforms[A] => type C[A] - override def fromIterable[B](coll: {*} Iterable[B]): C[B] + override def fromIterable[B](coll: Iterable[B]^): C[B] override def map[B](f: A => B): C[B] = fromIterable(View.Map(coll, f)) - override def flatMap[B](f: A => {*} IterableOnce[B]): C[B] = fromIterable(View.FlatMap(coll, f)) - override def ++[B >: A](xs: {*} IterableOnce[B]): C[B] = fromIterable(View.Concat(coll, xs)) - override def zip[B](xs: {*} IterableOnce[B]): C[(A @uncheckedVariance, B)] = fromIterable(View.Zip(coll, xs)) + override def flatMap[B](f: A => IterableOnce[B]^): C[B] = fromIterable(View.FlatMap(coll, f)) + override def ++[B >: A](xs: IterableOnce[B]^): C[B] = fromIterable(View.Concat(coll, xs)) + override def zip[B](xs: IterableOnce[B]^): C[(A @uncheckedVariance, B)] = fromIterable(View.Zip(coll, xs)) /* --------- Concrete collection types ------------------------------- */ @@ -206,7 +208,7 @@ object CollectionStrawMan5 { def hasNext = !current.isEmpty def next() = { val r = current.head; current = current.tail; r } } - def fromIterable[B](c: {*} Iterable[B]): List[B] = List.fromIterable(c) + def fromIterable[B](c: Iterable[B]^): List[B] = List.fromIterable(c) def apply(i: Int): A = { require(!isEmpty) if (i == 0) head else tail.apply(i - 1) @@ -217,7 +219,7 @@ object CollectionStrawMan5 { def ++:[B >: A](prefix: List[B]): List[B] = if (prefix.isEmpty) this else Cons(prefix.head, prefix.tail ++: this) - override def ++[B >: A](xs: {*} IterableOnce[B]): List[B] = xs match { + override def ++[B >: A](xs: IterableOnce[B]^): List[B] = xs match { case xs: List[B] => this ++: xs case _ => fromIterable(View.Concat(this, xs)) } @@ -240,7 +242,7 @@ object CollectionStrawMan5 { object List extends SeqFactory { type C[X] = List[X] - def fromIterable[B](coll: {*} Iterable[B]): List[B] = coll match { + def fromIterable[B](coll: Iterable[B]^): List[B] = coll match { case coll: List[B] => coll case _ => ListBuffer.fromIterable(coll).result } @@ -252,7 +254,7 @@ object CollectionStrawMan5 { private var first, last: List[A] = Nil private var aliased = false def iterator = first.iterator - def fromIterable[B](coll: {*} Iterable[B]): ListBuffer[B] = ListBuffer.fromIterable(coll) + def fromIterable[B](coll: Iterable[B]^): ListBuffer[B] = ListBuffer.fromIterable(coll) def apply(i: Int) = first.apply(i) def length = first.length @@ -286,7 +288,7 @@ object CollectionStrawMan5 { object ListBuffer extends SeqFactory { type C[X] = ListBuffer[X] - def fromIterable[B](coll: {*} Iterable[B]): ListBuffer[B] = new ListBuffer[B] ++= coll + def fromIterable[B](coll: Iterable[B]^): ListBuffer[B] = new ListBuffer[B] ++= coll } /** Concrete collection type: ArrayBuffer */ @@ -303,7 +305,7 @@ object CollectionStrawMan5 { override def knownLength = length override def view = new ArrayBufferView(elems, start, end) def iterator = view.iterator - def fromIterable[B](it: {*} Iterable[B]): ArrayBuffer[B] = + def fromIterable[B](it: Iterable[B]^): ArrayBuffer[B] = ArrayBuffer.fromIterable(it) def +=(elem: A): this.type = { if (end == elems.length) { @@ -324,7 +326,7 @@ object CollectionStrawMan5 { } def result = this def trimStart(n: Int): Unit = start += (n max 0) - override def ++[B >: A](xs: {*} IterableOnce[B]): ArrayBuffer[B] = xs match { + override def ++[B >: A](xs: IterableOnce[B]^): ArrayBuffer[B] = xs match { case xs: ArrayBuffer[B] @unchecked => val elems = new Array[AnyRef](length + xs.length) Array.copy(this.elems, this.start, elems, 0, this.length) @@ -338,7 +340,7 @@ object CollectionStrawMan5 { object ArrayBuffer extends SeqFactory { type C[X] = ArrayBuffer[X] - def fromIterable[B](coll: {*} Iterable[B]): ArrayBuffer[B] = + def fromIterable[B](coll: Iterable[B]^): ArrayBuffer[B] = if (coll.knownLength >= 0) { val elems = new Array[AnyRef](coll.knownLength) val it = coll.iterator @@ -368,12 +370,12 @@ object CollectionStrawMan5 { type C[X] = List[X] protected def coll = new StringView(s) def iterator = coll.iterator - protected def fromLikeIterable(coll: {*} Iterable[Char]): String = { + protected def fromLikeIterable(coll: Iterable[Char]^): String = { val sb = new StringBuilder for (ch <- coll) sb.append(ch) sb.toString } - def fromIterable[B](coll: {*} Iterable[B]): List[B] = List.fromIterable(coll) + def fromIterable[B](coll: Iterable[B]^): List[B] = List.fromIterable(coll) def map(f: Char => Char): String = { val sb = new StringBuilder for (ch <- s) sb.append(f(ch)) @@ -384,7 +386,7 @@ object CollectionStrawMan5 { for (ch <- s) sb.append(f(ch)) sb.toString } - def ++(xs: {*} IterableOnce[Char]): String = { + def ++(xs: IterableOnce[Char]^): String = { val sb = new StringBuilder(s) for (ch <- xs.iterator) sb.append(ch) sb.toString @@ -402,10 +404,10 @@ object CollectionStrawMan5 { /** Concrete collection type: View */ trait View[+A] extends Iterable[A] with IterableLike[A] { - this: {*} View[A] => - type C[X] = {this} View[X] + this: View[A]^ => + type C[X] = View[X]^{this} override def view: this.type = this - override def fromIterable[B](c: {*} Iterable[B]): {this, c} View[B] = { + override def fromIterable[B](c: Iterable[B]^): View[B]^{this, c} = { c match { case c: View[B] => c case _ => View.fromIterator(c.iterator) @@ -431,8 +433,8 @@ object CollectionStrawMan5 { } object View { - def fromIterator[A](it: => {*} Iterator[A]): {it} View[A] = new View[A]: - def iterator: {this} Iterator[A] = it + def fromIterator[A](it: => Iterator[A]^): View[A]^{it} = new View[A]: + def iterator: Iterator[A]^{this} = it case object Empty extends View[Nothing] { def iterator: Iterator[Nothing] = Iterator.empty @@ -444,43 +446,43 @@ object CollectionStrawMan5 { override def knownLength = xs.length } - case class Filter[A](val underlying: {*} Iterable[A], p: A => Boolean) extends View[A] { - this: {underlying, p} Filter[A] => - def iterator: {this} Iterator[A] = underlying.iterator.filter(p) + case class Filter[A](val underlying: Iterable[A]^, p: A => Boolean) extends View[A] { + this: Filter[A]^{underlying, p} => + def iterator: Iterator[A]^{this} = underlying.iterator.filter(p) } - case class Partition[A](val underlying: {*} Iterable[A], p: A => Boolean) { - self: {underlying, p} Partition[A] => + case class Partition[A](val underlying: Iterable[A]^, p: A => Boolean) { + self: Partition[A]^{underlying, p} => class Partitioned(expected: Boolean) extends View[A]: - this: {self} Partitioned => - def iterator: {this} Iterator[A] = + this: Partitioned^{self} => + def iterator: Iterator[A]^{this} = underlying.iterator.filter((x: A) => p(x) == expected) - val left: {self} Partitioned = Partitioned(true) - val right: {self} Partitioned = Partitioned(false) + val left: Partitioned^{self} = Partitioned(true) + val right: Partitioned^{self} = Partitioned(false) } - case class Drop[A](underlying: {*} Iterable[A], n: Int) extends View[A] { - this: {underlying} Drop[A] => - def iterator: {this} Iterator[A] = underlying.iterator.drop(n) + case class Drop[A](underlying: Iterable[A]^, n: Int) extends View[A] { + this: Drop[A]^{underlying} => + def iterator: Iterator[A]^{this} = underlying.iterator.drop(n) override def knownLength = if (underlying.knownLength >= 0) underlying.knownLength - n max 0 else -1 } - case class Map[A, B](underlying: {*} Iterable[A], f: A => B) extends View[B] { - this: {underlying, f} Map[A, B] => - def iterator: {this} Iterator[B] = underlying.iterator.map(f) + case class Map[A, B](underlying: Iterable[A]^, f: A => B) extends View[B] { + this: Map[A, B]^{underlying, f} => + def iterator: Iterator[B]^{this} = underlying.iterator.map(f) override def knownLength = underlying.knownLength } - case class FlatMap[A, B](underlying: {*} Iterable[A], f: A => {*} IterableOnce[B]) extends View[B] { - this: {underlying, f} FlatMap[A, B] => - def iterator: {this} Iterator[B] = underlying.iterator.flatMap(f) + case class FlatMap[A, B](underlying: Iterable[A]^, f: A => IterableOnce[B]^) extends View[B] { + this: FlatMap[A, B]^{underlying, f} => + def iterator: Iterator[B]^{this} = underlying.iterator.flatMap(f) } - case class Concat[A](underlying: {*} Iterable[A], other: {*} IterableOnce[A]) extends View[A] { - this: {underlying, other} Concat[A] => - def iterator: {this} Iterator[A] = underlying.iterator ++ other + case class Concat[A](underlying: Iterable[A]^, other: IterableOnce[A]^) extends View[A] { + this: Concat[A]^{underlying, other} => + def iterator: Iterator[A]^{this} = underlying.iterator ++ other override def knownLength = other match { case other: Iterable[_] if underlying.knownLength >= 0 && other.knownLength >= 0 => underlying.knownLength + other.knownLength @@ -489,9 +491,9 @@ object CollectionStrawMan5 { } } - case class Zip[A, B](underlying: {*} Iterable[A], other: {*} IterableOnce[B]) extends View[(A, B)] { - this: {underlying, other} Zip[A, B] => - def iterator: {this} Iterator[(A, B)] = underlying.iterator.zip(other) + case class Zip[A, B](underlying: Iterable[A]^, other: IterableOnce[B]^) extends View[(A, B)] { + this: Zip[A, B]^{underlying, other} => + def iterator: Iterator[(A, B)]^{this} = underlying.iterator.zip(other) override def knownLength = other match { case other: Iterable[_] if underlying.knownLength >= 0 && other.knownLength >= 0 => underlying.knownLength min other.knownLength @@ -504,7 +506,7 @@ object CollectionStrawMan5 { /* ---------- Iterators ---------------------------------------------------*/ /** A core Iterator class */ - trait Iterator[+A] extends IterableOnce[A] { self: {*} Iterator[A] => + trait Iterator[+A] extends IterableOnce[A] { self: Iterator[A]^ => def hasNext: Boolean def next(): A def iterator: this.type = this @@ -522,7 +524,7 @@ object CollectionStrawMan5 { } -1 } - def filter(p: A => Boolean): {this, p} Iterator[A] = new Iterator[A] { + def filter(p: A => Boolean): Iterator[A]^{this, p} = new Iterator[A] { private var hd: A = compiletime.uninitialized private var hdDefined: Boolean = false @@ -544,13 +546,13 @@ object CollectionStrawMan5 { else Iterator.empty.next() } - def map[B](f: A => B): {this, f} Iterator[B] = new Iterator[B] { + def map[B](f: A => B): Iterator[B]^{this, f} = new Iterator[B] { def hasNext = self.hasNext def next() = f(self.next()) } - def flatMap[B](f: A => {*} IterableOnce[B]): {this, f} Iterator[B] = new Iterator[B] { - private var myCurrent: {this} Iterator[B] = Iterator.empty + def flatMap[B](f: A => IterableOnce[B]^): Iterator[B]^{this, f} = new Iterator[B] { + private var myCurrent: Iterator[B]^{this} = Iterator.empty private def current = { while (!myCurrent.hasNext && self.hasNext) myCurrent = f(self.next()).iterator @@ -559,8 +561,8 @@ object CollectionStrawMan5 { def hasNext = current.hasNext def next() = current.next() } - def ++[B >: A](xs: {*} IterableOnce[B]): {this, xs} Iterator[B] = new Iterator[B] { - private var myCurrent: {self, xs} Iterator[B] = self + def ++[B >: A](xs: IterableOnce[B]^): Iterator[B]^{this, xs} = new Iterator[B] { + private var myCurrent: Iterator[B]^{self, xs} = self private var first = true private def current = { if (!myCurrent.hasNext && first) { @@ -572,7 +574,7 @@ object CollectionStrawMan5 { def hasNext = current.hasNext def next() = current.next() } - def drop(n: Int): {this} Iterator[A] = { + def drop(n: Int): Iterator[A]^{this} = { var i = 0 while (i < n && hasNext) { next() @@ -580,7 +582,7 @@ object CollectionStrawMan5 { } this } - def zip[B](that: {*} IterableOnce[B]): {this, that} Iterator[(A, B)] = new Iterator[(A, B)] { + def zip[B](that: IterableOnce[B]^): Iterator[(A, B)]^{this, that} = new Iterator[(A, B)] { val thatIterator = that.iterator def hasNext = self.hasNext && thatIterator.hasNext def next() = (self.next(), thatIterator.next()) diff --git a/tests/run-custom-args/captures/colltest5/Test_2.scala b/tests/run-custom-args/captures/colltest5/Test_2.scala index 8c070439db80..4a941afc124b 100644 --- a/tests/run-custom-args/captures/colltest5/Test_2.scala +++ b/tests/run-custom-args/captures/colltest5/Test_2.scala @@ -5,7 +5,7 @@ object Test { import colltest5.strawman.collections.* import CollectionStrawMan5.* - def seqOps(xs: Seq[Int]) = { // try with {*} Seq[Int] + def seqOps(xs: Seq[Int]) = { // try with Seq[Int]{ref any} val strPlusInt: (String, Int) => String = _ + _ val intPlusStr: (Int, String) => String = _ + _ val isEven: Int => Boolean = _ % 2 == 0 @@ -61,7 +61,7 @@ object Test { println(xs16) } - def viewOps(xs: {*} View[Int]) = { + def viewOps(xs: View[Int]{ref any}) = { val strPlusInt: (String, Int) => String = _ + _ val intPlusStr: (Int, String) => String = _ + _ val isEven: Int => Boolean = _ % 2 == 0 @@ -78,27 +78,27 @@ object Test { val x5 = xs.to(List) val y5: List[Int] = x5 val (xs6, xs7) = xs.partition(isEven) - val ys6: {xs6, isEven} View[Int] = xs6 - val ys7: {xs7, isEven} View[Int] = xs7 + val ys6: View[Int]{ref xs6, isEven} = xs6 + val ys7: View[Int]{ref xs7, isEven} = xs7 val (xs6a, xs7a) = xs.partition(_ % 2 == 0) - val ys6a: {xs6} View[Int] = xs6 - val ys7a: {xs7} View[Int] = xs7 + val ys6a: View[Int]{ref xs6} = xs6 + val ys7a: View[Int]{ref xs7} = xs7 val xs8 = xs.drop(2) - val ys8: {xs8} View[Int] = xs8 + val ys8: View[Int]{ref xs8} = xs8 val xs9 = xs.map(isNonNeg) - val ys9: {xs9} View[Boolean] = xs9 + val ys9: View[Boolean]{ref xs9} = xs9 val xs10 = xs.flatMap(flips) - val ys10: {xs10} View[Int] = xs10 + val ys10: View[Int]{ref xs10} = xs10 val xs11 = xs ++ xs - val ys11: {xs11} View[Int] = xs11 + val ys11: View[Int]{ref xs11} = xs11 val xs12 = xs ++ Nil - val ys12: {xs12} View[Int] = xs12 + val ys12: View[Int]{ref xs12} = xs12 val xs13 = Nil ++ xs val ys13: List[Int] = xs13 val xs14 = xs ++ Cons("a", Nil) - val ys14: {xs14} View[Any] = xs14 + val ys14: View[Any]{ref xs14} = xs14 val xs15 = xs.zip(xs9) - val ys15: {xs15} View[(Int, Boolean)] = xs15 + val ys15: View[(Int, Boolean)]{ref xs15} = xs15 println("-------") println(x1) println(x2) From a307ebd6232c0a4830d1964518f42893ca5c73c5 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 30 Apr 2023 11:55:12 +0200 Subject: [PATCH 520/657] Handle self types that are aliases of capturing types --- compiler/src/dotty/tools/dotc/core/Types.scala | 6 +++--- tests/pos-custom-args/captures/selftype-alias.scala | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 tests/pos-custom-args/captures/selftype-alias.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index fe0fc8a6dc2d..165037d65979 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4999,9 +4999,9 @@ object Types { if (!givenSelf.isValueType) appliedRef else if (clsd.is(Module)) givenSelf else if (ctx.erasedTypes) appliedRef - else givenSelf match - case givenSelf @ EventuallyCapturingType(tp, _) => - givenSelf.derivedAnnotatedType(tp & appliedRef, givenSelf.annot) + else givenSelf.dealiasKeepAnnots match + case givenSelf1 @ EventuallyCapturingType(tp, _) => + givenSelf1.derivedAnnotatedType(tp & appliedRef, givenSelf1.annot) case _ => AndType(givenSelf, appliedRef) } diff --git a/tests/pos-custom-args/captures/selftype-alias.scala b/tests/pos-custom-args/captures/selftype-alias.scala new file mode 100644 index 000000000000..284691131c7b --- /dev/null +++ b/tests/pos-custom-args/captures/selftype-alias.scala @@ -0,0 +1,9 @@ +import language.experimental.captureChecking + +type AnyIterableOnce[A] = IterableOnce[A]^ + +/** Iterator can be used only once */ +trait IterableOnce[+A]: + //this: IterableOnce[A]{ref any} => + this: AnyIterableOnce[A] => + def iterator: Iterator[A]^{this} From d7b3f0908c22e4bafd2cbf28d88043e9c48b5222 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 30 Apr 2023 12:53:31 +0200 Subject: [PATCH 521/657] Update tests and restore parsing for infix `^` in types --- .../dotty/tools/dotc/parsing/Parsers.scala | 9 ++++++- tests/pos-custom-args/captures/byname.scala | 6 ++--- .../pos-custom-args/captures/capt-test.scala | 8 +++--- tests/pos-custom-args/captures/classes.scala | 18 ++++++------- .../captures/colltest5/Test_2.scala | 26 +++++++++---------- 5 files changed, 37 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index c36e2238662f..06a8d99bbd1c 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1689,6 +1689,7 @@ object Parsers { if in.token == LPAREN then funParamClause() :: funParamClauses() else Nil /** InfixType ::= RefinedType {id [nl] RefinedType} + * | RefinedType `^` */ def infixType(): Tree = infixTypeRest(refinedType()) @@ -1708,7 +1709,13 @@ object Parsers { refinedTypeRest(atSpan(startOffset(t)) { RefinedTypeTree(rejectWildcardType(t), refinement(indentOK = true)) }) - else if in.isIdent(nme.UPARROW) then + else if in.isIdent(nme.UPARROW) + && (in.lookahead.token == LBRACE + || !canStartInfixTypeTokens.contains(in.lookahead.token)) + // Disambiguation: a `^` is treated as a postfix operator meaning `^{any}` + // if followed by `{` or newline, or any other token that cannot start + // an infix type. Otherwise it is treated as an infix operator. + then val upArrowStart = in.offset in.nextToken() def cs = diff --git a/tests/pos-custom-args/captures/byname.scala b/tests/pos-custom-args/captures/byname.scala index e54a67660172..780214ebdffd 100644 --- a/tests/pos-custom-args/captures/byname.scala +++ b/tests/pos-custom-args/captures/byname.scala @@ -1,12 +1,12 @@ import annotation.retainsByName class CC -type Cap = {*} CC +type Cap = CC^ class I -def test(cap1: Cap, cap2: Cap): I{ref cap1} = +def test(cap1: Cap, cap2: Cap): I^{cap1} = def f() = if cap1 == cap1 then I() else I() - def h(x: ->{ref any} I) = x + def h(x: ->{any} I) = x h(f()) // OK def hh(x: -> I @retainsByName(cap1)) = x h(f()) diff --git a/tests/pos-custom-args/captures/capt-test.scala b/tests/pos-custom-args/captures/capt-test.scala index 081dbd8a3aa1..e229c685d846 100644 --- a/tests/pos-custom-args/captures/capt-test.scala +++ b/tests/pos-custom-args/captures/capt-test.scala @@ -19,10 +19,10 @@ def map[A, B](f: A => B)(xs: LIST[A]): LIST[B] = xs.map(f) class C -type Cap = C{ref any} +type Cap = C^ class Foo(x: Cap): - this: Foo{ref x} => + this: Foo^{x} => def test(c: Cap, d: Cap) = def f(x: Cap): Unit = if c == x then () @@ -32,7 +32,7 @@ def test(c: Cap, d: Cap) = val zs = val z = g CONS(z, ys) - val zsc: LIST[Cap ->{ref d, y} Unit] = zs + val zsc: LIST[Cap ->{d, y} Unit] = zs val a4 = zs.map(identity) - val a4c: LIST[Cap ->{ref d, y} Unit] = a4 + val a4c: LIST[Cap ->{d, y} Unit] = a4 diff --git a/tests/pos-custom-args/captures/classes.scala b/tests/pos-custom-args/captures/classes.scala index 80971512e25e..bc827dcfc67d 100644 --- a/tests/pos-custom-args/captures/classes.scala +++ b/tests/pos-custom-args/captures/classes.scala @@ -1,22 +1,22 @@ import annotation.retains class B -type Cap = B{ref any} +type Cap = B^ class C(val n: Cap): - this: C{ref n} => - def foo(): B{ref n} = n + this: C^{n} => + def foo(): B^{n} = n def test(x: Cap, y: Cap, z: Cap) = val c0 = C(x) - val c1: C{ref x}{val n: B{ref x}} = c0 + val c1: C{val n: B^{x}}^{x} = c0 val d = c1.foo() - d: B{ref x} + d: B^{x} val c2 = if ??? then C(x) else C(y) val c2a = identity(c2) - val c3: C{ref x, y}{ val n: B{ref x, y} } = c2 + val c3: C{ val n: B^{x, y} }^{x, y} = c2 val d1 = c3.foo() - d1: B{ref x, y} + d1: B^{x, y} class Local: @@ -29,7 +29,7 @@ def test(x: Cap, y: Cap, z: Cap) = end Local val l = Local() - val l1: Local{ref x, y} = l + val l1: Local^{x, y} = l val l2 = Local(x) - val l3: Local{ref x, y, z} = l2 + val l3: Local^{x, y, z} = l2 diff --git a/tests/run-custom-args/captures/colltest5/Test_2.scala b/tests/run-custom-args/captures/colltest5/Test_2.scala index 4a941afc124b..d6b4e43483e6 100644 --- a/tests/run-custom-args/captures/colltest5/Test_2.scala +++ b/tests/run-custom-args/captures/colltest5/Test_2.scala @@ -5,7 +5,7 @@ object Test { import colltest5.strawman.collections.* import CollectionStrawMan5.* - def seqOps(xs: Seq[Int]) = { // try with Seq[Int]{ref any} + def seqOps(xs: Seq[Int]) = { // try with Seq[Int]^{any} val strPlusInt: (String, Int) => String = _ + _ val intPlusStr: (Int, String) => String = _ + _ val isEven: Int => Boolean = _ % 2 == 0 @@ -61,7 +61,7 @@ object Test { println(xs16) } - def viewOps(xs: View[Int]{ref any}) = { + def viewOps(xs: View[Int]^{any}) = { val strPlusInt: (String, Int) => String = _ + _ val intPlusStr: (Int, String) => String = _ + _ val isEven: Int => Boolean = _ % 2 == 0 @@ -78,27 +78,27 @@ object Test { val x5 = xs.to(List) val y5: List[Int] = x5 val (xs6, xs7) = xs.partition(isEven) - val ys6: View[Int]{ref xs6, isEven} = xs6 - val ys7: View[Int]{ref xs7, isEven} = xs7 + val ys6: View[Int]^{xs6, isEven} = xs6 + val ys7: View[Int]^{xs7, isEven} = xs7 val (xs6a, xs7a) = xs.partition(_ % 2 == 0) - val ys6a: View[Int]{ref xs6} = xs6 - val ys7a: View[Int]{ref xs7} = xs7 + val ys6a: View[Int]^{xs6} = xs6 + val ys7a: View[Int]^{xs7} = xs7 val xs8 = xs.drop(2) - val ys8: View[Int]{ref xs8} = xs8 + val ys8: View[Int]^{xs8} = xs8 val xs9 = xs.map(isNonNeg) - val ys9: View[Boolean]{ref xs9} = xs9 + val ys9: View[Boolean]^{xs9} = xs9 val xs10 = xs.flatMap(flips) - val ys10: View[Int]{ref xs10} = xs10 + val ys10: View[Int]^{xs10} = xs10 val xs11 = xs ++ xs - val ys11: View[Int]{ref xs11} = xs11 + val ys11: View[Int]^{xs11} = xs11 val xs12 = xs ++ Nil - val ys12: View[Int]{ref xs12} = xs12 + val ys12: View[Int]^{xs12} = xs12 val xs13 = Nil ++ xs val ys13: List[Int] = xs13 val xs14 = xs ++ Cons("a", Nil) - val ys14: View[Any]{ref xs14} = xs14 + val ys14: View[Any]^{xs14} = xs14 val xs15 = xs.zip(xs9) - val ys15: View[(Int, Boolean)]{ref xs15} = xs15 + val ys15: View[(Int, Boolean)]^{xs15} = xs15 println("-------") println(x1) println(x2) From d16c0f8efd44f0e1b80b418cc716f2b11a000325 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 30 Apr 2023 16:50:38 +0200 Subject: [PATCH 522/657] Change printing to new syntax --- .../tools/dotc/printing/PlainPrinter.scala | 39 ++++---- .../tools/dotc/printing/RefinedPrinter.scala | 90 +++++++++++-------- tests/neg-custom-args/captures/byname.check | 14 +-- tests/neg-custom-args/captures/byname.scala | 6 +- tests/neg-custom-args/captures/capt1.check | 18 ++-- tests/neg-custom-args/captures/cc-this.check | 2 +- tests/neg-custom-args/captures/cc-this3.check | 4 +- tests/neg-custom-args/captures/cc-this3.scala | 4 +- tests/neg-custom-args/captures/cc-this4.check | 2 +- tests/neg-custom-args/captures/cc-this5.check | 4 +- .../captures/class-contra.check | 4 +- .../captures/class-contra.scala | 10 +-- .../captures/curried-simplified.check | 36 ++++---- .../captures/curried-simplified.scala | 14 +-- tests/neg-custom-args/captures/eta.check | 8 +- tests/neg-custom-args/captures/eta.scala | 8 +- .../captures/exception-definitions.check | 12 +-- .../captures/exception-definitions.scala | 8 +- tests/neg-custom-args/captures/i15116.check | 8 +- tests/neg-custom-args/captures/i15116.scala | 10 +-- tests/neg-custom-args/captures/i15772.check | 20 ++--- tests/neg-custom-args/captures/i15772.scala | 34 +++---- tests/neg-custom-args/captures/lazylist.check | 28 +++--- tests/neg-custom-args/captures/lazylist.scala | 18 ++-- .../captures/lazylists-exceptions.check | 2 +- .../captures/lazylists-exceptions.scala | 16 ++-- .../neg-custom-args/captures/lazylists1.check | 6 +- .../neg-custom-args/captures/lazylists1.scala | 18 ++-- .../neg-custom-args/captures/lazylists2.check | 30 +++---- .../neg-custom-args/captures/lazylists2.scala | 36 ++++---- tests/neg-custom-args/captures/lazyref.check | 20 ++--- tests/neg-custom-args/captures/lazyref.scala | 12 +-- .../captures/nestedclass.check | 2 +- .../captures/nestedclass.scala | 2 +- tests/neg-custom-args/captures/real-try.check | 6 +- tests/neg-custom-args/captures/try.check | 12 +-- .../captures/usingLogFile.check | 16 ++-- .../captures/usingLogFile.scala | 10 +-- tests/neg-custom-args/captures/vars.check | 12 +-- tests/neg-custom-args/captures/vars.scala | 6 +- .../captures/nested-classes.scala | 4 +- 41 files changed, 305 insertions(+), 306 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index ee0062f77dcd..bd027ed79431 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -149,8 +149,14 @@ class PlainPrinter(_ctx: Context) extends Printer { + defn.ObjectClass + defn.FromJavaObjectSymbol - def toText(cs: CaptureSet): Text = - "{" ~ Text(cs.elems.toList.map(toTextCaptureRef), ", ") ~ "}" + def toTextCaptureSet(cs: CaptureSet): Text = + if printDebug && !cs.isConst then cs.toString + else if ctx.settings.YccDebug.value then cs.show + else if !cs.isConst && cs.elems.isEmpty then "?" + else "{" ~ Text(cs.elems.toList.map(toTextCaptureRef), ", ") ~ "}" + + protected def toTextCapturing(parent: Type, refsText: Text, boxText: Text): Text = + changePrec(InfixPrec)(boxText ~ toTextLocal(parent) ~ "^" ~ refsText) def toText(tp: Type): Text = controlled { homogenize(tp) match { @@ -207,20 +213,8 @@ class PlainPrinter(_ctx: Context) extends Printer { (" <: " ~ toText(bound) provided !bound.isAny) }.close case tp @ EventuallyCapturingType(parent, refs) => - def box = - Str("box ") provided tp.isBoxed //&& ctx.settings.YccDebug.value - def printRegular(refsText: Text) = - changePrec(GlobalPrec)(box ~ refsText ~ " " ~ toText(parent)) - if printDebug && !refs.isConst then - printRegular(refs.toString) - else if ctx.settings.YccDebug.value then - printRegular(refs.show) - else if !refs.isConst && refs.elems.isEmpty then - printRegular("?") - else if Config.printCaptureSetsAsPrefix then - printRegular(toText(refs)) - else - changePrec(InfixPrec)(box ~ toText(parent) ~ " @retains(" ~ toText(refs.elems.toList, ",") ~ ")") + val boxText: Text = Str("box ") provided tp.isBoxed //&& ctx.settings.YccDebug.value + toTextCapturing(parent, toTextCaptureSet(refs), boxText) case tp: PreviousErrorType if ctx.settings.XprintTypes.value => "" // do not print previously reported error message because they may try to print this error type again recuresevely case tp: ErrorType => @@ -241,14 +235,13 @@ class PlainPrinter(_ctx: Context) extends Printer { ~ (Str(": ") provided !tp.resultType.isInstanceOf[MethodOrPoly]) ~ toText(tp.resultType) } - case ExprType(ct @ EventuallyCapturingType(parent, refs)) - if ct.annot.symbol == defn.RetainsByNameAnnot => - if refs.isUniversal then changePrec(GlobalPrec) { "=> " ~ toText(parent) } - else toText(CapturingType(ExprType(parent), refs)) case ExprType(restp) => - changePrec(GlobalPrec) { - (if Feature.pureFunsEnabled then "-> " else "=> ") ~ toText(restp) - } + def arrowText: Text = restp match + case ct @ EventuallyCapturingType(parent, refs) if ct.annot.symbol == defn.RetainsByNameAnnot => + if refs.isUniversal then Str("=>") else Str("->") ~ toTextCaptureSet(refs) + case _ => + if Feature.pureFunsEnabled then "->" else "=>" + changePrec(GlobalPrec)(arrowText ~ " " ~ toText(restp)) case tp: HKTypeLambda => changePrec(GlobalPrec) { "[" ~ paramsText(tp) ~ "]" ~ lambdaHash(tp) ~ Str(" =>> ") ~ toTextGlobal(tp.resultType) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 34374fe24998..338b03c5adb7 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -144,44 +144,49 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { private def arrow(isGiven: Boolean, isPure: Boolean): String = (if isGiven then "?" else "") + (if isPure then "->" else "=>") - override def toText(tp: Type): Text = controlled { - def toTextTuple(args: List[Type]): Text = - "(" ~ argsText(args) ~ ")" + private def toTextFunction(tp: AppliedType, refs: Text = Str("")): Text = + val AppliedType(tycon, args) = (tp: @unchecked) + val tsym = tycon.typeSymbol + val isGiven = tsym.name.isContextFunction + val isPure = Feature.pureFunsEnabled && !tsym.name.isImpureFunction + changePrec(GlobalPrec) { + val argStr: Text = + if args.length == 2 + && !defn.isTupleNType(args.head) + && !isGiven + then + atPrec(InfixPrec) { argText(args.head) } + else + "(" + ~ argsText(args.init) + ~ ")" + argStr ~ " " ~ arrow(isGiven, isPure) ~ refs ~ " " ~ argText(args.last) + } - def toTextFunction(args: List[Type], isGiven: Boolean, isPure: Boolean): Text = + private def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: Text = Str("")): Text = info match + case info: MethodType => changePrec(GlobalPrec) { - val argStr: Text = - if args.length == 2 - && !defn.isTupleNType(args.head) - && !isGiven - then - atPrec(InfixPrec) { argText(args.head) } - else - "(" - ~ argsText(args.init) - ~ ")" - argStr ~ " " ~ arrow(isGiven, isPure) ~ " " ~ argText(args.last) + "(" + ~ paramsText(info) + ~ ") " + ~ arrow(info.isImplicitMethod, isPure) + ~ refs + ~ " " + ~ toTextMethodAsFunction(info.resultType, isPure) + } + case info: PolyType => + changePrec(GlobalPrec) { + "[" + ~ paramsText(info) + ~ "] => " + ~ toTextMethodAsFunction(info.resultType, isPure) } + case _ => + toText(info) - def toTextMethodAsFunction(info: Type, isPure: Boolean): Text = info match - case info: MethodType => - changePrec(GlobalPrec) { - "(" - ~ paramsText(info) - ~ ") " - ~ arrow(info.isImplicitMethod, isPure) - ~ " " - ~ toTextMethodAsFunction(info.resultType, isPure) - } - case info: PolyType => - changePrec(GlobalPrec) { - "[" - ~ paramsText(info) - ~ "] => " - ~ toTextMethodAsFunction(info.resultType, isPure) - } - case _ => - toText(info) + override def toText(tp: Type): Text = controlled { + def toTextTuple(args: List[Type]): Text = + "(" ~ argsText(args) ~ ")" def isInfixType(tp: Type): Boolean = tp match case AppliedType(tycon, args) => @@ -223,9 +228,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val tsym = tycon.typeSymbol if tycon.isRepeatedParam then toTextLocal(args.head) ~ "*" else if tp.isConvertibleParam then "into " ~ toText(args.head) - else if defn.isFunctionSymbol(tsym) then - toTextFunction(args, tsym.name.isContextFunction, - isPure = Feature.pureFunsEnabled && !tsym.name.isImpureFunction) + else if defn.isFunctionSymbol(tsym) then toTextFunction(tp) else if isInfixType(tp) then val l :: r :: Nil = args: @unchecked val opName = tyconName(tycon) @@ -618,7 +621,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def toTextAnnot = toTextLocal(arg) ~~ annotText(annot.symbol.enclosingClass, annot) def toTextRetainsAnnot = - try changePrec(GlobalPrec)(toText(captureSet) ~ " " ~ toText(arg)) + try changePrec(GlobalPrec)(toTextCaptureSet(captureSet) ~ " " ~ toText(arg)) catch case ex: IllegalCaptureRef => toTextAnnot if annot.symbol.maybeOwner == defn.RetainsAnnot && Feature.ccEnabled && Config.printCaptureSetsAsPrefix && !printDebug @@ -737,12 +740,21 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case ImpureByNameTypeTree(bntpt) => "=> " ~ toTextLocal(bntpt) case _ => - changePrec(GlobalPrec)("{" ~ Text(refs.map(toText), ", ") ~ "} " ~ toText(parent)) + toText(parent) ~ "^" + ~ changePrec(GlobalPrec)("{" ~ Text(refs.map(toText), ", ") ~ "}") case _ => tree.fallbackToText(this) } } + override protected def toTextCapturing(tp: Type, refsText: Text, boxText: Text): Text = tp match + case tp: AppliedType if defn.isFunctionSymbol(tp.typeSymbol) && !printDebug => + boxText ~ toTextFunction(tp, refsText) + case tp: RefinedType if defn.isFunctionOrPolyType(tp) && !printDebug => + boxText ~ toTextMethodAsFunction(tp.refinedInfo, isPure = !tp.typeSymbol.name.isImpureFunction, refsText) + case _ => + super.toTextCapturing(tp, refsText, boxText) + override def toText[T <: Untyped](tree: Tree[T]): Text = controlled { import untpd._ diff --git a/tests/neg-custom-args/captures/byname.check b/tests/neg-custom-args/captures/byname.check index 90cf6c145c33..b1d8fb3b5404 100644 --- a/tests/neg-custom-args/captures/byname.check +++ b/tests/neg-custom-args/captures/byname.check @@ -1,20 +1,14 @@ --- Warning: tests/neg-custom-args/captures/byname.scala:17:18 ---------------------------------------------------------- -17 | def h(x: {cap1} -> I) = x // warning - | ^ - | Style: by-name `->` should immediately follow closing `}` of capture set - | to avoid confusion with function type. - | That is, `{c}-> T` instead of `{c} -> T`. -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/byname.scala:10:6 ---------------------------------------- 10 | h(f2()) // error | ^^^^ - | Found: {cap1} (x$0: Int) -> Int - | Required: {cap2} (x$0: Int) -> Int + | Found: (x$0: Int) ->{cap1} Int + | Required: (x$0: Int) ->{cap2} Int | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/byname.scala:19:5 ---------------------------------------- 19 | h(g()) // error | ^^^ - | Found: {cap2} () ?-> I - | Required: {cap1} () ?-> I + | Found: () ?->{cap2} I + | Required: () ?->{cap1} I | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/byname.scala b/tests/neg-custom-args/captures/byname.scala index 1838647f2899..ac13174eb4f4 100644 --- a/tests/neg-custom-args/captures/byname.scala +++ b/tests/neg-custom-args/captures/byname.scala @@ -5,16 +5,16 @@ def test(cap1: Cap, cap2: Cap) = def g(x: Int) = if cap2 == cap2 then 1 else x def g2(x: Int) = if cap1 == cap1 then 1 else x def f2() = if cap1 == cap1 then g2 else g2 - def h(ff: => {cap2} Int -> Int) = ff + def h(ff: => Int ->{cap2} Int) = ff h(f()) // ok h(f2()) // error class I -def test2(cap1: Cap, cap2: Cap): {cap1} I = +def test2(cap1: Cap, cap2: Cap): I^{cap1} = def f() = if cap1 == cap1 then I() else I() def g() = if cap2 == cap2 then I() else I() - def h(x: {cap1} -> I) = x // warning + def h(x: ->{cap1} I) = x // ok h(f()) // OK h(g()) // error diff --git a/tests/neg-custom-args/captures/capt1.check b/tests/neg-custom-args/captures/capt1.check index 51ed3e6736cf..1e9e8626bdc5 100644 --- a/tests/neg-custom-args/captures/capt1.check +++ b/tests/neg-custom-args/captures/capt1.check @@ -1,21 +1,21 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:4:2 ------------------------------------------ 4 | () => if x == null then y else y // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: {x} () -> ? C + | Found: () ->{x} C^? | Required: () -> C | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:7:2 ------------------------------------------ 7 | () => if x == null then y else y // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: {x} () -> ? C + | Found: () ->{x} C^? | Required: Matchable | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:14:2 ----------------------------------------- 14 | def f(y: Int) = if x == null then y else y // error | ^ - | Found: {x} Int -> Int + | Found: Int ->{x} Int | Required: Matchable 15 | f | @@ -23,7 +23,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:21:2 ----------------------------------------- 21 | class F(y: Int) extends A: // error | ^ - | Found: {x} A + | Found: A^{x} | Required: A 22 | def m() = if x == null then y else y 23 | F(22) @@ -32,7 +32,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:26:2 ----------------------------------------- 26 | new A: // error | ^ - | Found: {x} A + | Found: A^{x} | Required: A 27 | def m() = if x == null then y else y | @@ -40,14 +40,14 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:32:24 ---------------------------------------- 32 | val z2 = h[() -> Cap](() => x) // error | ^^^^^^^ - | Found: {x} () -> Cap - | Required: () -> box {*} C + | Found: () ->{x} Cap + | Required: () -> box C^{*} | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:33:5 ----------------------------------------- 33 | (() => C()) // error | ^^^^^^^^^ - | Found: ? () -> Cap - | Required: () -> box {*} C + | Found: () ->? Cap + | Required: () -> box C^{*} | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/cc-this.check b/tests/neg-custom-args/captures/cc-this.check index 0049f42a5db5..47207f913f1d 100644 --- a/tests/neg-custom-args/captures/cc-this.check +++ b/tests/neg-custom-args/captures/cc-this.check @@ -1,7 +1,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-this.scala:8:15 --------------------------------------- 8 | val y: C = this // error | ^^^^ - | Found: (C.this : {C.this.x} C) + | Found: (C.this : C^{C.this.x}) | Required: C | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/cc-this3.check b/tests/neg-custom-args/captures/cc-this3.check index 705cdfbc00d7..ceb2694e8f15 100644 --- a/tests/neg-custom-args/captures/cc-this3.check +++ b/tests/neg-custom-args/captures/cc-this3.check @@ -1,14 +1,14 @@ -- [E058] Type Mismatch Error: tests/neg-custom-args/captures/cc-this3.scala:8:6 --------------------------------------- 8 |class B extends A: // error | ^ - | illegal inheritance: self type {*} B of class B does not conform to self type {} A + | illegal inheritance: self type B^{*} of class B does not conform to self type A^{} | of parent class A | | longer explanation available when compiling with `-explain` -- [E058] Type Mismatch Error: tests/neg-custom-args/captures/cc-this3.scala:11:6 -------------------------------------- 11 |class C(val f: () => Int) extends A // error | ^ - | illegal inheritance: self type {C.this.f} C of class C does not conform to self type {} A + | illegal inheritance: self type C^{C.this.f} of class C does not conform to self type A^{} | of parent class A | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/cc-this3.scala b/tests/neg-custom-args/captures/cc-this3.scala index eeb9606f0c81..61ccdb214b30 100644 --- a/tests/neg-custom-args/captures/cc-this3.scala +++ b/tests/neg-custom-args/captures/cc-this3.scala @@ -6,13 +6,13 @@ class A: val x: A = this class B extends A: // error - this: {*} B => + this: B^{*} => class C(val f: () => Int) extends A // error class A2 class B2 extends A2: // ok - this: {*} B2 => + this: B2^{*} => class C2(val f: () => Int) extends A2 // ok diff --git a/tests/neg-custom-args/captures/cc-this4.check b/tests/neg-custom-args/captures/cc-this4.check index a54ca8d57f4e..52c06f5bbc30 100644 --- a/tests/neg-custom-args/captures/cc-this4.check +++ b/tests/neg-custom-args/captures/cc-this4.check @@ -2,5 +2,5 @@ 1 |open class C: // error | ^ | class C needs an explicitly declared self type since its - | inferred self type {} C + | inferred self type C^{} | is not visible in other compilation units that define subclasses. diff --git a/tests/neg-custom-args/captures/cc-this5.check b/tests/neg-custom-args/captures/cc-this5.check index 8cc1ac9ccc5d..84ac97474b80 100644 --- a/tests/neg-custom-args/captures/cc-this5.check +++ b/tests/neg-custom-args/captures/cc-this5.check @@ -5,14 +5,14 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/cc-this5.scala:21:15 ------------------------------------- 21 | val x: A = this // error | ^^^^ - | Found: (A.this : {c} A) + | Found: (A.this : A^{c}) | Required: A | | longer explanation available when compiling with `-explain` -- [E058] Type Mismatch Error: tests/neg-custom-args/captures/cc-this5.scala:7:9 --------------------------------------- 7 | object D extends C: // error | ^ - | illegal inheritance: self type {c} D.type of object D does not conform to self type {} C + | illegal inheritance: self type D.type^{c} of object D does not conform to self type C^{} | of parent class C | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/class-contra.check b/tests/neg-custom-args/captures/class-contra.check index 69a5f0097de8..6d4c89f872ad 100644 --- a/tests/neg-custom-args/captures/class-contra.check +++ b/tests/neg-custom-args/captures/class-contra.check @@ -1,7 +1,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/class-contra.scala:12:39 --------------------------------- -12 | def fun(x: K{val f: {a} T}) = x.setf(a) // error +12 | def fun(x: K{val f: T^{a}}) = x.setf(a) // error | ^ - | Found: (a : {x, y} T) + | Found: (a : T^{x, y}) | Required: T | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/class-contra.scala b/tests/neg-custom-args/captures/class-contra.scala index 270aaf9309a9..210fd4e331f1 100644 --- a/tests/neg-custom-args/captures/class-contra.scala +++ b/tests/neg-custom-args/captures/class-contra.scala @@ -1,13 +1,13 @@ class C -type Cap = {*} C +type Cap = C^ -class K(val f: {*} T): - def setf(x: {f} T) = ??? +class K(val f: T^): + def setf(x: T^{f}) = ??? class T def test(x: Cap, y: Cap) = - val a: {x, y} T = ??? - def fun(x: K{val f: {a} T}) = x.setf(a) // error + val a: T^{x, y} = ??? + def fun(x: K{val f: T^{a}}) = x.setf(a) // error () \ No newline at end of file diff --git a/tests/neg-custom-args/captures/curried-simplified.check b/tests/neg-custom-args/captures/curried-simplified.check index 5d23a7a4955e..a8e187b1449f 100644 --- a/tests/neg-custom-args/captures/curried-simplified.check +++ b/tests/neg-custom-args/captures/curried-simplified.check @@ -1,42 +1,42 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/curried-simplified.scala:7:28 ---------------------------- 7 | def y1: () -> () -> Int = x1 // error | ^^ - | Found: {x} () -> {x} () -> Int + | Found: () ->? () ->{x} Int | Required: () -> () -> Int | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/curried-simplified.scala:9:28 ---------------------------- 9 | def y2: () -> () => Int = x2 // error | ^^ - | Found: {x} () -> {*} () -> Int + | Found: () ->{x} () ->{*} Int | Required: () -> () => Int | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/curried-simplified.scala:11:39 --------------------------- 11 | def y3: Cap -> Protect[Int -> Int] = x3 // error | ^^ - | Found: ? (x$0: Cap) -> {x$0} Int -> Int + | Found: (x$0: Cap) ->? Int ->{x$0} Int | Required: Cap -> Protect[Int -> Int] | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/curried-simplified.scala:15:33 --------------------------- -15 | def y5: Cap -> {} Int -> Int = x5 // error - | ^^ - | Found: ? Cap -> {x} Int -> Int - | Required: Cap -> {} Int -> Int +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/curried-simplified.scala:15:32 --------------------------- +15 | def y5: Cap -> Int ->{} Int = x5 // error + | ^^ + | Found: Cap ->? Int ->{x} Int + | Required: Cap -> Int ->{} Int | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/curried-simplified.scala:17:49 --------------------------- -17 | def y6: Cap -> {} Cap -> Protect[Int -> Int] = x6 // error - | ^^ - | Found: ? (x$0: Cap) -> {x$0} (x$0: Cap) -> {x$0, x$0} Int -> Int - | Required: Cap -> {} Cap -> Protect[Int -> Int] +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/curried-simplified.scala:17:48 --------------------------- +17 | def y6: Cap -> Cap ->{} Protect[Int -> Int] = x6 // error + | ^^ + | Found: (x$0: Cap) ->? (x$0: Cap) ->{x$0} Int ->{x$0, x$0} Int + | Required: Cap -> Cap ->{} Protect[Int -> Int] | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/curried-simplified.scala:19:49 --------------------------- -19 | def y7: Cap -> Protect[Cap -> {} Int -> Int] = x7 // error - | ^^ - | Found: ? (x$0: Cap) -> {x$0} (x: Cap) -> {x$0, x} Int -> Int - | Required: Cap -> Protect[Cap -> {} Int -> Int] +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/curried-simplified.scala:19:48 --------------------------- +19 | def y7: Cap -> Protect[Cap -> Int ->{} Int] = x7 // error + | ^^ + | Found: (x$0: Cap) ->? (x: Cap) ->{x$0} Int ->{x$0, x} Int + | Required: Cap -> Protect[Cap -> Int ->{} Int] | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/curried-simplified.scala b/tests/neg-custom-args/captures/curried-simplified.scala index 25b23370d154..988cf7c11c45 100644 --- a/tests/neg-custom-args/captures/curried-simplified.scala +++ b/tests/neg-custom-args/captures/curried-simplified.scala @@ -3,19 +3,19 @@ type Protect[T] = T def test(x: Cap, y: Cap) = - def x1: {x} () -> () -> Int = ??? + def x1: () -> () ->{x} Int = ??? def y1: () -> () -> Int = x1 // error - def x2: {x} () -> () => Int = ??? + def x2: () ->{x} () => Int = ??? def y2: () -> () => Int = x2 // error def x3: Cap -> Int -> Int = ??? def y3: Cap -> Protect[Int -> Int] = x3 // error def x4: Cap -> Protect[Int -> Int] = ??? - def y4: Cap -> {} Int -> Int = x4 // ok - def x5: Cap -> {x} Int -> Int = ??? - def y5: Cap -> {} Int -> Int = x5 // error + def y4: Cap -> Int ->{} Int = x4 // ok + def x5: Cap -> Int ->{x} Int = ??? + def y5: Cap -> Int ->{} Int = x5 // error def x6: Cap -> Cap -> Int -> Int = ??? - def y6: Cap -> {} Cap -> Protect[Int -> Int] = x6 // error + def y6: Cap -> Cap ->{} Protect[Int -> Int] = x6 // error def x7: Cap -> (x: Cap) -> Int -> Int = ??? - def y7: Cap -> Protect[Cap -> {} Int -> Int] = x7 // error + def y7: Cap -> Protect[Cap -> Int ->{} Int] = x7 // error diff --git a/tests/neg-custom-args/captures/eta.check b/tests/neg-custom-args/captures/eta.check index ebd63855181b..a77d66382095 100644 --- a/tests/neg-custom-args/captures/eta.check +++ b/tests/neg-custom-args/captures/eta.check @@ -1,14 +1,14 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/eta.scala:4:9 -------------------------------------------- 4 | g // error | ^ - | Found: ? () -> A - | Required: () -> {f} Proc + | Found: () ->? A + | Required: () -> Proc^{f} | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/eta.scala:6:14 ------------------------------------------- 6 | bar( () => f ) // error | ^^^^^^^ - | Found: {f} () -> box {f} () -> Unit - | Required: () -> box ? () -> Unit + | Found: () ->{f} box () ->{f} Unit + | Required: () -> box () ->? Unit | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/eta.scala b/tests/neg-custom-args/captures/eta.scala index 3d9d759d2203..5cc0196a04c6 100644 --- a/tests/neg-custom-args/captures/eta.scala +++ b/tests/neg-custom-args/captures/eta.scala @@ -1,7 +1,7 @@ - type Proc = (() -> Unit) - def foo(f: {*} Proc): {} Proc = - def bar[A <: {f} Proc](g: () -> A): () -> {f} Proc = + type Proc = () -> Unit + def foo(f: Proc^): Proc^{} = + def bar[A <: Proc^{f}](g: () -> A): () -> Proc^{f} = g // error - val stowaway: () -> {f} Proc = + val stowaway: () -> Proc^{f} = bar( () => f ) // error () => { stowaway.apply().apply() } \ No newline at end of file diff --git a/tests/neg-custom-args/captures/exception-definitions.check b/tests/neg-custom-args/captures/exception-definitions.check index aca5d9217d64..29bdbefcb843 100644 --- a/tests/neg-custom-args/captures/exception-definitions.check +++ b/tests/neg-custom-args/captures/exception-definitions.check @@ -2,16 +2,16 @@ 2 |class Err extends Exception: // error |^ |reference (scala.caps.* : Any) is not included in allowed capture set {} of pure base class class Throwable -3 | self: {*} Err => +3 | self: Err^{*} => -- Error: tests/neg-custom-args/captures/exception-definitions.scala:10:6 ---------------------------------------------- -10 |class Err4(c: {*} Any) extends AnyVal // error +10 |class Err4(c: Any^{*}) extends AnyVal // error |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |reference (Err4.this.c : {*} Any) is not included in allowed capture set {} of pure base class class AnyVal + |reference (Err4.this.c : Any^{*}) is not included in allowed capture set {} of pure base class class AnyVal -- Error: tests/neg-custom-args/captures/exception-definitions.scala:7:12 ---------------------------------------------- 7 | val x = c // error | ^ - |(c : {*} Any) cannot be referenced here; it is not included in the allowed capture set {} of pure base class class Throwable + |(c : Any^{*}) cannot be referenced here; it is not included in the allowed capture set {} of pure base class class Throwable -- Error: tests/neg-custom-args/captures/exception-definitions.scala:8:8 ----------------------------------------------- -8 | class Err3(c: {*} Any) extends Exception // error +8 | class Err3(c: Any^{*}) extends Exception // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | reference (Err3.this.c : {*} Any) is not included in allowed capture set {} of pure base class class Throwable + | reference (Err3.this.c : Any^{*}) is not included in allowed capture set {} of pure base class class Throwable diff --git a/tests/neg-custom-args/captures/exception-definitions.scala b/tests/neg-custom-args/captures/exception-definitions.scala index 9f3539b7febf..5ecd5eb85cf7 100644 --- a/tests/neg-custom-args/captures/exception-definitions.scala +++ b/tests/neg-custom-args/captures/exception-definitions.scala @@ -1,12 +1,12 @@ class Err extends Exception: // error - self: {*} Err => + self: Err^{*} => -def test(c: {*} Any) = +def test(c: Any^{*}) = class Err2 extends Exception: val x = c // error - class Err3(c: {*} Any) extends Exception // error + class Err3(c: Any^{*}) extends Exception // error -class Err4(c: {*} Any) extends AnyVal // error +class Err4(c: Any^{*}) extends AnyVal // error diff --git a/tests/neg-custom-args/captures/i15116.check b/tests/neg-custom-args/captures/i15116.check index 7c73a7ff52ff..844ce71d0fff 100644 --- a/tests/neg-custom-args/captures/i15116.check +++ b/tests/neg-custom-args/captures/i15116.check @@ -2,27 +2,27 @@ 3 | val x = Foo(m) // error | ^^^^^^^^^^^^^^ | Non-local value x cannot have an inferred type - | {Bar.this.m} Foo{val m: {Bar.this.m} String} + | Foo{val m: String^{Bar.this.m}}^{Bar.this.m} | with non-empty capture set {Bar.this.m}. | The type needs to be declared explicitly. -- Error: tests/neg-custom-args/captures/i15116.scala:5:6 -------------------------------------------------------------- 5 | val x = Foo(m) // error | ^^^^^^^^^^^^^^ | Non-local value x cannot have an inferred type - | {Baz.this} Foo{val m: {*} String} + | Foo{val m: String^{*}}^{Baz.this} | with non-empty capture set {Baz.this}. | The type needs to be declared explicitly. -- Error: tests/neg-custom-args/captures/i15116.scala:7:6 -------------------------------------------------------------- 7 | val x = Foo(m) // error | ^^^^^^^^^^^^^^ | Non-local value x cannot have an inferred type - | {Bar1.this.m} Foo{val m: {Bar1.this.m} String} + | Foo{val m: String^{Bar1.this.m}}^{Bar1.this.m} | with non-empty capture set {Bar1.this.m}. | The type needs to be declared explicitly. -- Error: tests/neg-custom-args/captures/i15116.scala:9:6 -------------------------------------------------------------- 9 | val x = Foo(m) // error | ^^^^^^^^^^^^^^ | Non-local value x cannot have an inferred type - | {Baz2.this} Foo{val m: {*} String} + | Foo{val m: String^{*}}^{Baz2.this} | with non-empty capture set {Baz2.this}. | The type needs to be declared explicitly. diff --git a/tests/neg-custom-args/captures/i15116.scala b/tests/neg-custom-args/captures/i15116.scala index 1659f251df3e..e1c23e4c6aef 100644 --- a/tests/neg-custom-args/captures/i15116.scala +++ b/tests/neg-custom-args/captures/i15116.scala @@ -1,9 +1,9 @@ -class Foo(m: {*} String) -class Bar(val m: {*} String): +class Foo(m: String^{*}) +class Bar(val m: String^{*}): val x = Foo(m) // error -trait Baz(val m: {*} String): +trait Baz(val m: String^{*}): val x = Foo(m) // error -class Bar1(m: {*} String): +class Bar1(m: String^{*}): val x = Foo(m) // error -trait Baz2(m: {*} String): +trait Baz2(m: String^{*}): val x = Foo(m) // error diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index a587f2d262ed..f26d929bbdcd 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -1,28 +1,28 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:20:49 --------------------------------------- -20 | val boxed1 : (({*} C) => Unit) -> Unit = box1(c) // error +20 | val boxed1 : ((C^{*}) => Unit) -> Unit = box1(c) // error | ^^^^^^^ - | Found: {c} ({*} ({c} C{val arg: {*} C}) -> Unit) -> Unit - | Required: (({*} C) => Unit) -> Unit + | Found: (C{val arg: C^{*}}^{c} ->{*} Unit) ->{c} Unit + | Required: (C^{*} => Unit) -> Unit | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:27:38 --------------------------------------- -27 | val boxed2 : Observe[{*} C] = box2(c) // error +27 | val boxed2 : Observe[C^{*}] = box2(c) // error | ^^^^^^^ - | Found: {c} ({*} ({c} C{val arg: {*} C}) -> Unit) -> Unit - | Required: Observe[{*} C] + | Found: (C{val arg: C^{*}}^{c} ->{*} Unit) ->{c} Unit + | Required: Observe[C^{*}] | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:33:37 --------------------------------------- -33 | val boxed2 : Observe[{*} C] = box2(c) // error +33 | val boxed2 : Observe[C]^{*} = box2(c) // error | ^ - | Found: {*} C - | Required: box {*} C{val arg: ? C} + | Found: C^{*} + | Required: box C{val arg: C^?}^{*} | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:44:2 ---------------------------------------- 44 | x: (() -> Unit) // error | ^ - | Found: {x} () -> Unit + | Found: () ->{x} Unit | Required: () -> Unit | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/i15772.scala b/tests/neg-custom-args/captures/i15772.scala index d3afdb6c63f1..a22bbea742be 100644 --- a/tests/neg-custom-args/captures/i15772.scala +++ b/tests/neg-custom-args/captures/i15772.scala @@ -1,6 +1,6 @@ type Observe[T] = (T => Unit) -> Unit -def unsafe(cap: {*} C) = cap.bad() +def unsafe(cap: C^{*}) = cap.bad() def box1[T](v: T) : (T => Unit) -> Unit = { (fn: T => Unit) => fn(v) @@ -10,35 +10,35 @@ def box2[T](v: T) : Observe[T] = { (fn: T => Unit) => fn(v) } -class C(val arg: {*} C) { +class C(val arg: C^{*}) { def bad() = println("I've gone bad!") } -def main1(x: {*} C) : () -> Int = +def main1(x: C^{*}) : () -> Int = () => - val c : {x} C = new C(x) - val boxed1 : (({*} C) => Unit) -> Unit = box1(c) // error - boxed1((cap: {*} C) => unsafe(c)) + val c : C^{x} = new C(x) + val boxed1 : ((C^{*}) => Unit) -> Unit = box1(c) // error + boxed1((cap: C^{*}) => unsafe(c)) 0 -def main2(x: {*} C) : () -> Int = +def main2(x: C^{*}) : () -> Int = () => - val c : {x} C = new C(x) - val boxed2 : Observe[{*} C] = box2(c) // error - boxed2((cap: {*} C) => unsafe(c)) + val c : C^{x} = new C(x) + val boxed2 : Observe[C^{*}] = box2(c) // error + boxed2((cap: C^{*}) => unsafe(c)) 0 -def main3(x: {*} C) = - def c : {*} C = new C(x) - val boxed2 : Observe[{*} C] = box2(c) // error - boxed2((cap: {*} C) => unsafe(c)) +def main3(x: C^{*}) = + def c : C^{*} = new C(x) + val boxed2 : Observe[C]^{*} = box2(c) // error + boxed2((cap: C^{*}) => unsafe(c)) 0 trait File: def write(s: String): Unit -def main(io: {*} Any) = - val sayHello: (({io} File) => Unit) = (file: {io} File) => file.write("Hello World!\r\n") - val filesList : List[{io} File] = ??? +def main(io: Any^{*}) = + val sayHello: ((File^{io}) => Unit) = (file: File^{io}) => file.write("Hello World!\r\n") + val filesList : List[File]^{io} = ??? val x = () => filesList.foreach(sayHello) x: (() -> Unit) // error diff --git a/tests/neg-custom-args/captures/lazylist.check b/tests/neg-custom-args/captures/lazylist.check index 471e2a038450..78c51e5ee42b 100644 --- a/tests/neg-custom-args/captures/lazylist.check +++ b/tests/neg-custom-args/captures/lazylist.check @@ -1,42 +1,42 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:17:15 ------------------------------------- 17 | def tail = xs() // error | ^^^^ - | Found: {LazyCons.this.xs} lazylists.LazyList[T] + | Found: lazylists.LazyList[T]^{LazyCons.this.xs} | Required: lazylists.LazyList[T] | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:35:29 ------------------------------------- 35 | val ref1c: LazyList[Int] = ref1 // error | ^^^^ - | Found: (ref1 : {cap1} lazylists.LazyCons[Int]{val xs: {cap1} () -> {*} lazylists.LazyList[Int]}) - | Required: lazylists.LazyList[Int] + | Found: (ref1 : lazylists.LazyCons[Int]{val xs: () ->{cap1} lazylists.LazyList[Int]^{*}}^{cap1}) + | Required: lazylists.LazyList[Int] | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:37:36 ------------------------------------- -37 | val ref2c: {ref1} LazyList[Int] = ref2 // error +37 | val ref2c: LazyList[Int]^{ref1} = ref2 // error | ^^^^ - | Found: (ref2 : {cap2, ref1} lazylists.LazyList[Int]) - | Required: {ref1} lazylists.LazyList[Int] + | Found: (ref2 : lazylists.LazyList[Int]^{cap2, ref1}) + | Required: lazylists.LazyList[Int]^{ref1} | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:39:36 ------------------------------------- -39 | val ref3c: {cap2} LazyList[Int] = ref3 // error +39 | val ref3c: LazyList[Int]^{cap2} = ref3 // error | ^^^^ - | Found: (ref3 : {cap2, ref1} lazylists.LazyList[Int]) - | Required: {cap2} lazylists.LazyList[Int] + | Found: (ref3 : lazylists.LazyList[Int]^{cap2, ref1}) + | Required: lazylists.LazyList[Int]^{cap2} | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:41:48 ------------------------------------- -41 | val ref4c: {cap1, ref3, cap3} LazyList[Int] = ref4 // error +41 | val ref4c: LazyList[Int]^{cap1, ref3, cap3} = ref4 // error | ^^^^ - | Found: (ref4 : {cap3, cap2, ref1, cap1} lazylists.LazyList[Int]) - | Required: {cap1, ref3, cap3} lazylists.LazyList[Int] + | Found: (ref4 : lazylists.LazyList[Int]^{cap3, cap2, ref1, cap1}) + | Required: lazylists.LazyList[Int]^{cap1, ref3, cap3} | | longer explanation available when compiling with `-explain` -- [E164] Declaration Error: tests/neg-custom-args/captures/lazylist.scala:22:6 ---------------------------------------- -22 | def tail: {*} LazyList[Nothing] = ??? // error overriding +22 | def tail: LazyList[Nothing]^{*} = ??? // error overriding | ^ | error overriding method tail in class LazyList of type -> lazylists.LazyList[Nothing]; - | method tail of type -> {*} lazylists.LazyList[Nothing] has incompatible type + | method tail of type -> lazylists.LazyList[Nothing]^{*} has incompatible type | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/lazylist.scala b/tests/neg-custom-args/captures/lazylist.scala index 2674f15a0ee3..6b4cf00e5566 100644 --- a/tests/neg-custom-args/captures/lazylist.scala +++ b/tests/neg-custom-args/captures/lazylist.scala @@ -1,17 +1,17 @@ package lazylists abstract class LazyList[+T]: - this: ({*} LazyList[T]) => + this: LazyList[T]^{*} => def isEmpty: Boolean def head: T def tail: LazyList[T] - def map[U](f: T => U): {f, this} LazyList[U] = + def map[U](f: T => U): LazyList[U]^{f, this} = if isEmpty then LazyNil else LazyCons(f(head), () => tail.map(f)) -class LazyCons[+T](val x: T, val xs: () => {*} LazyList[T]) extends LazyList[T]: +class LazyCons[+T](val x: T, val xs: () => LazyList[T]^{*}) extends LazyList[T]: def isEmpty = false def head = x def tail = xs() // error @@ -19,13 +19,13 @@ class LazyCons[+T](val x: T, val xs: () => {*} LazyList[T]) extends LazyList[T]: object LazyNil extends LazyList[Nothing]: def isEmpty = true def head = ??? - def tail: {*} LazyList[Nothing] = ??? // error overriding + def tail: LazyList[Nothing]^{*} = ??? // error overriding -def map[A, B](xs: {*} LazyList[A], f: A => B): {f, xs} LazyList[B] = +def map[A, B](xs: LazyList[A]^{*}, f: A => B): LazyList[B]^{f, xs} = xs.map(f) class CC -type Cap = {*} CC +type Cap = CC^{*} def test(cap1: Cap, cap2: Cap, cap3: Cap) = def f[T](x: LazyList[T]): LazyList[T] = if cap1 == cap1 then x else LazyNil @@ -34,8 +34,8 @@ def test(cap1: Cap, cap2: Cap, cap3: Cap) = val ref1 = LazyCons(1, () => f(LazyNil)) val ref1c: LazyList[Int] = ref1 // error val ref2 = map(ref1, g) - val ref2c: {ref1} LazyList[Int] = ref2 // error + val ref2c: LazyList[Int]^{ref1} = ref2 // error val ref3 = ref1.map(g) - val ref3c: {cap2} LazyList[Int] = ref3 // error + val ref3c: LazyList[Int]^{cap2} = ref3 // error val ref4 = (if cap1 == cap2 then ref1 else ref2).map(h) - val ref4c: {cap1, ref3, cap3} LazyList[Int] = ref4 // error + val ref4c: LazyList[Int]^{cap1, ref3, cap3} = ref4 // error diff --git a/tests/neg-custom-args/captures/lazylists-exceptions.check b/tests/neg-custom-args/captures/lazylists-exceptions.check index bd6fad047fe9..c8f835f4fdfa 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.check +++ b/tests/neg-custom-args/captures/lazylists-exceptions.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/lazylists-exceptions.scala:36:2 ----------------------------------------------- 36 | try // error | ^ - | The expression's type {*} LazyList[Int] is not allowed to capture the root capability `*`. + | The expression's type LazyList[Int]^{*} is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. 37 | tabulate(10) { i => 38 | if i > 9 then throw Ex1() diff --git a/tests/neg-custom-args/captures/lazylists-exceptions.scala b/tests/neg-custom-args/captures/lazylists-exceptions.scala index 6cba934d61e8..a2b902e83cb9 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.scala +++ b/tests/neg-custom-args/captures/lazylists-exceptions.scala @@ -1,31 +1,31 @@ import language.experimental.saferExceptions trait LazyList[+A]: - this: {*} LazyList[A] => + this: LazyList[A]^ => def isEmpty: Boolean def head: A - def tail: {this} LazyList[A] + def tail: LazyList[A]^{this} object LazyNil extends LazyList[Nothing]: def isEmpty: Boolean = true def head = ??? def tail = ??? -final class LazyCons[+T](val x: T, val xs: () => {*} LazyList[T]) extends LazyList[T]: - this: {*} LazyList[T] => +final class LazyCons[+T](val x: T, val xs: () => LazyList[T]^{*}) extends LazyList[T]: + this: LazyList[T]^{*} => def isEmpty = false def head = x - def tail: {this} LazyList[T] = xs() + def tail: LazyList[T]^{this} = xs() end LazyCons extension [A](x: A) - def #:(xs1: => {*} LazyList[A]): {xs1} LazyList[A] = + def #:(xs1: => LazyList[A]^{*}): LazyList[A]^{xs1} = LazyCons(x, () => xs1) -def tabulate[A](n: Int)(gen: Int => A): {gen} LazyList[A] = - def recur(i: Int): {gen} LazyList[A] = +def tabulate[A](n: Int)(gen: Int => A): LazyList[A]^{gen} = + def recur(i: Int): LazyList[A]^{gen} = if i == n then LazyNil else gen(i) #: recur(i + 1) recur(0) diff --git a/tests/neg-custom-args/captures/lazylists1.check b/tests/neg-custom-args/captures/lazylists1.check index f91e2500dc15..127a0563c3c9 100644 --- a/tests/neg-custom-args/captures/lazylists1.check +++ b/tests/neg-custom-args/captures/lazylists1.check @@ -1,7 +1,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylists1.scala:25:66 ----------------------------------- -25 | def concat(other: {f} LazyList[A]): {this, f} LazyList[A] = ??? : ({xs, f} LazyList[A]) // error +25 | def concat(other: LazyList[A]^{f}): LazyList[A]^{this, f} = ??? : (LazyList[A]^{xs, f}) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: {xs, f} LazyList[A] - | Required: {Mapped.this, f} LazyList[A] + | Found: LazyList[A]^{xs, f} + | Required: LazyList[A]^{Mapped.this, f} | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/lazylists1.scala b/tests/neg-custom-args/captures/lazylists1.scala index c6475223b783..88d9e5672b5d 100644 --- a/tests/neg-custom-args/captures/lazylists1.scala +++ b/tests/neg-custom-args/captures/lazylists1.scala @@ -1,27 +1,27 @@ class CC -type Cap = {*} CC +type Cap = CC^{*} trait LazyList[+A]: - this: ({*} LazyList[A]) => + this: LazyList[A]^{*} => def isEmpty: Boolean def head: A - def tail: {this} LazyList[A] + def tail: LazyList[A]^{this} object LazyNil extends LazyList[Nothing]: def isEmpty: Boolean = true def head = ??? def tail = ??? -extension [A](xs: {*} LazyList[A]) - def map[B](f: A => B): {xs, f} LazyList[B] = +extension [A](xs: LazyList[A]^{*}) + def map[B](f: A => B): LazyList[B]^{xs, f} = final class Mapped extends LazyList[B]: - this: ({xs, f} Mapped) => + this: (Mapped^{xs, f}) => def isEmpty = false def head: B = f(xs.head) - def tail: {this} LazyList[B] = xs.tail.map(f) // OK - def drop(n: Int): {this} LazyList[B] = ??? : ({xs, f} LazyList[B]) // OK - def concat(other: {f} LazyList[A]): {this, f} LazyList[A] = ??? : ({xs, f} LazyList[A]) // error + def tail: LazyList[B]^{this} = xs.tail.map(f) // OK + def drop(n: Int): LazyList[B]^{this} = ??? : (LazyList[B]^{xs, f}) // OK + def concat(other: LazyList[A]^{f}): LazyList[A]^{this, f} = ??? : (LazyList[A]^{xs, f}) // error new Mapped diff --git a/tests/neg-custom-args/captures/lazylists2.check b/tests/neg-custom-args/captures/lazylists2.check index 812170aabdfe..72efbc08f8e2 100644 --- a/tests/neg-custom-args/captures/lazylists2.check +++ b/tests/neg-custom-args/captures/lazylists2.check @@ -1,24 +1,24 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylists2.scala:18:4 ------------------------------------ 18 | final class Mapped extends LazyList[B]: // error | ^ - | Found: {f, xs} LazyList[? B] - | Required: {f} LazyList[B] -19 | this: ({xs, f} Mapped) => + | Found: LazyList[B^?]^{f, xs} + | Required: LazyList[B]^{f} +19 | this: (Mapped^{xs, f}) => 20 | def isEmpty = false 21 | def head: B = f(xs.head) -22 | def tail: {this} LazyList[B] = xs.tail.map(f) +22 | def tail: LazyList[B]^{this} = xs.tail.map(f) 23 | new Mapped | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylists2.scala:27:4 ------------------------------------ 27 | final class Mapped extends LazyList[B]: // error | ^ - | Found: {f, xs} LazyList[? B] - | Required: {xs} LazyList[B] -28 | this: ({xs, f} Mapped) => + | Found: LazyList[B^?]^{f, xs} + | Required: LazyList[B]^{xs} +28 | this: Mapped^{xs, f} => 29 | def isEmpty = false 30 | def head: B = f(xs.head) -31 | def tail: {this} LazyList[B] = xs.tail.map(f) +31 | def tail: LazyList[B]^{this} = xs.tail.map(f) 32 | new Mapped | | longer explanation available when compiling with `-explain` @@ -26,19 +26,19 @@ 40 | def head: B = f(xs.head) // error | ^ |(f : A => B) cannot be referenced here; it is not included in the allowed capture set {xs} of the self type of class Mapped --- Error: tests/neg-custom-args/captures/lazylists2.scala:41:49 -------------------------------------------------------- -41 | def tail: {this} LazyList[B] = xs.tail.map(f) // error - | ^ +-- Error: tests/neg-custom-args/captures/lazylists2.scala:41:48 -------------------------------------------------------- +41 | def tail: LazyList[B]^{this}= xs.tail.map(f) // error + | ^ |(f : A => B) cannot be referenced here; it is not included in the allowed capture set {xs} of the self type of class Mapped -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylists2.scala:45:4 ------------------------------------ 45 | final class Mapped extends LazyList[B]: // error | ^ - | Found: {f, xs} LazyList[? B] - | Required: {xs} LazyList[B] -46 | this: ({xs, f} Mapped) => + | Found: LazyList[B^?]^{f, xs} + | Required: LazyList[B]^{xs} +46 | this: (Mapped^{xs, f}) => 47 | def isEmpty = false 48 | def head: B = f(xs.head) -49 | def tail: {xs, f} LazyList[B] = xs.tail.map(f) +49 | def tail: LazyList[B]^{xs, f} = xs.tail.map(f) 50 | new Mapped | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/lazylists2.scala b/tests/neg-custom-args/captures/lazylists2.scala index 574fb5a1a488..f6c1cf95a8ed 100644 --- a/tests/neg-custom-args/captures/lazylists2.scala +++ b/tests/neg-custom-args/captures/lazylists2.scala @@ -1,62 +1,62 @@ class CC -type Cap = {*} CC +type Cap = CC^ trait LazyList[+A]: - this: ({*} LazyList[A]) => + this: LazyList[A]^ => def isEmpty: Boolean def head: A - def tail: {this} LazyList[A] + def tail: LazyList[A]^{this} object LazyNil extends LazyList[Nothing]: def isEmpty: Boolean = true def head = ??? def tail = ??? -extension [A](xs: {*} LazyList[A]) - def map[B](f: A => B): {f} LazyList[B] = +extension [A](xs: LazyList[A]^) + def map[B](f: A => B): LazyList[B]^{f} = final class Mapped extends LazyList[B]: // error - this: ({xs, f} Mapped) => + this: (Mapped^{xs, f}) => def isEmpty = false def head: B = f(xs.head) - def tail: {this} LazyList[B] = xs.tail.map(f) + def tail: LazyList[B]^{this} = xs.tail.map(f) new Mapped - def map2[B](f: A => B): {xs} LazyList[B] = + def map2[B](f: A => B): LazyList[B]^{xs} = final class Mapped extends LazyList[B]: // error - this: ({xs, f} Mapped) => + this: Mapped^{xs, f} => def isEmpty = false def head: B = f(xs.head) - def tail: {this} LazyList[B] = xs.tail.map(f) + def tail: LazyList[B]^{this} = xs.tail.map(f) new Mapped - def map3[B](f: A => B): {xs} LazyList[B] = + def map3[B](f: A => B): LazyList[B]^{xs} = final class Mapped extends LazyList[B]: - this: ({xs} Mapped) => + this: Mapped^{xs} => def isEmpty = false def head: B = f(xs.head) // error - def tail: {this} LazyList[B] = xs.tail.map(f) // error + def tail: LazyList[B]^{this}= xs.tail.map(f) // error new Mapped - def map4[B](f: A => B): {xs} LazyList[B] = + def map4[B](f: A => B): LazyList[B]^{xs} = final class Mapped extends LazyList[B]: // error - this: ({xs, f} Mapped) => + this: (Mapped^{xs, f}) => def isEmpty = false def head: B = f(xs.head) - def tail: {xs, f} LazyList[B] = xs.tail.map(f) + def tail: LazyList[B]^{xs, f} = xs.tail.map(f) new Mapped def map5[B](f: A => B): LazyList[B] = class Mapped extends LazyList[B]: - this: ({xs, f} Mapped) => + this: (Mapped^{xs, f}) => def isEmpty = false def head: B = f(xs.head) - def tail: {this} LazyList[B] = xs.tail.map(f) + def tail: LazyList[B]^{this} = xs.tail.map(f) class Mapped2 extends Mapped: // error this: Mapped => new Mapped2 diff --git a/tests/neg-custom-args/captures/lazyref.check b/tests/neg-custom-args/captures/lazyref.check index 7471f8f4f686..9c9e2b757fb7 100644 --- a/tests/neg-custom-args/captures/lazyref.check +++ b/tests/neg-custom-args/captures/lazyref.check @@ -1,28 +1,28 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:19:28 -------------------------------------- 19 | val ref1c: LazyRef[Int] = ref1 // error | ^^^^ - | Found: (ref1 : {cap1} LazyRef[Int]{val elem: {cap1} () -> Int}) + | Found: (ref1 : LazyRef[Int]{val elem: () ->{cap1} Int}^{cap1}) | Required: LazyRef[Int] | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:21:35 -------------------------------------- -21 | val ref2c: {cap2} LazyRef[Int] = ref2 // error +21 | val ref2c: LazyRef[Int]^{cap2} = ref2 // error | ^^^^ - | Found: (ref2 : {cap2, ref1} LazyRef[Int]{val elem: {*} () -> Int}) - | Required: {cap2} LazyRef[Int] + | Found: (ref2 : LazyRef[Int]{val elem: () ->{*} Int}^{cap2, ref1}) + | Required: LazyRef[Int]^{cap2} | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:23:35 -------------------------------------- -23 | val ref3c: {ref1} LazyRef[Int] = ref3 // error +23 | val ref3c: LazyRef[Int]^{ref1} = ref3 // error | ^^^^ - | Found: (ref3 : {cap2, ref1} LazyRef[Int]{val elem: {*} () -> Int}) - | Required: {ref1} LazyRef[Int] + | Found: (ref3 : LazyRef[Int]{val elem: () ->{*} Int}^{cap2, ref1}) + | Required: LazyRef[Int]^{ref1} | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:25:35 -------------------------------------- -25 | val ref4c: {cap1} LazyRef[Int] = ref4 // error +25 | val ref4c: LazyRef[Int]^{cap1} = ref4 // error | ^^^^ - | Found: (ref4 : {cap2, cap1} LazyRef[Int]{val elem: {*} () -> Int}) - | Required: {cap1} LazyRef[Int] + | Found: (ref4 : LazyRef[Int]{val elem: () ->{*} Int}^{cap2, cap1}) + | Required: LazyRef[Int]^{cap1} | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/lazyref.scala b/tests/neg-custom-args/captures/lazyref.scala index 8395e5cb42cd..54e64b4b4b4b 100644 --- a/tests/neg-custom-args/captures/lazyref.scala +++ b/tests/neg-custom-args/captures/lazyref.scala @@ -3,13 +3,13 @@ type Cap = {*} CC class LazyRef[T](val elem: () => T): val get: () => T = elem - def map[U](f: T => U): {f, this} LazyRef[U] = + def map[U](f: T => U): LazyRef[U]^{f, this} = new LazyRef(() => f(elem())) -def map[A, B](ref: {*} LazyRef[A], f: A => B): {f, ref} LazyRef[B] = +def map[A, B](ref: LazyRef[A]^{*}, f: A => B): LazyRef[B]^{f, ref} = new LazyRef(() => f(ref.elem())) -def mapc[A, B]: (ref: {*} LazyRef[A], f: A => B) -> {f, ref} LazyRef[B] = +def mapc[A, B]: (ref: LazyRef[A]^{*}, f: A => B) -> LazyRef[B]^{f, ref} = (ref1, f1) => map[A, B](ref1, f1) def test(cap1: Cap, cap2: Cap) = @@ -18,8 +18,8 @@ def test(cap1: Cap, cap2: Cap) = val ref1 = LazyRef(() => f(0)) val ref1c: LazyRef[Int] = ref1 // error val ref2 = map(ref1, g) - val ref2c: {cap2} LazyRef[Int] = ref2 // error + val ref2c: LazyRef[Int]^{cap2} = ref2 // error val ref3 = ref1.map(g) - val ref3c: {ref1} LazyRef[Int] = ref3 // error + val ref3c: LazyRef[Int]^{ref1} = ref3 // error val ref4 = (if cap1 == cap2 then ref1 else ref2).map(g) - val ref4c: {cap1} LazyRef[Int] = ref4 // error + val ref4c: LazyRef[Int]^{cap1} = ref4 // error diff --git a/tests/neg-custom-args/captures/nestedclass.check b/tests/neg-custom-args/captures/nestedclass.check index cb4421ece0ec..2987318caf4f 100644 --- a/tests/neg-custom-args/captures/nestedclass.check +++ b/tests/neg-custom-args/captures/nestedclass.check @@ -1,7 +1,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/nestedclass.scala:15:15 ---------------------------------- 15 | val xsc: C = xs // error | ^^ - | Found: (xs : {cap1} C) + | Found: (xs : C^{cap1}) | Required: C | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/nestedclass.scala b/tests/neg-custom-args/captures/nestedclass.scala index 38adf7998868..5e5900694228 100644 --- a/tests/neg-custom-args/captures/nestedclass.scala +++ b/tests/neg-custom-args/captures/nestedclass.scala @@ -1,5 +1,5 @@ class CC -type Cap = {*} CC +type Cap = CC^{*} abstract class C: def head: String diff --git a/tests/neg-custom-args/captures/real-try.check b/tests/neg-custom-args/captures/real-try.check index 9745470f219c..b991e3fe09c1 100644 --- a/tests/neg-custom-args/captures/real-try.check +++ b/tests/neg-custom-args/captures/real-try.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:12:2 ----------------------------------------------------------- 12 | try // error | ^ - | The expression's type {*} () -> Unit is not allowed to capture the root capability `*`. + | The expression's type () ->{*} Unit is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. 13 | () => foo(1) 14 | catch @@ -10,7 +10,7 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:18:2 ----------------------------------------------------------- 18 | try // error | ^ - | The expression's type {*} () -> ? Cell[Unit] is not allowed to capture the root capability `*`. + | The expression's type () ->{*} Cell[Unit]^? is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. 19 | () => Cell(foo(1)) 20 | catch @@ -19,5 +19,5 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:30:4 ----------------------------------------------------------- 30 | b.x // error | ^^^ - | The expression's type box {*} () -> Unit is not allowed to capture the root capability `*`. + | The expression's type box () ->{*} Unit is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. diff --git a/tests/neg-custom-args/captures/try.check b/tests/neg-custom-args/captures/try.check index d4bcc859d256..efe71386a183 100644 --- a/tests/neg-custom-args/captures/try.check +++ b/tests/neg-custom-args/captures/try.check @@ -1,8 +1,8 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:23:49 ------------------------------------------ 23 | val a = handle[Exception, CanThrow[Exception]] { // error | ^ - | Found: ? ({*} CT[Exception]) -> CanThrow[Exception] - | Required: {*} CanThrow[Exception] -> box {*} CT[Exception] + | Found: CT[Exception]^{*} ->? CanThrow[Exception] + | Required: CanThrow[Exception] ->{*} box CT[Exception]^{*} 24 | (x: CanThrow[Exception]) => x 25 | }{ | @@ -10,8 +10,8 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:29:43 ------------------------------------------ 29 | val b = handle[Exception, () -> Nothing] { // error | ^ - | Found: ? (x: {*} CT[Exception]) -> {x} () -> Nothing - | Required: {*} (x$0: CanThrow[Exception]) -> () -> Nothing + | Found: (x: CT[Exception]^{*}) ->? () ->{x} Nothing + | Required: (x$0: CanThrow[Exception]) ->{*} () -> Nothing 30 | (x: CanThrow[Exception]) => () => raise(new Exception)(using x) 31 | } { | @@ -24,7 +24,7 @@ 51 | 22 52 |} { // error | ^ - | Found: {x$0} () -> Int + | Found: () ->{x$0} Int | Required: () -> Int 53 | (ex: Exception) => () => 22 54 |} @@ -38,7 +38,7 @@ 39 | 22 40 | } { // error | ^ - | The expression's type box {x$0, *} () -> Int is not allowed to capture the root capability `*`. + | The expression's type box () ->{x$0, *} Int is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. 41 | (ex: Exception) => () => 22 42 | } diff --git a/tests/neg-custom-args/captures/usingLogFile.check b/tests/neg-custom-args/captures/usingLogFile.check index 05fb385a64f7..68d8a4a43508 100644 --- a/tests/neg-custom-args/captures/usingLogFile.check +++ b/tests/neg-custom-args/captures/usingLogFile.check @@ -1,39 +1,39 @@ -- Error: tests/neg-custom-args/captures/usingLogFile.scala:33:2 ------------------------------------------------------- 33 | later3() // error | ^^^^^^ - | box {*} () -> Unit cannot be box-converted to a type that can be selected or applied + | box () ->{*} Unit cannot be box-converted to a type that can be selected or applied | since one of their capture sets contains the root capability `*` -- Error: tests/neg-custom-args/captures/usingLogFile.scala:37:9 ------------------------------------------------------- 37 | later4.x() // error | ^^^^^^^^ - | The expression's type box {*} () -> Unit is not allowed to capture the root capability `*`. + | The expression's type box () ->{*} Unit is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:23:6 ------------------------------------------------------- 23 | val later = usingLogFile { f => () => f.write(0) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Non-local value later cannot have an inferred type - | {x$0} () -> Unit + | () ->{x$0} Unit | with non-empty capture set {x$0}. | The type needs to be declared explicitly. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:29:9 ------------------------------------------------------- 29 | later2.x() // error | ^^^^^^^^ - | The expression's type box {x$0, *} () -> Unit is not allowed to capture the root capability `*`. + | The expression's type box () ->{x$0, *} Unit is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:47:6 ------------------------------------------------------- 47 | val later = usingLogFile { f => () => f.write(0) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Non-local value later cannot have an inferred type - | {x$0} () -> Unit + | () ->{x$0} Unit | with non-empty capture set {x$0}. | The type needs to be declared explicitly. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:62:25 ------------------------------------------------------ 62 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | The expression's type box {x$0, *} (x$0: Int) -> Unit is not allowed to capture the root capability `*`. - | This usually means that a capability persists longer than its allowed lifetime. + | The expression's type box (x$0: Int) ->{x$0, *} Unit is not allowed to capture the root capability `*`. + | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:71:25 ------------------------------------------------------ 71 | val later = usingFile("logfile", usingLogger(_, l => () => l.log("test"))) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | The expression's type box {x$0, *} () -> Unit is not allowed to capture the root capability `*`. + | The expression's type box () ->{x$0, *} Unit is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. diff --git a/tests/neg-custom-args/captures/usingLogFile.scala b/tests/neg-custom-args/captures/usingLogFile.scala index 8b367239050d..b4c40d503e08 100644 --- a/tests/neg-custom-args/captures/usingLogFile.scala +++ b/tests/neg-custom-args/captures/usingLogFile.scala @@ -14,7 +14,7 @@ object Test1: object Test2: - def usingLogFile[T](op: ({*} FileOutputStream) => T): T = + def usingLogFile[T](op: FileOutputStream^{*} => T): T = val logFile = FileOutputStream("log") val result = op(logFile) logFile.close() @@ -38,7 +38,7 @@ object Test2: object Test3: - def usingLogFile[T](op: ({*} FileOutputStream) => T) = + def usingLogFile[T](op: FileOutputStream^{*} => T) = val logFile = FileOutputStream("log") val result = op(logFile) logFile.close() @@ -47,10 +47,10 @@ object Test3: val later = usingLogFile { f => () => f.write(0) } // error object Test4: - class Logger(f: {*} OutputStream): + class Logger(f: OutputStream^{*}): def log(msg: String): Unit = ??? - def usingFile[T](name: String, op: ({*} OutputStream) => T): T = + def usingFile[T](name: String, op: OutputStream^{*} => T): T = val f = new FileOutputStream(name) val result = op(f) f.close() @@ -63,7 +63,7 @@ object Test4: later(1) - def usingLogger[T](f: {*} OutputStream, op: ({f} Logger) => T): T = + def usingLogger[T](f: OutputStream^{*}, op: Logger^{f} => T): T = val logger = Logger(f) op(logger) diff --git a/tests/neg-custom-args/captures/vars.check b/tests/neg-custom-args/captures/vars.check index 4b9ab5723ce6..285a15262bb1 100644 --- a/tests/neg-custom-args/captures/vars.check +++ b/tests/neg-custom-args/captures/vars.check @@ -1,31 +1,31 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars.scala:11:24 ----------------------------------------- 11 | val z2c: () -> Unit = z2 // error | ^^ - | Found: {z2} () -> Unit + | Found: () ->{z2} Unit | Required: () -> Unit | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars.scala:15:10 ----------------------------------------- 15 | val u = a // error | ^ - | Found: (a : box {*} String -> String) - | Required: {*} (x$0: String) -> String + | Found: (a : box String ->{*} String) + | Required: (x$0: String) ->{*} String | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/vars.scala:16:2 --------------------------------------------------------------- 16 | a("") // error | ^ - | box {*} String -> String cannot be box-converted to a type that can be selected or applied + | box String ->{*} String cannot be box-converted to a type that can be selected or applied | since one of their capture sets contains the root capability `*` -- Error: tests/neg-custom-args/captures/vars.scala:17:4 --------------------------------------------------------------- 17 | b.head // error | ^^^^^^ - | The expression's type box {*} String -> String is not allowed to capture the root capability `*`. + | The expression's type box String ->{*} String is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/vars.scala:32:8 --------------------------------------------------------------- 32 | local { cap3 => // error | ^ - | The expression's type box {x$0, *} (x$0: String) -> String is not allowed to capture the root capability `*`. + | The expression's type box (x$0: String) ->{x$0, *} String is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. 33 | def g(x: String): String = if cap3 == cap3 then "" else "a" 34 | g diff --git a/tests/neg-custom-args/captures/vars.scala b/tests/neg-custom-args/captures/vars.scala index 2ad8fec53619..e437d087d5a2 100644 --- a/tests/neg-custom-args/captures/vars.scala +++ b/tests/neg-custom-args/captures/vars.scala @@ -1,12 +1,12 @@ class CC -type Cap = {*} CC +type Cap = CC^ def test(cap1: Cap, cap2: Cap) = def f(x: String): String = if cap1 == cap1 then "" else "a" var x = f val y = x val z = () => if x("") == "" then "a" else "b" - val zc: {cap1} () -> String = z + val zc: () ->{cap1} String = z val z2 = () => { x = identity } val z2c: () -> Unit = z2 // error @@ -35,7 +35,7 @@ def test(cap1: Cap, cap2: Cap) = } class Ref: - var elem: {cap1} String -> String = null + var elem: String ->{cap1} String = null val r = Ref() r.elem = f diff --git a/tests/pos-custom-args/captures/nested-classes.scala b/tests/pos-custom-args/captures/nested-classes.scala index 2e893b7413a5..b16fc4365183 100644 --- a/tests/pos-custom-args/captures/nested-classes.scala +++ b/tests/pos-custom-args/captures/nested-classes.scala @@ -5,10 +5,10 @@ import annotation.{capability, constructorOnly} class Blah class Pkg(using @constructorOnly io: IO): class Foo: - def m(foo: {io} Blah) = ??? + def m(foo: Blah^{io}) = ??? class Pkg2(using io: IO): class Foo: - def m(foo: {io} Blah): Any = io; ??? + def m(foo: Blah^{io}): Any = io; ??? def main(using io: IO) = val pkg = Pkg() From 8aa41030067d94a9cac7c29bf42f713f429c069a Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 30 Apr 2023 17:21:13 +0200 Subject: [PATCH 523/657] Avoid printing root capture set Use ^ and => shorthands instead. --- .../tools/dotc/printing/PlainPrinter.scala | 12 ++++++-- .../tools/dotc/printing/RefinedPrinter.scala | 14 ++++++--- tests/neg-custom-args/captures/capt1.check | 4 +-- tests/neg-custom-args/captures/cc-this3.check | 2 +- tests/neg-custom-args/captures/cc-this3.scala | 4 +-- .../captures/curried-simplified.check | 2 +- .../captures/exception-definitions.check | 16 +++++----- .../captures/exception-definitions.scala | 8 ++--- tests/neg-custom-args/captures/i15116.check | 4 +-- tests/neg-custom-args/captures/i15116.scala | 10 +++---- tests/neg-custom-args/captures/i15772.check | 30 +++++++++---------- tests/neg-custom-args/captures/i15772.scala | 26 ++++++++-------- tests/neg-custom-args/captures/lazylist.check | 8 ++--- tests/neg-custom-args/captures/lazylist.scala | 10 +++---- .../captures/lazylists-exceptions.check | 2 +- .../captures/lazylists-exceptions.scala | 6 ++-- tests/neg-custom-args/captures/lazyref.check | 6 ++-- tests/neg-custom-args/captures/lazyref.scala | 4 +-- .../captures/nestedclass.scala | 2 +- tests/neg-custom-args/captures/real-try.check | 6 ++-- tests/neg-custom-args/captures/try.check | 10 +++---- .../captures/usingLogFile.check | 12 ++++---- .../captures/usingLogFile.scala | 10 +++---- tests/neg-custom-args/captures/vars.check | 10 +++---- 24 files changed, 116 insertions(+), 102 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index bd027ed79431..91e152c57048 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -155,8 +155,15 @@ class PlainPrinter(_ctx: Context) extends Printer { else if !cs.isConst && cs.elems.isEmpty then "?" else "{" ~ Text(cs.elems.toList.map(toTextCaptureRef), ", ") ~ "}" + /** Print capturing type, overridden in RefinedPrinter to account for + * capturing function types. + */ protected def toTextCapturing(parent: Type, refsText: Text, boxText: Text): Text = - changePrec(InfixPrec)(boxText ~ toTextLocal(parent) ~ "^" ~ refsText) + changePrec(InfixPrec): + boxText ~ toTextLocal(parent) ~ "^" + ~ (refsText provided refsText != rootSetText) + + final protected def rootSetText = Str("{*}") def toText(tp: Type): Text = controlled { homogenize(tp) match { @@ -214,7 +221,8 @@ class PlainPrinter(_ctx: Context) extends Printer { }.close case tp @ EventuallyCapturingType(parent, refs) => val boxText: Text = Str("box ") provided tp.isBoxed //&& ctx.settings.YccDebug.value - toTextCapturing(parent, toTextCaptureSet(refs), boxText) + val refsText = if refs.isUniversal then rootSetText else toTextCaptureSet(refs) + toTextCapturing(parent, refsText, boxText) case tp: PreviousErrorType if ctx.settings.XprintTypes.value => "" // do not print previously reported error message because they may try to print this error type again recuresevely case tp: ErrorType => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 338b03c5adb7..2e2ef69ace3f 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -148,7 +148,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val AppliedType(tycon, args) = (tp: @unchecked) val tsym = tycon.typeSymbol val isGiven = tsym.name.isContextFunction - val isPure = Feature.pureFunsEnabled && !tsym.name.isImpureFunction + val capturesRoot = refs == rootSetText + val isPure = + Feature.pureFunsEnabled && !tsym.name.isImpureFunction && !capturesRoot changePrec(GlobalPrec) { val argStr: Text = if args.length == 2 @@ -160,17 +162,21 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "(" ~ argsText(args.init) ~ ")" - argStr ~ " " ~ arrow(isGiven, isPure) ~ refs ~ " " ~ argText(args.last) + argStr + ~ " " ~ arrow(isGiven, isPure) + ~ (refs provided !capturesRoot) + ~ " " ~ argText(args.last) } private def toTextMethodAsFunction(info: Type, isPure: Boolean, refs: Text = Str("")): Text = info match case info: MethodType => + val capturesRoot = refs == rootSetText changePrec(GlobalPrec) { "(" ~ paramsText(info) ~ ") " - ~ arrow(info.isImplicitMethod, isPure) - ~ refs + ~ arrow(info.isImplicitMethod, isPure && !capturesRoot) + ~ (refs provided !capturesRoot) ~ " " ~ toTextMethodAsFunction(info.resultType, isPure) } diff --git a/tests/neg-custom-args/captures/capt1.check b/tests/neg-custom-args/captures/capt1.check index 1e9e8626bdc5..f51d4631782d 100644 --- a/tests/neg-custom-args/captures/capt1.check +++ b/tests/neg-custom-args/captures/capt1.check @@ -41,13 +41,13 @@ 32 | val z2 = h[() -> Cap](() => x) // error | ^^^^^^^ | Found: () ->{x} Cap - | Required: () -> box C^{*} + | Required: () -> box C^ | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:33:5 ----------------------------------------- 33 | (() => C()) // error | ^^^^^^^^^ | Found: () ->? Cap - | Required: () -> box C^{*} + | Required: () -> box C^ | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/cc-this3.check b/tests/neg-custom-args/captures/cc-this3.check index ceb2694e8f15..d57471c6872e 100644 --- a/tests/neg-custom-args/captures/cc-this3.check +++ b/tests/neg-custom-args/captures/cc-this3.check @@ -1,7 +1,7 @@ -- [E058] Type Mismatch Error: tests/neg-custom-args/captures/cc-this3.scala:8:6 --------------------------------------- 8 |class B extends A: // error | ^ - | illegal inheritance: self type B^{*} of class B does not conform to self type A^{} + | illegal inheritance: self type B^ of class B does not conform to self type A^{} | of parent class A | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/cc-this3.scala b/tests/neg-custom-args/captures/cc-this3.scala index 61ccdb214b30..25af19dd6c4a 100644 --- a/tests/neg-custom-args/captures/cc-this3.scala +++ b/tests/neg-custom-args/captures/cc-this3.scala @@ -6,13 +6,13 @@ class A: val x: A = this class B extends A: // error - this: B^{*} => + this: B^ => class C(val f: () => Int) extends A // error class A2 class B2 extends A2: // ok - this: B2^{*} => + this: B2^ => class C2(val f: () => Int) extends A2 // ok diff --git a/tests/neg-custom-args/captures/curried-simplified.check b/tests/neg-custom-args/captures/curried-simplified.check index a8e187b1449f..6a792314e4e3 100644 --- a/tests/neg-custom-args/captures/curried-simplified.check +++ b/tests/neg-custom-args/captures/curried-simplified.check @@ -8,7 +8,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/curried-simplified.scala:9:28 ---------------------------- 9 | def y2: () -> () => Int = x2 // error | ^^ - | Found: () ->{x} () ->{*} Int + | Found: () ->{x} () => Int | Required: () -> () => Int | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/exception-definitions.check b/tests/neg-custom-args/captures/exception-definitions.check index 29bdbefcb843..23dbe0fb6529 100644 --- a/tests/neg-custom-args/captures/exception-definitions.check +++ b/tests/neg-custom-args/captures/exception-definitions.check @@ -2,16 +2,16 @@ 2 |class Err extends Exception: // error |^ |reference (scala.caps.* : Any) is not included in allowed capture set {} of pure base class class Throwable -3 | self: Err^{*} => +3 | self: Err^ => -- Error: tests/neg-custom-args/captures/exception-definitions.scala:10:6 ---------------------------------------------- -10 |class Err4(c: Any^{*}) extends AnyVal // error - |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - |reference (Err4.this.c : Any^{*}) is not included in allowed capture set {} of pure base class class AnyVal +10 |class Err4(c: Any^) extends AnyVal // error + |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + |reference (Err4.this.c : Any^) is not included in allowed capture set {} of pure base class class AnyVal -- Error: tests/neg-custom-args/captures/exception-definitions.scala:7:12 ---------------------------------------------- 7 | val x = c // error | ^ - |(c : Any^{*}) cannot be referenced here; it is not included in the allowed capture set {} of pure base class class Throwable + |(c : Any^) cannot be referenced here; it is not included in the allowed capture set {} of pure base class class Throwable -- Error: tests/neg-custom-args/captures/exception-definitions.scala:8:8 ----------------------------------------------- -8 | class Err3(c: Any^{*}) extends Exception // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | reference (Err3.this.c : Any^{*}) is not included in allowed capture set {} of pure base class class Throwable +8 | class Err3(c: Any^) extends Exception // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | reference (Err3.this.c : Any^) is not included in allowed capture set {} of pure base class class Throwable diff --git a/tests/neg-custom-args/captures/exception-definitions.scala b/tests/neg-custom-args/captures/exception-definitions.scala index 5ecd5eb85cf7..996f64ae4bd1 100644 --- a/tests/neg-custom-args/captures/exception-definitions.scala +++ b/tests/neg-custom-args/captures/exception-definitions.scala @@ -1,12 +1,12 @@ class Err extends Exception: // error - self: Err^{*} => + self: Err^ => -def test(c: Any^{*}) = +def test(c: Any^) = class Err2 extends Exception: val x = c // error - class Err3(c: Any^{*}) extends Exception // error + class Err3(c: Any^) extends Exception // error -class Err4(c: Any^{*}) extends AnyVal // error +class Err4(c: Any^) extends AnyVal // error diff --git a/tests/neg-custom-args/captures/i15116.check b/tests/neg-custom-args/captures/i15116.check index 844ce71d0fff..4b637a7c2e40 100644 --- a/tests/neg-custom-args/captures/i15116.check +++ b/tests/neg-custom-args/captures/i15116.check @@ -9,7 +9,7 @@ 5 | val x = Foo(m) // error | ^^^^^^^^^^^^^^ | Non-local value x cannot have an inferred type - | Foo{val m: String^{*}}^{Baz.this} + | Foo{val m: String^}^{Baz.this} | with non-empty capture set {Baz.this}. | The type needs to be declared explicitly. -- Error: tests/neg-custom-args/captures/i15116.scala:7:6 -------------------------------------------------------------- @@ -23,6 +23,6 @@ 9 | val x = Foo(m) // error | ^^^^^^^^^^^^^^ | Non-local value x cannot have an inferred type - | Foo{val m: String^{*}}^{Baz2.this} + | Foo{val m: String^}^{Baz2.this} | with non-empty capture set {Baz2.this}. | The type needs to be declared explicitly. diff --git a/tests/neg-custom-args/captures/i15116.scala b/tests/neg-custom-args/captures/i15116.scala index e1c23e4c6aef..c4dc6c88d56c 100644 --- a/tests/neg-custom-args/captures/i15116.scala +++ b/tests/neg-custom-args/captures/i15116.scala @@ -1,9 +1,9 @@ -class Foo(m: String^{*}) -class Bar(val m: String^{*}): +class Foo(m: String^) +class Bar(val m: String^): val x = Foo(m) // error -trait Baz(val m: String^{*}): +trait Baz(val m: String^): val x = Foo(m) // error -class Bar1(m: String^{*}): +class Bar1(m: String^): val x = Foo(m) // error -trait Baz2(m: String^{*}): +trait Baz2(m: String^): val x = Foo(m) // error diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index f26d929bbdcd..e77d4022451d 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -1,22 +1,22 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:20:49 --------------------------------------- -20 | val boxed1 : ((C^{*}) => Unit) -> Unit = box1(c) // error - | ^^^^^^^ - | Found: (C{val arg: C^{*}}^{c} ->{*} Unit) ->{c} Unit - | Required: (C^{*} => Unit) -> Unit +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:20:46 --------------------------------------- +20 | val boxed1 : ((C^) => Unit) -> Unit = box1(c) // error + | ^^^^^^^ + | Found: (C{val arg: C^}^{c} => Unit) ->{c} Unit + | Required: (C^ => Unit) -> Unit | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:27:38 --------------------------------------- -27 | val boxed2 : Observe[C^{*}] = box2(c) // error - | ^^^^^^^ - | Found: (C{val arg: C^{*}}^{c} ->{*} Unit) ->{c} Unit - | Required: Observe[C^{*}] +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:27:35 --------------------------------------- +27 | val boxed2 : Observe[C^] = box2(c) // error + | ^^^^^^^ + | Found: (C{val arg: C^}^{c} => Unit) ->{c} Unit + | Required: Observe[C^] | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:33:37 --------------------------------------- -33 | val boxed2 : Observe[C]^{*} = box2(c) // error - | ^ - | Found: C^{*} - | Required: box C{val arg: C^?}^{*} +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:33:34 --------------------------------------- +33 | val boxed2 : Observe[C]^ = box2(c) // error + | ^ + | Found: C^ + | Required: box C{val arg: C^?}^ | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:44:2 ---------------------------------------- diff --git a/tests/neg-custom-args/captures/i15772.scala b/tests/neg-custom-args/captures/i15772.scala index a22bbea742be..e4efb6b9ccab 100644 --- a/tests/neg-custom-args/captures/i15772.scala +++ b/tests/neg-custom-args/captures/i15772.scala @@ -1,6 +1,6 @@ type Observe[T] = (T => Unit) -> Unit -def unsafe(cap: C^{*}) = cap.bad() +def unsafe(cap: C^) = cap.bad() def box1[T](v: T) : (T => Unit) -> Unit = { (fn: T => Unit) => fn(v) @@ -10,34 +10,34 @@ def box2[T](v: T) : Observe[T] = { (fn: T => Unit) => fn(v) } -class C(val arg: C^{*}) { +class C(val arg: C^) { def bad() = println("I've gone bad!") } -def main1(x: C^{*}) : () -> Int = +def main1(x: C^) : () -> Int = () => val c : C^{x} = new C(x) - val boxed1 : ((C^{*}) => Unit) -> Unit = box1(c) // error - boxed1((cap: C^{*}) => unsafe(c)) + val boxed1 : ((C^) => Unit) -> Unit = box1(c) // error + boxed1((cap: C^) => unsafe(c)) 0 -def main2(x: C^{*}) : () -> Int = +def main2(x: C^) : () -> Int = () => val c : C^{x} = new C(x) - val boxed2 : Observe[C^{*}] = box2(c) // error - boxed2((cap: C^{*}) => unsafe(c)) + val boxed2 : Observe[C^] = box2(c) // error + boxed2((cap: C^) => unsafe(c)) 0 -def main3(x: C^{*}) = - def c : C^{*} = new C(x) - val boxed2 : Observe[C]^{*} = box2(c) // error - boxed2((cap: C^{*}) => unsafe(c)) +def main3(x: C^) = + def c : C^ = new C(x) + val boxed2 : Observe[C]^ = box2(c) // error + boxed2((cap: C^) => unsafe(c)) 0 trait File: def write(s: String): Unit -def main(io: Any^{*}) = +def main(io: Any^) = val sayHello: ((File^{io}) => Unit) = (file: File^{io}) => file.write("Hello World!\r\n") val filesList : List[File]^{io} = ??? val x = () => filesList.foreach(sayHello) diff --git a/tests/neg-custom-args/captures/lazylist.check b/tests/neg-custom-args/captures/lazylist.check index 78c51e5ee42b..4b7611fc3fb7 100644 --- a/tests/neg-custom-args/captures/lazylist.check +++ b/tests/neg-custom-args/captures/lazylist.check @@ -8,8 +8,8 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:35:29 ------------------------------------- 35 | val ref1c: LazyList[Int] = ref1 // error | ^^^^ - | Found: (ref1 : lazylists.LazyCons[Int]{val xs: () ->{cap1} lazylists.LazyList[Int]^{*}}^{cap1}) - | Required: lazylists.LazyList[Int] + | Found: (ref1 : lazylists.LazyCons[Int]{val xs: () ->{cap1} lazylists.LazyList[Int]^}^{cap1}) + | Required: lazylists.LazyList[Int] | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:37:36 ------------------------------------- @@ -34,9 +34,9 @@ | | longer explanation available when compiling with `-explain` -- [E164] Declaration Error: tests/neg-custom-args/captures/lazylist.scala:22:6 ---------------------------------------- -22 | def tail: LazyList[Nothing]^{*} = ??? // error overriding +22 | def tail: LazyList[Nothing]^ = ??? // error overriding | ^ | error overriding method tail in class LazyList of type -> lazylists.LazyList[Nothing]; - | method tail of type -> lazylists.LazyList[Nothing]^{*} has incompatible type + | method tail of type -> lazylists.LazyList[Nothing]^ has incompatible type | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/lazylist.scala b/tests/neg-custom-args/captures/lazylist.scala index 6b4cf00e5566..e6e4d003f7ae 100644 --- a/tests/neg-custom-args/captures/lazylist.scala +++ b/tests/neg-custom-args/captures/lazylist.scala @@ -1,7 +1,7 @@ package lazylists abstract class LazyList[+T]: - this: LazyList[T]^{*} => + this: LazyList[T]^ => def isEmpty: Boolean def head: T @@ -11,7 +11,7 @@ abstract class LazyList[+T]: if isEmpty then LazyNil else LazyCons(f(head), () => tail.map(f)) -class LazyCons[+T](val x: T, val xs: () => LazyList[T]^{*}) extends LazyList[T]: +class LazyCons[+T](val x: T, val xs: () => LazyList[T]^) extends LazyList[T]: def isEmpty = false def head = x def tail = xs() // error @@ -19,13 +19,13 @@ class LazyCons[+T](val x: T, val xs: () => LazyList[T]^{*}) extends LazyList[T]: object LazyNil extends LazyList[Nothing]: def isEmpty = true def head = ??? - def tail: LazyList[Nothing]^{*} = ??? // error overriding + def tail: LazyList[Nothing]^ = ??? // error overriding -def map[A, B](xs: LazyList[A]^{*}, f: A => B): LazyList[B]^{f, xs} = +def map[A, B](xs: LazyList[A]^, f: A => B): LazyList[B]^{f, xs} = xs.map(f) class CC -type Cap = CC^{*} +type Cap = CC^ def test(cap1: Cap, cap2: Cap, cap3: Cap) = def f[T](x: LazyList[T]): LazyList[T] = if cap1 == cap1 then x else LazyNil diff --git a/tests/neg-custom-args/captures/lazylists-exceptions.check b/tests/neg-custom-args/captures/lazylists-exceptions.check index c8f835f4fdfa..4c451b1fea8a 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.check +++ b/tests/neg-custom-args/captures/lazylists-exceptions.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/lazylists-exceptions.scala:36:2 ----------------------------------------------- 36 | try // error | ^ - | The expression's type LazyList[Int]^{*} is not allowed to capture the root capability `*`. + | The expression's type LazyList[Int]^ is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. 37 | tabulate(10) { i => 38 | if i > 9 then throw Ex1() diff --git a/tests/neg-custom-args/captures/lazylists-exceptions.scala b/tests/neg-custom-args/captures/lazylists-exceptions.scala index a2b902e83cb9..6a72facf7285 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.scala +++ b/tests/neg-custom-args/captures/lazylists-exceptions.scala @@ -12,8 +12,8 @@ object LazyNil extends LazyList[Nothing]: def head = ??? def tail = ??? -final class LazyCons[+T](val x: T, val xs: () => LazyList[T]^{*}) extends LazyList[T]: - this: LazyList[T]^{*} => +final class LazyCons[+T](val x: T, val xs: () => LazyList[T]^) extends LazyList[T]: + this: LazyList[T]^ => def isEmpty = false def head = x @@ -21,7 +21,7 @@ final class LazyCons[+T](val x: T, val xs: () => LazyList[T]^{*}) extends LazyLi end LazyCons extension [A](x: A) - def #:(xs1: => LazyList[A]^{*}): LazyList[A]^{xs1} = + def #:(xs1: => LazyList[A]^): LazyList[A]^{xs1} = LazyCons(x, () => xs1) def tabulate[A](n: Int)(gen: Int => A): LazyList[A]^{gen} = diff --git a/tests/neg-custom-args/captures/lazyref.check b/tests/neg-custom-args/captures/lazyref.check index 9c9e2b757fb7..8c91ec13b5d8 100644 --- a/tests/neg-custom-args/captures/lazyref.check +++ b/tests/neg-custom-args/captures/lazyref.check @@ -8,21 +8,21 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:21:35 -------------------------------------- 21 | val ref2c: LazyRef[Int]^{cap2} = ref2 // error | ^^^^ - | Found: (ref2 : LazyRef[Int]{val elem: () ->{*} Int}^{cap2, ref1}) + | Found: (ref2 : LazyRef[Int]{val elem: () => Int}^{cap2, ref1}) | Required: LazyRef[Int]^{cap2} | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:23:35 -------------------------------------- 23 | val ref3c: LazyRef[Int]^{ref1} = ref3 // error | ^^^^ - | Found: (ref3 : LazyRef[Int]{val elem: () ->{*} Int}^{cap2, ref1}) + | Found: (ref3 : LazyRef[Int]{val elem: () => Int}^{cap2, ref1}) | Required: LazyRef[Int]^{ref1} | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazyref.scala:25:35 -------------------------------------- 25 | val ref4c: LazyRef[Int]^{cap1} = ref4 // error | ^^^^ - | Found: (ref4 : LazyRef[Int]{val elem: () ->{*} Int}^{cap2, cap1}) + | Found: (ref4 : LazyRef[Int]{val elem: () => Int}^{cap2, cap1}) | Required: LazyRef[Int]^{cap1} | | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/lazyref.scala b/tests/neg-custom-args/captures/lazyref.scala index 54e64b4b4b4b..797781ac5360 100644 --- a/tests/neg-custom-args/captures/lazyref.scala +++ b/tests/neg-custom-args/captures/lazyref.scala @@ -6,10 +6,10 @@ class LazyRef[T](val elem: () => T): def map[U](f: T => U): LazyRef[U]^{f, this} = new LazyRef(() => f(elem())) -def map[A, B](ref: LazyRef[A]^{*}, f: A => B): LazyRef[B]^{f, ref} = +def map[A, B](ref: LazyRef[A]^, f: A => B): LazyRef[B]^{f, ref} = new LazyRef(() => f(ref.elem())) -def mapc[A, B]: (ref: LazyRef[A]^{*}, f: A => B) -> LazyRef[B]^{f, ref} = +def mapc[A, B]: (ref: LazyRef[A]^, f: A => B) -> LazyRef[B]^{f, ref} = (ref1, f1) => map[A, B](ref1, f1) def test(cap1: Cap, cap2: Cap) = diff --git a/tests/neg-custom-args/captures/nestedclass.scala b/tests/neg-custom-args/captures/nestedclass.scala index 5e5900694228..0581f9ce9b2d 100644 --- a/tests/neg-custom-args/captures/nestedclass.scala +++ b/tests/neg-custom-args/captures/nestedclass.scala @@ -1,5 +1,5 @@ class CC -type Cap = CC^{*} +type Cap = CC^ abstract class C: def head: String diff --git a/tests/neg-custom-args/captures/real-try.check b/tests/neg-custom-args/captures/real-try.check index b991e3fe09c1..c46d1e075075 100644 --- a/tests/neg-custom-args/captures/real-try.check +++ b/tests/neg-custom-args/captures/real-try.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:12:2 ----------------------------------------------------------- 12 | try // error | ^ - | The expression's type () ->{*} Unit is not allowed to capture the root capability `*`. + | The expression's type () => Unit is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. 13 | () => foo(1) 14 | catch @@ -10,7 +10,7 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:18:2 ----------------------------------------------------------- 18 | try // error | ^ - | The expression's type () ->{*} Cell[Unit]^? is not allowed to capture the root capability `*`. + | The expression's type () => Cell[Unit]^? is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. 19 | () => Cell(foo(1)) 20 | catch @@ -19,5 +19,5 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:30:4 ----------------------------------------------------------- 30 | b.x // error | ^^^ - | The expression's type box () ->{*} Unit is not allowed to capture the root capability `*`. + | The expression's type box () => Unit is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. diff --git a/tests/neg-custom-args/captures/try.check b/tests/neg-custom-args/captures/try.check index efe71386a183..604f7bb1efb4 100644 --- a/tests/neg-custom-args/captures/try.check +++ b/tests/neg-custom-args/captures/try.check @@ -1,8 +1,8 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:23:49 ------------------------------------------ 23 | val a = handle[Exception, CanThrow[Exception]] { // error | ^ - | Found: CT[Exception]^{*} ->? CanThrow[Exception] - | Required: CanThrow[Exception] ->{*} box CT[Exception]^{*} + | Found: CT[Exception]^ ->? CanThrow[Exception] + | Required: CanThrow[Exception] => box CT[Exception]^ 24 | (x: CanThrow[Exception]) => x 25 | }{ | @@ -10,8 +10,8 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:29:43 ------------------------------------------ 29 | val b = handle[Exception, () -> Nothing] { // error | ^ - | Found: (x: CT[Exception]^{*}) ->? () ->{x} Nothing - | Required: (x$0: CanThrow[Exception]) ->{*} () -> Nothing + | Found: (x: CT[Exception]^) ->? () ->{x} Nothing + | Required: (x$0: CanThrow[Exception]) => () -> Nothing 30 | (x: CanThrow[Exception]) => () => raise(new Exception)(using x) 31 | } { | @@ -38,7 +38,7 @@ 39 | 22 40 | } { // error | ^ - | The expression's type box () ->{x$0, *} Int is not allowed to capture the root capability `*`. + | The expression's type box () => Int is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. 41 | (ex: Exception) => () => 22 42 | } diff --git a/tests/neg-custom-args/captures/usingLogFile.check b/tests/neg-custom-args/captures/usingLogFile.check index 68d8a4a43508..99e895c74ee7 100644 --- a/tests/neg-custom-args/captures/usingLogFile.check +++ b/tests/neg-custom-args/captures/usingLogFile.check @@ -1,12 +1,12 @@ -- Error: tests/neg-custom-args/captures/usingLogFile.scala:33:2 ------------------------------------------------------- 33 | later3() // error | ^^^^^^ - | box () ->{*} Unit cannot be box-converted to a type that can be selected or applied + | box () => Unit cannot be box-converted to a type that can be selected or applied | since one of their capture sets contains the root capability `*` -- Error: tests/neg-custom-args/captures/usingLogFile.scala:37:9 ------------------------------------------------------- 37 | later4.x() // error | ^^^^^^^^ - | The expression's type box () ->{*} Unit is not allowed to capture the root capability `*`. + | The expression's type box () => Unit is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:23:6 ------------------------------------------------------- 23 | val later = usingLogFile { f => () => f.write(0) } // error @@ -18,7 +18,7 @@ -- Error: tests/neg-custom-args/captures/usingLogFile.scala:29:9 ------------------------------------------------------- 29 | later2.x() // error | ^^^^^^^^ - | The expression's type box () ->{x$0, *} Unit is not allowed to capture the root capability `*`. + | The expression's type box () => Unit is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:47:6 ------------------------------------------------------- 47 | val later = usingLogFile { f => () => f.write(0) } // error @@ -30,10 +30,10 @@ -- Error: tests/neg-custom-args/captures/usingLogFile.scala:62:25 ------------------------------------------------------ 62 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | The expression's type box (x$0: Int) ->{x$0, *} Unit is not allowed to capture the root capability `*`. - | This usually means that a capability persists longer than its allowed lifetime. + | The expression's type box (x$0: Int) => Unit is not allowed to capture the root capability `*`. + | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:71:25 ------------------------------------------------------ 71 | val later = usingFile("logfile", usingLogger(_, l => () => l.log("test"))) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | The expression's type box () ->{x$0, *} Unit is not allowed to capture the root capability `*`. + | The expression's type box () => Unit is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. diff --git a/tests/neg-custom-args/captures/usingLogFile.scala b/tests/neg-custom-args/captures/usingLogFile.scala index b4c40d503e08..1e281f130b8a 100644 --- a/tests/neg-custom-args/captures/usingLogFile.scala +++ b/tests/neg-custom-args/captures/usingLogFile.scala @@ -14,7 +14,7 @@ object Test1: object Test2: - def usingLogFile[T](op: FileOutputStream^{*} => T): T = + def usingLogFile[T](op: FileOutputStream^ => T): T = val logFile = FileOutputStream("log") val result = op(logFile) logFile.close() @@ -38,7 +38,7 @@ object Test2: object Test3: - def usingLogFile[T](op: FileOutputStream^{*} => T) = + def usingLogFile[T](op: FileOutputStream^ => T) = val logFile = FileOutputStream("log") val result = op(logFile) logFile.close() @@ -47,10 +47,10 @@ object Test3: val later = usingLogFile { f => () => f.write(0) } // error object Test4: - class Logger(f: OutputStream^{*}): + class Logger(f: OutputStream^): def log(msg: String): Unit = ??? - def usingFile[T](name: String, op: OutputStream^{*} => T): T = + def usingFile[T](name: String, op: OutputStream^ => T): T = val f = new FileOutputStream(name) val result = op(f) f.close() @@ -63,7 +63,7 @@ object Test4: later(1) - def usingLogger[T](f: OutputStream^{*}, op: Logger^{f} => T): T = + def usingLogger[T](f: OutputStream^, op: Logger^{f} => T): T = val logger = Logger(f) op(logger) diff --git a/tests/neg-custom-args/captures/vars.check b/tests/neg-custom-args/captures/vars.check index 285a15262bb1..b0420c7e5179 100644 --- a/tests/neg-custom-args/captures/vars.check +++ b/tests/neg-custom-args/captures/vars.check @@ -8,24 +8,24 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars.scala:15:10 ----------------------------------------- 15 | val u = a // error | ^ - | Found: (a : box String ->{*} String) - | Required: (x$0: String) ->{*} String + | Found: (a : box String => String) + | Required: (x$0: String) => String | | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/vars.scala:16:2 --------------------------------------------------------------- 16 | a("") // error | ^ - | box String ->{*} String cannot be box-converted to a type that can be selected or applied + | box String => String cannot be box-converted to a type that can be selected or applied | since one of their capture sets contains the root capability `*` -- Error: tests/neg-custom-args/captures/vars.scala:17:4 --------------------------------------------------------------- 17 | b.head // error | ^^^^^^ - | The expression's type box String ->{*} String is not allowed to capture the root capability `*`. + | The expression's type box String => String is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/vars.scala:32:8 --------------------------------------------------------------- 32 | local { cap3 => // error | ^ - | The expression's type box (x$0: String) ->{x$0, *} String is not allowed to capture the root capability `*`. + | The expression's type box (x$0: String) => String is not allowed to capture the root capability `*`. | This usually means that a capability persists longer than its allowed lifetime. 33 | def g(x: String): String = if cap3 == cap3 then "" else "a" 34 | g From d5cee7ce13c374b6888af7747f81ca7601cfdfee Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 30 Apr 2023 17:52:28 +0200 Subject: [PATCH 524/657] Change to new syntax in pos-custom-args --- tests/pos-custom-args/bounded1.scala | 14 ++-- tests/pos-custom-args/captures/bounded.scala | 8 +-- tests/pos-custom-args/captures/boxed1.scala | 4 +- .../captures/boxmap-paper.scala | 16 ++--- .../captures/caps-universal.scala | 2 +- .../captures/capt-capability.scala | 6 +- .../captures/capt-depfun.scala | 4 +- .../captures/capt-depfun2.scala | 2 +- tests/pos-custom-args/captures/capt0.scala | 2 +- tests/pos-custom-args/captures/capt2.scala | 8 +-- .../pos-custom-args/captures/caseclass.scala | 10 +-- .../pos-custom-args/captures/cc-expand.scala | 2 +- tests/pos-custom-args/captures/cc-this.scala | 4 +- .../captures/compare-refined.scala | 10 +-- .../captures/curried-shorthands.scala | 8 +-- tests/pos-custom-args/captures/filevar.scala | 8 +-- tests/pos-custom-args/captures/hk-param.scala | 14 ++-- tests/pos-custom-args/captures/i15922.scala | 6 +- tests/pos-custom-args/captures/i16116.scala | 4 +- tests/pos-custom-args/captures/i16226.scala | 8 +-- .../pos-custom-args/captures/iterators.scala | 8 +-- .../captures/lazylists-exceptions.scala | 32 ++++----- .../captures/lazylists-mono.scala | 12 ++-- .../pos-custom-args/captures/lazylists.scala | 20 +++--- .../pos-custom-args/captures/lazylists1.scala | 24 +++---- tests/pos-custom-args/captures/lazyref.scala | 16 ++--- .../captures/list-encoding.scala | 4 +- tests/pos-custom-args/captures/lists.scala | 72 +++++++++---------- tests/pos-custom-args/captures/logger.scala | 26 +++---- .../captures/nonvariant-inf.scala | 4 +- .../captures/override-adapt-box-pos-alt.scala | 8 +-- .../captures/override-adapt-box-pos.scala | 12 ++-- tests/pos-custom-args/captures/pairs.scala | 14 ++-- .../captures/selftype-alias.scala | 1 - .../captures/stack-alloc.scala | 2 +- tests/pos-custom-args/captures/vars.scala | 8 +-- 36 files changed, 201 insertions(+), 202 deletions(-) diff --git a/tests/pos-custom-args/bounded1.scala b/tests/pos-custom-args/bounded1.scala index 5fb7f0da904b..e16da4935a14 100644 --- a/tests/pos-custom-args/bounded1.scala +++ b/tests/pos-custom-args/bounded1.scala @@ -1,27 +1,27 @@ // To be revisited class CC -type Cap = {*} CC +type Cap = CC^ def test(c: Cap) = - class B[X <: {c} Object](x: X): + class B[X <: Object^{c}](x: X): def elem = x def lateElem = () => x def f(x: Int): Int = if c == c then x else 0 val b = new B(f) val r1 = b.elem - val r1c: {c} Int -> Int = r1 + val r1c: Int^{c} -> Int = r1 val r2 = b.lateElem - val r2c: () -> {c} Int -> Int = r2 // was error now OK + val r2c: () -> Int^{c} -> Int = r2 // was error now OK def test2(c: Cap) = - class B[X <: {*} Any](x: X): + class B[X <: Any^](x: X): def elem = x def lateElem = () => x def f(x: Int): Int = if c == c then x else 0 val b = new B(f) val r1 = b.elem - val r1c: {c} Int -> Int = r1 + val r1c: Int ->{c} Int = r1 val r2 = b.lateElem - val r2c: () -> {c} Int -> Int = r2 // was error now OK \ No newline at end of file + val r2c: () -> Int ->{c} Int = r2 // was error now OK \ No newline at end of file diff --git a/tests/pos-custom-args/captures/bounded.scala b/tests/pos-custom-args/captures/bounded.scala index 85c1a67387b5..7959df7d50cf 100644 --- a/tests/pos-custom-args/captures/bounded.scala +++ b/tests/pos-custom-args/captures/bounded.scala @@ -1,14 +1,14 @@ class CC -type Cap = {*} CC +type Cap = CC^ def test(c: Cap) = - class B[X <: {c} Object](x: X): + class B[X <: Object^{c}](x: X): def elem = x def lateElem = () => x def f(x: Int): Int = if c == c then x else 0 val b = new B(f) val r1 = b.elem - val r1c: {c} Int -> Int = r1 + val r1c: Int ->{c} Int = r1 val r2 = b.lateElem - val r2c: {c} () -> {c} Int -> Int = r2 \ No newline at end of file + val r2c: () ->{c} Int ->{c} Int = r2 \ No newline at end of file diff --git a/tests/pos-custom-args/captures/boxed1.scala b/tests/pos-custom-args/captures/boxed1.scala index ba198335f51d..8c6b63ef0134 100644 --- a/tests/pos-custom-args/captures/boxed1.scala +++ b/tests/pos-custom-args/captures/boxed1.scala @@ -6,6 +6,6 @@ def foo(x: => Int): Unit = () def test(c: Cap) = val f = () => { c; 1 } - val _: {c} () -> Int = f + val _: () ->{c} Int = f val g = () => Box(f) - val _: () -> Box[{f} () -> Int] = g + val _: () -> Box[() ->{f} Int] = g diff --git a/tests/pos-custom-args/captures/boxmap-paper.scala b/tests/pos-custom-args/captures/boxmap-paper.scala index aff4c38e1b9d..9d5bb49af25d 100644 --- a/tests/pos-custom-args/captures/boxmap-paper.scala +++ b/tests/pos-custom-args/captures/boxmap-paper.scala @@ -12,25 +12,25 @@ def map[A, B](c: Cell[A])(f: A => B): Cell[B] def pureMap[A, B](c: Cell[A])(f: A -> B): Cell[B] = c[Cell[B]]((x: A) => cell(f(x))) -def lazyMap[A, B](c: Cell[A])(f: A => B): {f} () -> Cell[B] +def lazyMap[A, B](c: Cell[A])(f: A => B): () ->{f} Cell[B] = () => c[Cell[B]]((x: A) => cell(f(x))) trait IO: def print(s: String): Unit -def test(io: {*} IO) = +def test(io: IO^) = - val loggedOne: {io} () -> Int = () => { io.print("1"); 1 } + val loggedOne: () ->{io} Int = () => { io.print("1"); 1 } - val c: Cell[{io} () -> Int] - = cell[{io} () -> Int](loggedOne) + val c: Cell[() ->{io} Int] + = cell[() ->{io} Int](loggedOne) - val g = (f: {io} () -> Int) => + val g = (f: () ->{io} Int) => val x = f(); io.print(" + ") val y = f(); io.print(s" = ${x + y}") - val r = lazyMap[{io} () -> Int, Unit](c)(f => g(f)) - val r2 = lazyMap[{io} () -> Int, Unit](c)(g) + val r = lazyMap[() ->{io} Int, Unit](c)(f => g(f)) + val r2 = lazyMap[() ->{io} Int, Unit](c)(g) val r3 = lazyMap(c)(g) val _ = r() val _ = r2() diff --git a/tests/pos-custom-args/captures/caps-universal.scala b/tests/pos-custom-args/captures/caps-universal.scala index d84f2b7b2584..77ce10c4e42e 100644 --- a/tests/pos-custom-args/captures/caps-universal.scala +++ b/tests/pos-custom-args/captures/caps-universal.scala @@ -2,6 +2,6 @@ import annotation.retains val foo: Int => Int = x => x val bar: (Int -> Int) @retains(caps.*) = foo -val baz: {*} Int -> Int = bar +val baz: Int => Int = bar diff --git a/tests/pos-custom-args/captures/capt-capability.scala b/tests/pos-custom-args/captures/capt-capability.scala index 4dbd6e32f2a4..830d341c7bca 100644 --- a/tests/pos-custom-args/captures/capt-capability.scala +++ b/tests/pos-custom-args/captures/capt-capability.scala @@ -1,7 +1,7 @@ import annotation.capability @capability class Cap -def f1(c: Cap): {c} () -> c.type = () => c // ok +def f1(c: Cap): () ->{c} c.type = () => c // ok def f2: Int = val g: Boolean => Int = ??? @@ -17,8 +17,8 @@ def f3: Int = def foo() = val x: Cap = ??? val y: Cap = x - val x2: {x} () -> Cap = ??? - val y2: {x} () -> Cap = x2 + val x2: () ->{x} Cap = ??? + val y2: () ->{x} Cap = x2 val z1: () => Cap = f1(x) def h[X](a: X)(b: X) = a diff --git a/tests/pos-custom-args/captures/capt-depfun.scala b/tests/pos-custom-args/captures/capt-depfun.scala index 0e9786b2ee34..3a896b8acb8e 100644 --- a/tests/pos-custom-args/captures/capt-depfun.scala +++ b/tests/pos-custom-args/captures/capt-depfun.scala @@ -16,6 +16,6 @@ def f(y: Cap, z: Cap): String @retains(caps.*) = val d = a(g()) val ac: ((x: Cap) -> ID[String @retains(x) -> String @retains(x)]) = ??? - val bc: (({y} String) -> {y} String) = ac(y) - val dc: (String -> {y, z} String) = ac(g()) + val bc: String^{y} -> String^{y} = ac(y) + val dc: String -> String^{y, z} = ac(g()) c diff --git a/tests/pos-custom-args/captures/capt-depfun2.scala b/tests/pos-custom-args/captures/capt-depfun2.scala index 1c747d5885e6..96e97de95d72 100644 --- a/tests/pos-custom-args/captures/capt-depfun2.scala +++ b/tests/pos-custom-args/captures/capt-depfun2.scala @@ -5,5 +5,5 @@ type Cap = C @retains(caps.*) def f(y: Cap, z: Cap) = def g(): C @retains(y, z) = ??? val ac: ((x: Cap) -> Array[String @retains(x)]) = ??? - val dc: Array[? >: String <: {y, z} String] = ac(g()) // needs to be inferred + val dc: Array[? >: String <: String]^{y, z} = ac(g()) // needs to be inferred val ec = ac(y) diff --git a/tests/pos-custom-args/captures/capt0.scala b/tests/pos-custom-args/captures/capt0.scala index 52d6253af46b..fca48170b6f5 100644 --- a/tests/pos-custom-args/captures/capt0.scala +++ b/tests/pos-custom-args/captures/capt0.scala @@ -1,7 +1,7 @@ object Test: def test() = - val x: {*} Any = "abc" + val x: Any^ = "abc" val y: Object @scala.annotation.retains(x) = ??? val z: Object @scala.annotation.retains(x, caps.*) = y: Object @annotation.retains(x) diff --git a/tests/pos-custom-args/captures/capt2.scala b/tests/pos-custom-args/captures/capt2.scala index 77c0caaf0f1d..498dbd371ca0 100644 --- a/tests/pos-custom-args/captures/capt2.scala +++ b/tests/pos-custom-args/captures/capt2.scala @@ -3,7 +3,7 @@ class C type Cap = C @retains(caps.*) def test1() = - val y: {*} String = "" + val y: String^ = "" def x: Object @retains(y) = y def test2() = @@ -13,8 +13,8 @@ def test2() = z: (() -> Unit) @retains(x) def z2: (() -> Unit) @retains(y) = y z2: (() -> Unit) @retains(y) - val p: {*} () -> String = () => "abc" - val q: {p} C = ??? - val _ = p: ({p} () -> String) + val p: () => String = () => "abc" + val q: C^{p} = ??? + val _ = p: (() ->{p} String) diff --git a/tests/pos-custom-args/captures/caseclass.scala b/tests/pos-custom-args/captures/caseclass.scala index a845da181e9f..ffbf878dca49 100644 --- a/tests/pos-custom-args/captures/caseclass.scala +++ b/tests/pos-custom-args/captures/caseclass.scala @@ -1,6 +1,6 @@ @annotation.capability class C object test1: - case class Ref(x: {*} String) + case class Ref(x: String^) def test(c: C) = val x1 = Ref("hello") @@ -14,7 +14,7 @@ object test2: val pure: () -> Unit = () => () val impure: () => Unit = pure - val mixed: {c} () -> Unit = pure + val mixed: () ->{c} Unit = pure val x = Ref(impure) val y0 = x.copy(pure) val yc0: Ref = y0 @@ -25,10 +25,10 @@ object test2: val yc2: Ref = y2 val x3 = Ref(mixed) - val _: {c} Ref = x3 + val _: Ref^{c} = x3 val y3 = x3.copy() - val yc3: {c} Ref = y3 + val yc3: Ref^{c} = y3 val y4 = y3 match case Ref(xx) => xx - val y4c: {x3} () -> Unit = y4 + val y4c: () ->{x3} Unit = y4 diff --git a/tests/pos-custom-args/captures/cc-expand.scala b/tests/pos-custom-args/captures/cc-expand.scala index 87b2c34caf5f..1d398353a5ec 100644 --- a/tests/pos-custom-args/captures/cc-expand.scala +++ b/tests/pos-custom-args/captures/cc-expand.scala @@ -9,7 +9,7 @@ object Test: def test(ct: CT, dt: CT) = - def x0: A -> {ct} B = ??? + def x0: A -> B^{ct} = ??? def x1: A -> B @retains(ct) = ??? def x2: A -> B -> C @retains(ct) = ??? diff --git a/tests/pos-custom-args/captures/cc-this.scala b/tests/pos-custom-args/captures/cc-this.scala index 77414fa9b8c0..2124ee494041 100644 --- a/tests/pos-custom-args/captures/cc-this.scala +++ b/tests/pos-custom-args/captures/cc-this.scala @@ -5,7 +5,7 @@ def eff(using Cap): Unit = () def test(using Cap) = class C(val x: () => Int): - val y: {*} C = this + val y: C^ = this def f = () => eff @@ -14,4 +14,4 @@ def test(using Cap) = def c1 = new C(f) def c2 = c1 def c3 = c2.y - val _ = c3: {*} C + val _ = c3: C^ diff --git a/tests/pos-custom-args/captures/compare-refined.scala b/tests/pos-custom-args/captures/compare-refined.scala index c60bfee602b3..306f2216ab82 100644 --- a/tests/pos-custom-args/captures/compare-refined.scala +++ b/tests/pos-custom-args/captures/compare-refined.scala @@ -2,11 +2,11 @@ abstract class LIST[+T]: def map[U](f: T => U): LIST[U] = ??? class C -type Cap = {*} C +type Cap = C^ def test(d: Cap) = - val zsc: LIST[{d} Cap -> Unit] = ??? - val a4 = zsc.map[{d} Cap -> Unit]((x: {d} Cap -> Unit) => x) - val a5 = zsc.map[{d} Cap -> Unit](identity[{d} Cap -> Unit]) - val a6 = zsc.map(identity[{d} Cap -> Unit]) + val zsc: LIST[Cap ->{d} Unit] = ??? + val a4 = zsc.map[Cap ->{d} Unit]((x: Cap ->{d} Unit) => x) + val a5 = zsc.map[Cap ->{d} Unit](identity[Cap ->{d} Unit]) + val a6 = zsc.map(identity[Cap ->{d} Unit]) val a7 = zsc.map(identity) diff --git a/tests/pos-custom-args/captures/curried-shorthands.scala b/tests/pos-custom-args/captures/curried-shorthands.scala index 7c58729a3041..c68dc4b5cdbf 100644 --- a/tests/pos-custom-args/captures/curried-shorthands.scala +++ b/tests/pos-custom-args/captures/curried-shorthands.scala @@ -10,15 +10,15 @@ object Test: val f3 = (f: Int => Int) => println(f(3)) (xs: List[Int]) => xs.map(_ + 1) - val f3c: (Int => Int) -> {} List[Int] -> List[Int] = f3 + val f3c: (Int => Int) -> List[Int] ->{} List[Int] = f3 class LL[A]: - def drop(n: Int): {this} LL[A] = ??? + def drop(n: Int): LL[A]^{this} = ??? def test(ct: CanThrow[Exception]) = - def xs: {ct} LL[Int] = ??? + def xs: LL[Int]^{ct} = ??? val ys = xs.drop(_) - val ysc: Int -> {ct} LL[Int] = ys + val ysc: Int -> LL[Int]^{ct} = ys diff --git a/tests/pos-custom-args/captures/filevar.scala b/tests/pos-custom-args/captures/filevar.scala index 17a8281c4d29..a6cc7ca9ff47 100644 --- a/tests/pos-custom-args/captures/filevar.scala +++ b/tests/pos-custom-args/captures/filevar.scala @@ -6,10 +6,10 @@ object test1: class File: def write(x: String): Unit = ??? - class Service(f: {*} File): + class Service(f: File^): def log = f.write("log") - def withFile[T](op: (f: {*} File) => T): T = + def withFile[T](op: (f: File^) => T): T = op(new File) def test = @@ -24,10 +24,10 @@ object test2: def write(x: String): Unit = ??? class Service(io: IO): - var file: {io} File = uninitialized + var file: File^{io} = uninitialized def log = file.write("log") - def withFile[T](io: IO)(op: (f: {io} File) => T): T = + def withFile[T](io: IO)(op: (f: File^{io}) => T): T = op(new File) def test(io: IO) = diff --git a/tests/pos-custom-args/captures/hk-param.scala b/tests/pos-custom-args/captures/hk-param.scala index b0e894d865e9..bf2f75f29e7f 100644 --- a/tests/pos-custom-args/captures/hk-param.scala +++ b/tests/pos-custom-args/captures/hk-param.scala @@ -1,17 +1,17 @@ /** Concrete collection type: View */ -trait View[+A] extends Itable[A], ILike[A, [X] =>> {*} View[X]]: - override def fromIterable[B](c: {*} Itable[B]): {c} View[B] = ??? +trait View[+A] extends Itable[A], ILike[A, [X] =>> View[X]^]: + override def fromIterable[B](c: Itable[B]^): View[B]^{c} = ??? trait IPolyTransforms[+A, +C[A]] extends Any: - def fromIterable[B](coll: {*} Itable[B]): C[B] + def fromIterable[B](coll: Itable[B]^): C[B] -trait ILike[+A, +C[X] <: {*} Itable[X]] extends IPolyTransforms[A, C] +trait ILike[+A, +C[X] <: Itable[X]^] extends IPolyTransforms[A, C] /** Base trait for generic collections */ -trait Itable[+A] extends ItableOnce[A] with ILike[A, {*} Itable] +trait Itable[+A] extends ItableOnce[A] with ILike[A, Itable^] /** Iterator can be used only once */ trait ItableOnce[+A] { - this: {*} ItableOnce[A] => - def iterator: {this} Iterator[A] + this: ItableOnce[A]^ => + def iterator: Iterator[A]^{this} } diff --git a/tests/pos-custom-args/captures/i15922.scala b/tests/pos-custom-args/captures/i15922.scala index 8547f7598eef..23109a3ba8f4 100644 --- a/tests/pos-custom-args/captures/i15922.scala +++ b/tests/pos-custom-args/captures/i15922.scala @@ -2,13 +2,13 @@ trait Cap { def use(): Int } type Id[X] = [T] -> (op: X => T) -> T def mkId[X](x: X): Id[X] = [T] => (op: X => T) => op(x) -def withCap[X](op: ({*} Cap) => X): X = { - val cap: {*} Cap = new Cap { def use() = { println("cap is used"); 0 } } +def withCap[X](op: (Cap^) => X): X = { + val cap: Cap^ = new Cap { def use() = { println("cap is used"); 0 } } val result = op(cap) result } -def leaking(c: {*} Cap): Id[{c} Cap] = mkId(c) +def leaking(c: Cap^): Id[Cap^{c}] = mkId(c) def test = val bad = withCap(leaking) diff --git a/tests/pos-custom-args/captures/i16116.scala b/tests/pos-custom-args/captures/i16116.scala index 2f5d5304dca5..0311e744f146 100644 --- a/tests/pos-custom-args/captures/i16116.scala +++ b/tests/pos-custom-args/captures/i16116.scala @@ -17,7 +17,7 @@ object Test { @capability class CpsTransform[F[_]] { - def await[T](ft: F[T]): { this } T = ??? + def await[T](ft: F[T]): T^{ this } = ??? } transparent inline def cpsAsync[F[_]](using m:CpsMonad[F]) = @@ -27,7 +27,7 @@ object Test { def apply[A](expr: (CpsTransform[F], C) ?=> A): F[A] = ??? } - def asyncPlus[F[_]](a:Int, b:F[Int])(using cps: CpsTransform[F]): { cps } Int = + def asyncPlus[F[_]](a:Int, b:F[Int])(using cps: CpsTransform[F]): Int^{ cps } = a + (cps.await(b).asInstanceOf[Int]) def testExample1Future(): Unit = diff --git a/tests/pos-custom-args/captures/i16226.scala b/tests/pos-custom-args/captures/i16226.scala index 8edf3f54d739..4cd7f0ceea81 100644 --- a/tests/pos-custom-args/captures/i16226.scala +++ b/tests/pos-custom-args/captures/i16226.scala @@ -1,14 +1,14 @@ @annotation.capability class Cap class LazyRef[T](val elem: () => T): - val get: {elem} () -> T = elem - def map[U](f: T => U): {f, this} LazyRef[U] = + val get: () ->{elem} T = elem + def map[U](f: T => U): LazyRef[U]^{f, this} = new LazyRef(() => f(elem())) -def map[A, B](ref: {*} LazyRef[A], f: A => B): {f, ref} LazyRef[B] = +def map[A, B](ref: LazyRef[A]^, f: A => B): LazyRef[B]^{f, ref} = new LazyRef(() => f(ref.elem())) def main(io: Cap) = { - def mapd[A, B]: ({io} LazyRef[A], A => B) => {*} LazyRef[B] = + def mapd[A, B]: (LazyRef[A]^{io}, A => B) => LazyRef[B]^ = (ref1, f1) => map[A, B](ref1, f1) } diff --git a/tests/pos-custom-args/captures/iterators.scala b/tests/pos-custom-args/captures/iterators.scala index 50be2012e25c..10a7f57cd68f 100644 --- a/tests/pos-custom-args/captures/iterators.scala +++ b/tests/pos-custom-args/captures/iterators.scala @@ -1,19 +1,19 @@ package cctest abstract class Iterator[T]: - thisIterator: {*} Iterator[T] => + thisIterator: Iterator[T]^ => def hasNext: Boolean def next: T - def map(f: {*} T => T): {f, this} Iterator[T] = new Iterator: + def map(f: T => T): Iterator[T]^{f, this} = new Iterator: def hasNext = thisIterator.hasNext def next = f(thisIterator.next) end Iterator class C -type Cap = {*} C +type Cap = C^ -def map[T, U](it: {*} Iterator[T], f: {*} T => U): {it, f} Iterator[U] = new Iterator: +def map[T, U](it: Iterator[T]^, f: T^ => U): Iterator[U]^{it, f} = new Iterator: def hasNext = it.hasNext def next = f(it.next) diff --git a/tests/pos-custom-args/captures/lazylists-exceptions.scala b/tests/pos-custom-args/captures/lazylists-exceptions.scala index 2d4ebb245dca..8f1fba2bf2dc 100644 --- a/tests/pos-custom-args/captures/lazylists-exceptions.scala +++ b/tests/pos-custom-args/captures/lazylists-exceptions.scala @@ -4,52 +4,52 @@ import scala.compiletime.uninitialized trait LzyList[+A]: def isEmpty: Boolean def head: A - def tail: {this} LzyList[A] + def tail: LzyList[A]^{this} object LzyNil extends LzyList[Nothing]: def isEmpty = true def head = ??? def tail = ??? -final class LzyCons[+A](hd: A, tl: () => {*} LzyList[A]) extends LzyList[A]: +final class LzyCons[+A](hd: A, tl: () => LzyList[A]^) extends LzyList[A]: private var forced = false - private var cache: {this} LzyList[A] = uninitialized + private var cache: LzyList[A]^{this} = uninitialized private def force = if !forced then { cache = tl(); forced = true } cache def isEmpty = false def head = hd - def tail: {this} LzyList[A] = force + def tail: LzyList[A]^{this} = force end LzyCons -extension [A](xs: {*} LzyList[A]) - def map[B](f: A => B): {xs, f} LzyList[B] = +extension [A](xs: LzyList[A]^) + def map[B](f: A => B): LzyList[B]^{xs, f} = if xs.isEmpty then LzyNil else LzyCons(f(xs.head), () => xs.tail.map(f)) - def filter(p: A => Boolean): {xs, p} LzyList[A] = + def filter(p: A => Boolean): LzyList[A]^{xs, p} = if xs.isEmpty then LzyNil else if p(xs.head) then lazyCons(xs.head, xs.tail.filter(p)) else xs.tail.filter(p) - def concat(ys: {*} LzyList[A]): {xs, ys} LzyList[A] = + def concat(ys: LzyList[A]^): LzyList[A]^{xs, ys} = if xs.isEmpty then ys else xs.head #: xs.tail.concat(ys) - def drop(n: Int): {xs} LzyList[A] = + def drop(n: Int): LzyList[A]^{xs} = if n == 0 then xs else xs.tail.drop(n - 1) end extension extension [A](x: A) - def #:(xs1: => {*} LzyList[A]): {xs1} LzyList[A] = + def #:(xs1: => LzyList[A]^): LzyList[A]^{xs1} = LzyCons(x, () => xs1) -def lazyCons[A](x: A, xs1: => {*} LzyList[A]): {xs1} LzyList[A] = +def lazyCons[A](x: A, xs1: => LzyList[A]^): LzyList[A]^{xs1} = LzyCons(x, () => xs1) -def tabulate[A](n: Int)(gen: Int => A): {gen} LzyList[A] = - def recur(i: Int): {gen} LzyList[A] = +def tabulate[A](n: Int)(gen: Int => A): LzyList[A]^{gen} = + def recur(i: Int): LzyList[A]^{gen} = if i == n then LzyNil else gen(i) #: recur(i + 1) recur(0) @@ -69,16 +69,16 @@ def test(using cap1: CanThrow[Ex1], cap2: CanThrow[Ex2]) = x * x def x1 = xs.map(f) - def x1c: {cap1} LzyList[Int] = x1 + def x1c: LzyList[Int]^{cap1} = x1 def x2 = x1.concat(xs.map(g).filter(_ > 0)) - def x2c: {cap1, cap2} LzyList[Int] = x2 + def x2c: LzyList[Int]^{cap1, cap2} = x2 val x3 = tabulate(10) { i => if i > 9 then throw Ex1() i * i } - val x3c: {cap1} LzyList[Int] = x3 + val x3c: LzyList[Int]^{cap1} = x3 class LimitExceeded extends Exception diff --git a/tests/pos-custom-args/captures/lazylists-mono.scala b/tests/pos-custom-args/captures/lazylists-mono.scala index 44ab36ded6a2..d0f88d0de6a3 100644 --- a/tests/pos-custom-args/captures/lazylists-mono.scala +++ b/tests/pos-custom-args/captures/lazylists-mono.scala @@ -1,26 +1,26 @@ class CC -type Cap = {*} CC +type Cap = CC^ //------------------------------------------------- def test(E: Cap) = trait LazyList[+A]: - protected def contents: {E} () -> (A, {E} LazyList[A]) + protected def contents: () ->{E} (A, {E} LazyList[A]) def isEmpty: Boolean def head: A = contents()._1 def tail: {E} LazyList[A] = contents()._2 - class LazyCons[+A](override val contents: {E} () -> (A, {E} LazyList[A])) + class LazyCons[+A](override val contents: () ->{E} (A, {E} LazyList[A])) extends LazyList[A]: def isEmpty: Boolean = false object LazyNil extends LazyList[Nothing]: - def contents: {E} () -> (Nothing, LazyList[Nothing]) = ??? + def contents: () ->{E} (Nothing, LazyList[Nothing]) = ??? def isEmpty: Boolean = true - extension [A](xs: {E} LazyList[A]) - def map[B](f: {E} A -> B): {E} LazyList[B] = + extension [A](xs: LazyList[A]^{E}) + def map[B](f: A ->{E} B): LazyList[B]^{E} = if xs.isEmpty then LazyNil else val cons = () => (f(xs.head), xs.tail.map(f)) diff --git a/tests/pos-custom-args/captures/lazylists.scala b/tests/pos-custom-args/captures/lazylists.scala index fd130c87cdea..273f21c1fcf3 100644 --- a/tests/pos-custom-args/captures/lazylists.scala +++ b/tests/pos-custom-args/captures/lazylists.scala @@ -1,26 +1,26 @@ class CC -type Cap = {*} CC +type Cap = CC^ trait LazyList[+A]: - this: {*} LazyList[A] => + this: LazyList[A]^ => def isEmpty: Boolean def head: A - def tail: {this} LazyList[A] + def tail: LazyList[A]^{this} object LazyNil extends LazyList[Nothing]: def isEmpty: Boolean = true def head = ??? def tail = ??? -extension [A](xs: {*} LazyList[A]) - def map[B](f: A => B): {xs, f} LazyList[B] = +extension [A](xs: LazyList[A]^) + def map[B](f: A => B): LazyList[B]^{xs, f} = final class Mapped extends LazyList[B]: - this: {xs, f} Mapped => + this: Mapped^{xs, f} => def isEmpty = false def head: B = f(xs.head) - def tail: {this} LazyList[B] = xs.tail.map(f) // OK + def tail: LazyList[B]^{this} = xs.tail.map(f) // OK if xs.isEmpty then LazyNil else new Mapped @@ -30,12 +30,12 @@ def test(cap1: Cap, cap2: Cap) = val xs = class Initial extends LazyList[String]: - this: {cap1} Initial => + this: Initial^{cap1} => def isEmpty = false def head = f("") def tail = LazyNil new Initial - val xsc: {cap1} LazyList[String] = xs + val xsc: LazyList[String]^{cap1} = xs val ys = xs.map(g) - val ysc: {cap1, cap2} LazyList[String] = ys + val ysc: LazyList[String]^{cap1, cap2} = ys diff --git a/tests/pos-custom-args/captures/lazylists1.scala b/tests/pos-custom-args/captures/lazylists1.scala index a59e7c0da12f..62b34f442221 100644 --- a/tests/pos-custom-args/captures/lazylists1.scala +++ b/tests/pos-custom-args/captures/lazylists1.scala @@ -1,28 +1,28 @@ class CC -type Cap = {*} CC +type Cap = CC^ trait LazyList[+A]: def isEmpty: Boolean def head: A - def tail: {this} LazyList[A] - def concat[B >: A](other: {*} LazyList[B]): {this, other} LazyList[B] + def tail: LazyList[A]^{this} + def concat[B >: A](other: LazyList[B]^): LazyList[B]^{this, other} object LazyNil extends LazyList[Nothing]: def isEmpty: Boolean = true def head = ??? def tail = ??? - def concat[B](other: {*} LazyList[B]): {other} LazyList[B] = other + def concat[B](other: LazyList[B]^): LazyList[B]^{other} = other -final class LazyCons[+A](x: A)(xs: () => {*} LazyList[A]) extends LazyList[A]: +final class LazyCons[+A](x: A)(xs: () => LazyList[A]^) extends LazyList[A]: def isEmpty = false def head = x - def tail: {this} LazyList[A] = xs() - def concat[B >: A](other: {*} LazyList[B]): {this, other} LazyList[B] = + def tail: LazyList[A]^{this} = xs() + def concat[B >: A](other: LazyList[B]^): LazyList[B]^{this, other} = LazyCons(head)(() => tail.concat(other)) -extension [A](xs: {*} LazyList[A]) - def map[B](f: A => B): {xs, f} LazyList[B] = +extension [A](xs: LazyList[A]^) + def map[B](f: A => B): LazyList[B]^{xs, f} = if xs.isEmpty then LazyNil else LazyCons(f(xs.head))(() => xs.tail.map(f)) @@ -31,9 +31,9 @@ def test(cap1: Cap, cap2: Cap) = def g(x: String): String = if cap2 == cap2 then "" else "a" val xs = new LazyCons("")(() => if f("") == f("") then LazyNil else LazyNil) - val xsc: {cap1} LazyList[String] = xs + val xsc: LazyList[String]^{cap1} = xs val ys = xs.map(g) - val ysc: {cap1, cap2} LazyList[String] = ys + val ysc: LazyList[String]^{cap1, cap2} = ys val zs = new LazyCons("")(() => if g("") == g("") then LazyNil else LazyNil) val as = xs.concat(zs) - val asc: {xs, zs} LazyList[String] = as + val asc: LazyList[String]^{xs, zs} = as diff --git a/tests/pos-custom-args/captures/lazyref.scala b/tests/pos-custom-args/captures/lazyref.scala index 0d988dc3e17b..3dae51b491b4 100644 --- a/tests/pos-custom-args/captures/lazyref.scala +++ b/tests/pos-custom-args/captures/lazyref.scala @@ -1,24 +1,24 @@ @annotation.capability class Cap class LazyRef[T](val elem: () => T): - val get: {elem} () -> T = elem - def map[U](f: T => U): {f, this} LazyRef[U] = + val get: () ->{elem} T = elem + def map[U](f: T => U): LazyRef[U]^{f, this} = new LazyRef(() => f(elem())) -def map[A, B](ref: {*} LazyRef[A], f: A => B): {f, ref} LazyRef[B] = +def map[A, B](ref: LazyRef[A]^, f: A => B): LazyRef[B]^{f, ref} = new LazyRef(() => f(ref.elem())) -def mapc[A, B]: (ref: {*} LazyRef[A], f: A => B) => {f, ref} LazyRef[B] = +def mapc[A, B]: (ref: LazyRef[A]^, f: A => B) => LazyRef[B]^{f, ref} = (ref1, f1) => map[A, B](ref1, f1) def test(cap1: Cap, cap2: Cap) = def f(x: Int) = if cap1 == cap1 then x else 0 def g(x: Int) = if cap2 == cap2 then x else 0 val ref1 = LazyRef(() => f(0)) - val ref1c: {cap1} LazyRef[Int] = ref1 + val ref1c: LazyRef[Int]^{cap1} = ref1 val ref2 = map(ref1, g) - val ref2c: {cap2, ref1} LazyRef[Int] = ref2 + val ref2c: LazyRef[Int]^{cap2, ref1} = ref2 val ref3 = ref1.map(g) - val ref3c: {cap2, ref1} LazyRef[Int] = ref3 + val ref3c: LazyRef[Int]^{cap2, ref1} = ref3 val ref4 = (if cap1 == cap2 then ref1 else ref2).map(g) - val ref4c: {cap1, cap2} LazyRef[Int] = ref4 + val ref4c: LazyRef[Int]^{cap1, cap2} = ref4 diff --git a/tests/pos-custom-args/captures/list-encoding.scala b/tests/pos-custom-args/captures/list-encoding.scala index 87630467023e..d959b523404b 100644 --- a/tests/pos-custom-args/captures/list-encoding.scala +++ b/tests/pos-custom-args/captures/list-encoding.scala @@ -7,7 +7,7 @@ type Op[T, C] = (v: T) => (s: C) => C type List[T] = - [C] -> (op: Op[T, C]) -> {op} (s: C) -> C + [C] -> (op: Op[T, C]) -> (s: C) ->{op} C def nil[T]: List[T] = [C] => (op: Op[T, C]) => (s: C) => s @@ -15,7 +15,7 @@ def nil[T]: List[T] = def cons[T](hd: T, tl: List[T]): List[T] = [C] => (op: Op[T, C]) => (s: C) => op(hd)(tl(op)(s)) -def foo(c: {*} Cap) = +def foo(c: Cap^) = def f(x: String @retains(c), y: String @retains(c)) = cons(x, cons(y, nil)) def g(x: String @retains(c), y: Any) = diff --git a/tests/pos-custom-args/captures/lists.scala b/tests/pos-custom-args/captures/lists.scala index 6389ec933b32..4e76ed3d5e1d 100644 --- a/tests/pos-custom-args/captures/lists.scala +++ b/tests/pos-custom-args/captures/lists.scala @@ -2,7 +2,7 @@ abstract class LIST[+T]: def isEmpty: Boolean def head: T def tail: LIST[T] - def map[U](f: {*} T -> U): LIST[U] = + def map[U](f: T => U): LIST[U] = if isEmpty then NIL else CONS(f(head), tail.map(f)) @@ -28,9 +28,9 @@ def test(c: Cap, d: Cap, e: Cap) = val zs = val z = g CONS(z, ys) - val zsc: LIST[{d, y} Cap -> Unit] = zs + val zsc: LIST[Cap ->{d, y} Unit] = zs val z1 = zs.head - val z1c: {y, d} Cap -> Unit = z1 + val z1c: Cap ->{y, d} Unit = z1 val ys1 = zs.tail val y1 = ys1.head @@ -38,53 +38,53 @@ def test(c: Cap, d: Cap, e: Cap) = def m1[A, B] = (f: A => B) => (xs: LIST[A]) => xs.map(f) - def m1c: (f: String => Int) -> {f} LIST[String] -> LIST[Int] = m1[String, Int] + def m1c: (f: String => Int) -> LIST[String] ->{f} LIST[Int] = m1[String, Int] def m2 = [A, B] => (f: A => B) => (xs: LIST[A]) => xs.map(f) - def m2c: [A, B] -> (f: A => B) -> {f} LIST[A] -> LIST[B] = m2 + def m2c: [A, B] -> (f: A => B) -> LIST[A] ->{f} LIST[B] = m2 def eff[A](x: A) = if x == e then x else x val eff2 = [A] => (x: A) => if x == e then x else x - val a0 = identity[{d, y} Cap -> Unit] - val a0c: {d, y} ({d, y} Cap -> Unit) -> {d, y} Cap -> Unit = a0 - val a1 = zs.map[{d, y} Cap -> Unit](a0) - val a1c: LIST[{d, y} Cap -> Unit] = a1 - val a2 = zs.map[{d, y} Cap -> Unit](identity[{d, y} Cap -> Unit]) - val a2c: LIST[{d, y} Cap -> Unit] = a2 - val a3 = zs.map(identity[{d, y} Cap -> Unit]) - val a3c: LIST[{d, y} Cap -> Unit] = a3 + val a0 = identity[Cap ->{d, y} Unit] + val a0c: (Cap ->{d, y} Unit) ->{d, y} Cap ->{d, y} Unit = a0 + val a1 = zs.map[Cap ->{d, y} Unit](a0) + val a1c: LIST[Cap ->{d, y} Unit] = a1 + val a2 = zs.map[Cap ->{d, y} Unit](identity[Cap ->{d, y} Unit]) + val a2c: LIST[Cap ->{d, y} Unit] = a2 + val a3 = zs.map(identity[Cap ->{d, y} Unit]) + val a3c: LIST[Cap ->{d, y} Unit] = a3 val a4 = zs.map(identity) - val a4c: LIST[{d, c} Cap -> Unit] = a4 - val a5 = map[{d, y} Cap -> Unit, {d, y} Cap -> Unit](identity)(zs) - val a5c: LIST[{d, c} Cap -> Unit] = a5 - val a6 = m1[{d, y} Cap -> Unit, {d, y} Cap -> Unit](identity)(zs) - val a6c: LIST[{d, c} Cap -> Unit] = a6 + val a4c: LIST[Cap ->{d, c} Unit] = a4 + val a5 = map[Cap ->{d, y} Unit, Cap ->{d, y} Unit](identity)(zs) + val a5c: LIST[Cap ->{d, c} Unit] = a5 + val a6 = m1[Cap ->{d, y} Unit, Cap ->{d, y} Unit](identity)(zs) + val a6c: LIST[Cap ->{d, c} Unit] = a6 - val b0 = eff[{d, y} Cap -> Unit] - val b0c: {e, d, y} ({d, y} Cap -> Unit) -> {d, y} Cap -> Unit = b0 - val b1 = zs.map[{d, y} Cap -> Unit](a0) - val b1c: {e} LIST[{d, y} Cap -> Unit] = b1 - val b2 = zs.map[{d, y} Cap -> Unit](eff[{d, y} Cap -> Unit]) - val b2c: {e} LIST[{d, y} Cap -> Unit] = b2 - val b3 = zs.map(eff[{d, y} Cap -> Unit]) - val b3c: {e} LIST[{d, y} Cap -> Unit] = b3 + val b0 = eff[Cap ->{d, y} Unit] + val b0c: (Cap ->{d, y} Unit) ->{e, d, y} Cap ->{d, y} Unit = b0 + val b1 = zs.map[Cap ->{d, y} Unit](a0) + val b1c: LIST[Cap ->{d, y} Unit]^{e} = b1 + val b2 = zs.map[Cap ->{d, y} Unit](eff[Cap ->{d, y} Unit]) + val b2c: LIST[Cap ->{d, y} Unit]^{e} = b2 + val b3 = zs.map(eff[Cap ->{d, y} Unit]) + val b3c: LIST[Cap ->{d, y} Unit]^{e} = b3 val b4 = zs.map(eff) - val b4c: {e} LIST[{d, c} Cap -> Unit] = b4 + val b4c: LIST[{d, c} Cap -> Unit]^{e} = b4 val b5 = map[{d, y} Cap -> Unit, {d, y} Cap -> Unit](eff)(zs) - val b5c: {e} LIST[{d, c} Cap -> Unit] = b5 + val b5c: LIST[{d, c} Cap -> Unit]^{e} = b5 val b6 = m1[{d, y} Cap -> Unit, {d, y} Cap -> Unit](eff)(zs) - val b6c: {e} LIST[{d, c} Cap -> Unit] = b6 + val b6c: LIST[{d, c} Cap -> Unit]^{e} = b6 val c0 = eff2[{d, y} Cap -> Unit] - val c0c: {e, d, y} ({d, y} Cap -> Unit) -> {d, y} Cap -> Unit = c0 - val c1 = zs.map[{d, y} Cap -> Unit](a0) - val c1c: {e} LIST[{d, y} Cap -> Unit] = c1 - val c2 = zs.map[{d, y} Cap -> Unit](eff2[{d, y} Cap -> Unit]) - val c2c: {e} LIST[{d, y} Cap -> Unit] = c2 - val c3 = zs.map(eff2[{d, y} Cap -> Unit]) - val c3c: {e} LIST[{d, y} Cap -> Unit] = c3 + val c0c: ({d, y} Cap -> Unit) ->{e, d, y} Cap ->{d, y} Unit = c0 + val c1 = zs.map[Cap ->{d, y} Unit](a0) + val c1c: LIST[{d, y} Cap -> Unit]^{e} = c1 + val c2 = zs.map[Cap ->{d, y} Unit](eff2[Cap ->{d, y} Unit]) + val c2c: LIST[{d, y} Cap -> Unit]^{e} = c2 + val c3 = zs.map(eff2[Cap ->{d, y} Unit]) + val c3c: LIST[Cap ->{d, y} Unit]^{e} = c3 diff --git a/tests/pos-custom-args/captures/logger.scala b/tests/pos-custom-args/captures/logger.scala index e5b6c834ffe0..3f417da8c1be 100644 --- a/tests/pos-custom-args/captures/logger.scala +++ b/tests/pos-custom-args/captures/logger.scala @@ -7,9 +7,9 @@ class Logger(using fs: FileSystem): def log(s: String): Unit = ??? def test(using fs: FileSystem) = - val l: {fs} Logger = Logger(using fs) + val l: Logger^{fs} = Logger(using fs) l.log("hello world!") - val xs: {l} LazyList[Int] = + val xs: LazyList[Int]^{l} = LazyList.from(1) .map { i => l.log(s"computing elem # $i") @@ -19,25 +19,25 @@ def test(using fs: FileSystem) = trait LazyList[+A]: def isEmpty: Boolean def head: A - def tail: {this} LazyList[A] + def tail: LazyList[A]^{this} object LazyNil extends LazyList[Nothing]: def isEmpty: Boolean = true def head = ??? def tail = ??? -final class LazyCons[+T](val x: T, val xs: () => {*} LazyList[T]) extends LazyList[T]: +final class LazyCons[+T](val x: T, val xs: () => LazyList[T]^) extends LazyList[T]: def isEmpty = false def head = x - def tail: {this} LazyList[T] = xs() + def tail: LazyList[T]^{this} = xs() end LazyCons extension [A](x: A) - def #::(xs1: => {*} LazyList[A]): {xs1} LazyList[A] = + def #::(xs1: => LazyList[A]^): LazyList[A]^{xs1} = LazyCons(x, () => xs1) -extension [A](xs: {*} LazyList[A]) - def map[B](f: A => B): {xs, f} LazyList[B] = +extension [A](xs: LazyList[A]^) + def map[B](f: A => B): LazyList[B]^{xs, f} = if xs.isEmpty then LazyNil else f(xs.head) #:: xs.tail.map(f) @@ -50,17 +50,17 @@ class Pair[+A, +B](x: A, y: B): def snd: B = y def test2(ct: CanThrow[Exception], fs: FileSystem) = - def x: {ct} Int -> String = ??? - def y: {fs} Logger = ??? + def x: Int ->{ct} String = ??? + def y: Logger^{fs} = ??? def p = Pair(x, y) def f = () => p.fst /* - val l1: {*} Int -> String = ??? - val l2: {c} Object = ??? + val l1: Int => String = ??? + val l2: Object^{c} = ??? val pd = () => Pair(l1, l2) - val p2: Pair[{*} Int -> String, {c} Object] = pd() + val p2: Pair[Int => String, Object]^{c} = pd() val hd = () => p2.fst */ \ No newline at end of file diff --git a/tests/pos-custom-args/captures/nonvariant-inf.scala b/tests/pos-custom-args/captures/nonvariant-inf.scala index 6569f35042e8..4798f98c9fce 100644 --- a/tests/pos-custom-args/captures/nonvariant-inf.scala +++ b/tests/pos-custom-args/captures/nonvariant-inf.scala @@ -3,7 +3,7 @@ trait Iterable[+A] /** Base trait for instances that can construct a collection from an iterable */ trait FromIterable { - type C[X] <: {*} Iterable[X] - def fromIterable[B](it: {*} Iterable[B]): {it} C[B] + type C[X] <: Iterable[X]^ + def fromIterable[B](it: Iterable[B]^): C[B]^{it} def empty[A]: C[A] = fromIterable(??? : Iterable[A]) } diff --git a/tests/pos-custom-args/captures/override-adapt-box-pos-alt.scala b/tests/pos-custom-args/captures/override-adapt-box-pos-alt.scala index c7e4d38723d7..bb6b4030dbff 100644 --- a/tests/pos-custom-args/captures/override-adapt-box-pos-alt.scala +++ b/tests/pos-custom-args/captures/override-adapt-box-pos-alt.scala @@ -9,9 +9,9 @@ abstract class A[X] { class C -def test(io: {*} IO) = { - class B extends A[{io} C] { // X =:= {io} C - def foo(x: Unit): {io} C = ??? - def bar(op: ({io} C) => Int): Int = 0 +def test(io: IO^) = { + class B extends A[C^{io}] { // X =:= {io} C + def foo(x: Unit): C^{io} = ??? + def bar(op: (C^{io}) => Int): Int = 0 } } diff --git a/tests/pos-custom-args/captures/override-adapt-box-pos.scala b/tests/pos-custom-args/captures/override-adapt-box-pos.scala index 7496a138070d..9adaec6896cf 100644 --- a/tests/pos-custom-args/captures/override-adapt-box-pos.scala +++ b/tests/pos-custom-args/captures/override-adapt-box-pos.scala @@ -4,16 +4,16 @@ class IO abstract class A[X, Y] { def foo(x: Unit): X - def bar(x: Int, y: {} IO): X + def bar(x: Int, y: IO^{}): X def baz(x: Y): X } class C -def test(io: {*} IO) = { - class B extends A[{io} C, {} C] { // X =:= {io} C - override def foo(x: Unit): {io} C = ??? - override def bar(x: Int, y: {} IO): {io} C = ??? - override def baz(x: {} C): {io} C = ??? +def test(io: IO^) = { + class B extends A[C^{io}, C^{}] { // X =:= {io} C + override def foo(x: Unit): C^{io} = ??? + override def bar(x: Int, y: IO^{}): C^{io} = ??? + override def baz(x: C^{}): C^{io} = ??? } } diff --git a/tests/pos-custom-args/captures/pairs.scala b/tests/pos-custom-args/captures/pairs.scala index 9c8ec003d28d..bc20d20ffd92 100644 --- a/tests/pos-custom-args/captures/pairs.scala +++ b/tests/pos-custom-args/captures/pairs.scala @@ -12,21 +12,21 @@ object Generic: def g(x: Cap): Unit = if d == x then () val p = Pair(f, g) val x1 = p.fst - val x1c: {c} Cap -> Unit = x1 + val x1c: Cap ->{c} Unit = x1 val y1 = p.snd - val y1c: {d} Cap -> Unit = y1 + val y1c: Cap ->{d} Unit = y1 object Monomorphic: - class Pair(x: Cap => Unit, y: {*} Cap -> Unit): - def fst: {x} Cap -> Unit = x - def snd: {y} Cap -> Unit = y + class Pair(x: Cap => Unit, y: Cap => Unit): + def fst: Cap ->{x} Unit = x + def snd: Cap ->{y} Unit = y def test(c: Cap, d: Cap) = def f(x: Cap): Unit = if c == x then () def g(x: Cap): Unit = if d == x then () val p = Pair(f, g) val x1 = p.fst - val x1c: {c} Cap -> Unit = x1 + val x1c: Cap ->{c} Unit = x1 val y1 = p.snd - val y1c: {d} Cap -> Unit = y1 + val y1c: Cap ->{d} Unit = y1 diff --git a/tests/pos-custom-args/captures/selftype-alias.scala b/tests/pos-custom-args/captures/selftype-alias.scala index 284691131c7b..180c7b27b146 100644 --- a/tests/pos-custom-args/captures/selftype-alias.scala +++ b/tests/pos-custom-args/captures/selftype-alias.scala @@ -4,6 +4,5 @@ type AnyIterableOnce[A] = IterableOnce[A]^ /** Iterator can be used only once */ trait IterableOnce[+A]: - //this: IterableOnce[A]{ref any} => this: AnyIterableOnce[A] => def iterator: Iterator[A]^{this} diff --git a/tests/pos-custom-args/captures/stack-alloc.scala b/tests/pos-custom-args/captures/stack-alloc.scala index 03b6708a3119..7013f978c281 100644 --- a/tests/pos-custom-args/captures/stack-alloc.scala +++ b/tests/pos-custom-args/captures/stack-alloc.scala @@ -5,7 +5,7 @@ class Pooled val stack = mutable.ArrayBuffer[Pooled]() var nextFree = 0 -def withFreshPooled[T](op: ({*} Pooled) => T): T = +def withFreshPooled[T](op: Pooled^ => T): T = if nextFree >= stack.size then stack.append(new Pooled) val pooled = stack(nextFree) nextFree = nextFree + 1 diff --git a/tests/pos-custom-args/captures/vars.scala b/tests/pos-custom-args/captures/vars.scala index 12721158a2bb..ccf2cd587eb1 100644 --- a/tests/pos-custom-args/captures/vars.scala +++ b/tests/pos-custom-args/captures/vars.scala @@ -5,13 +5,13 @@ def test(cap1: Cap, cap2: Cap) = var x = f val y = x val z = () => if x("") == "" then "a" else "b" - val zc: {cap1} () -> String = z + val zc: () ->{cap1} String = z val z2 = () => { x = identity } - val z2c: {cap1} () -> Unit = z2 + val z2c: () ->{cap1} Unit = z2 class Ref: - var elem: {cap1} String -> String = null + var elem: String ->{cap1} String = null val r = Ref() r.elem = f - val fc: {cap1} String -> String = r.elem + val fc: String ->{cap1} String = r.elem From 057bd73c79b2b1a6d5899f2cd013e0ce78c67b2c Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 30 Apr 2023 19:46:54 +0200 Subject: [PATCH 525/657] Change to new syntax in run-custom-args And fine tune distinction of ^ as postfix or infix operator --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 7 ++++--- tests/pos-custom-args/captures/trickyTrailingUpArrow.scala | 6 ++++++ tests/run-custom-args/captures/minicheck.scala | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 tests/pos-custom-args/captures/trickyTrailingUpArrow.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 06a8d99bbd1c..c8d2ed0e6457 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1711,10 +1711,11 @@ object Parsers { }) else if in.isIdent(nme.UPARROW) && (in.lookahead.token == LBRACE - || !canStartInfixTypeTokens.contains(in.lookahead.token)) + || !canStartInfixTypeTokens.contains(in.lookahead.token) + || in.lookahead.lineOffset > 0) // Disambiguation: a `^` is treated as a postfix operator meaning `^{any}` - // if followed by `{` or newline, or any other token that cannot start - // an infix type. Otherwise it is treated as an infix operator. + // if followed by `{` or a new line (significant or not), or a token that + // cannot start an infix type. Otherwise it is treated as an infix operator. then val upArrowStart = in.offset in.nextToken() diff --git a/tests/pos-custom-args/captures/trickyTrailingUpArrow.scala b/tests/pos-custom-args/captures/trickyTrailingUpArrow.scala new file mode 100644 index 000000000000..e9d1f50848c0 --- /dev/null +++ b/tests/pos-custom-args/captures/trickyTrailingUpArrow.scala @@ -0,0 +1,6 @@ +object Test: + var x = 0 + type FreshContext = String^ + x += 1 + + inline def ctx(using c: String) = c diff --git a/tests/run-custom-args/captures/minicheck.scala b/tests/run-custom-args/captures/minicheck.scala index 344e021493e5..bdc591580482 100644 --- a/tests/run-custom-args/captures/minicheck.scala +++ b/tests/run-custom-args/captures/minicheck.scala @@ -83,7 +83,7 @@ abstract class Ctx: def run: Run def detached: DetachedContext -type Context = {*} Ctx +type Context = Ctx^ abstract class DetachedContext extends Ctx: def outer: DetachedContext @@ -110,9 +110,9 @@ object NoContext extends FreshCtx(-1): owner = NoSymbol scope = EmptyScope -type FreshContext = {*} FreshCtx +type FreshContext = FreshCtx^ -inline def ctx(using c: Context): {c} Ctx = c +inline def ctx(using c: Context): Ctx^{c} = c // !cc! it does not work if ctxStack is an Array[FreshContext] instead. var ctxStack = Array.tabulate(16)(new FreshCtx(_)) From 14155320891da1cac09b31382294bbb56a393941 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 1 May 2023 10:08:46 +0200 Subject: [PATCH 526/657] Futher fix in trailing uparrow detection Recognize as trailing in front of `->` or `?->` --- .../dotty/tools/dotc/parsing/Parsers.scala | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index c8d2ed0e6457..13fa1d1beba8 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1703,20 +1703,27 @@ object Parsers { def refinedType() = refinedTypeRest(withType()) + /** Disambiguation: a `^` is treated as a postfix operator meaning `^{any}` + * if followed by `{`, `->`, or `?->`, + * or followed by a new line (significant or not), + * or followed by a token that cannot start an infix type. + * Otherwise it is treated as an infix operator. + */ + private def isTrailingUpArrow = + val ahead = in.lookahead + ahead.token == LBRACE + || ahead.isIdent(nme.PUREARROW) + || ahead.isIdent(nme.PURECTXARROW) + || !canStartInfixTypeTokens.contains(ahead.token) + || ahead.lineOffset > 0 + def refinedTypeRest(t: Tree): Tree = { argumentStart() if in.isNestedStart then refinedTypeRest(atSpan(startOffset(t)) { RefinedTypeTree(rejectWildcardType(t), refinement(indentOK = true)) }) - else if in.isIdent(nme.UPARROW) - && (in.lookahead.token == LBRACE - || !canStartInfixTypeTokens.contains(in.lookahead.token) - || in.lookahead.lineOffset > 0) - // Disambiguation: a `^` is treated as a postfix operator meaning `^{any}` - // if followed by `{` or a new line (significant or not), or a token that - // cannot start an infix type. Otherwise it is treated as an infix operator. - then + else if in.isIdent(nme.UPARROW) && isTrailingUpArrow then val upArrowStart = in.offset in.nextToken() def cs = From 9127445c8885b3acbf759c37c15e159a239da75b Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 1 May 2023 10:09:13 +0200 Subject: [PATCH 527/657] Disallow old syntax for captures --- .../dotty/tools/dotc/parsing/Parsers.scala | 63 +++---------------- tests/neg-custom-args/capt-wf.scala | 28 ++++----- .../captures/boundschecks.scala | 8 +-- .../captures/boundschecks2.scala | 6 +- .../captures/box-adapt-boxing.scala | 22 +++---- .../captures/box-adapt-cases.scala | 18 +++--- .../captures/box-adapt-cov.scala | 16 ++--- .../captures/box-adapt-cs.scala | 18 +++--- .../captures/box-adapt-depfun.scala | 18 +++--- .../captures/box-adapt-typefun.scala | 12 ++-- .../captures/capt-depfun.scala | 2 +- tests/neg-custom-args/captures/capt-env.scala | 2 +- .../captures/capt-wf-typer.scala | 4 +- tests/neg-custom-args/captures/capt-wf2.scala | 4 +- tests/neg-custom-args/captures/capt2.scala | 6 +- .../captures/caseclass/Test_2.scala | 8 +-- .../neg-custom-args/captures/cc-depfun.scala | 4 +- tests/neg-custom-args/captures/cc-this2.check | 2 +- .../captures/cc-this2/D_2.scala | 2 +- .../captures/class-constr.scala | 14 ++--- tests/neg-custom-args/captures/classes.scala | 6 +- tests/neg-custom-args/captures/ctest.scala | 2 +- .../captures/emptyref-in-self.scala | 4 +- tests/neg-custom-args/captures/filevar.scala | 4 +- .../captures/heal-tparam-cs.scala | 20 +++--- tests/neg-custom-args/captures/i15049.scala | 6 +- tests/neg-custom-args/captures/i15749.scala | 6 +- tests/neg-custom-args/captures/i15749a.scala | 14 ++--- tests/neg-custom-args/captures/i15921.scala | 2 +- .../captures/i15923-cases.scala | 6 +- tests/neg-custom-args/captures/i15923.scala | 4 +- tests/neg-custom-args/captures/i16114.scala | 32 +++++----- .../neg-custom-args/captures/impurefuns.scala | 3 + .../captures/inner-classes.scala | 8 +-- tests/neg-custom-args/captures/lazyref.scala | 2 +- .../override-adapt-box-selftype.scala | 36 +++++------ .../captures/override-adapt-box.scala | 8 +-- .../captures/override-boxed.scala | 7 ++- .../captures/stack-alloc.scala | 2 +- tests/neg-custom-args/captures/try3.scala | 4 +- tests/neg-custom-args/captures/unbox.scala | 2 +- tests/pending/neg/cc-depfun.scala | 14 +++++ .../captures/lazylists-mono.scala | 6 +- tests/pos-custom-args/captures/lists.scala | 18 +++--- .../captures/trickyTrailingUpArrow.scala | 3 + .../dotc/transform/TreeMapWithStages.scala | 2 +- tests/pos/boxmap-paper.scala | 16 ++--- 47 files changed, 232 insertions(+), 262 deletions(-) create mode 100644 tests/neg-custom-args/captures/impurefuns.scala create mode 100644 tests/pending/neg/cc-depfun.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 13fa1d1beba8..5c306302361b 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -970,29 +970,6 @@ object Parsers { isArrowIndent() else false - /** Under captureChecking language import: is the following token sequence a - * capture set `{ref1, ..., refN}` followed by a token that can start a type? - */ - def followingIsCaptureSet(): Boolean = - Feature.ccEnabled && { - val lookahead = in.LookaheadScanner() - def followingIsTypeStart() = - lookahead.nextToken() - canStartInfixTypeTokens.contains(lookahead.token) - || lookahead.token == LBRACKET - def recur(): Boolean = - (lookahead.isIdent || lookahead.token == THIS) && { - lookahead.nextToken() - if lookahead.token == COMMA then - lookahead.nextToken() - recur() - else - lookahead.token == RBRACE && followingIsTypeStart() - } - lookahead.nextToken() - if lookahead.token == RBRACE then followingIsTypeStart() else recur() - } - /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ var opStack: List[OpInfo] = Nil @@ -1504,9 +1481,12 @@ object Parsers { val paramSpan = Span(start, in.lastOffset) atSpan(start, in.offset) { var token = in.token + var isPure = false if isPureArrow(nme.PUREARROW) then + isPure = true token = ARROW else if isPureArrow(nme.PURECTXARROW) then + isPure = true token = CTXARROW else if token == TLARROW then if !imods.flags.isEmpty || params.isEmpty then @@ -1525,7 +1505,7 @@ object Parsers { else accept(ARROW) - val resultType = capturesAndResult(typ) + val resultType = if isPure then capturesAndResult(typ) else typ() if token == TLARROW then for case ValDef(_, tpt, _) <- params do if isByNameType(tpt) then @@ -1618,8 +1598,6 @@ object Parsers { } else { accept(TLARROW); typ() } } - else if in.token == LBRACE && followingIsCaptureSet() then - CapturingTypeTree(captureSet(), typ()) else if (in.token == INDENT) enclosed(INDENT, typ()) else infixType() @@ -1958,31 +1936,9 @@ object Parsers { def paramTypeOf(core: () => Tree): Tree = if in.token == ARROW || isPureArrow(nme.PUREARROW) then val isImpure = in.token == ARROW - val tp = atSpan(in.skipToken()) { ByNameTypeTree(capturesAndResult(core)) } + val tp = atSpan(in.skipToken()): + ByNameTypeTree(if isImpure then core() else capturesAndResult(core)) if isImpure && Feature.pureFunsEnabled then ImpureByNameTypeTree(tp) else tp - else if in.token == LBRACE && followingIsCaptureSet() then - val start = in.offset - val cs = captureSet() - val endCsOffset = in.lastOffset - val startTpOffset = in.offset - val tp = paramTypeOf(core) - val tp1 = tp match - case ImpureByNameTypeTree(tp1) => - syntaxError(em"explicit captureSet is superfluous for impure call-by-name type", start) - tp1 - case CapturingTypeTree(_, tp1: ByNameTypeTree) => - syntaxError(em"only one captureSet is allowed here", start) - tp1 - case _: ByNameTypeTree if startTpOffset > endCsOffset => - report.warning( - i"""Style: by-name `->` should immediately follow closing `}` of capture set - |to avoid confusion with function type. - |That is, `{c}-> T` instead of `{c} -> T`.""", - source.atSpan(Span(startTpOffset, startTpOffset))) - tp - case _ => - tp - CapturingTypeTree(cs, tp1) else core() @@ -2069,8 +2025,6 @@ object Parsers { def typeDependingOn(location: Location): Tree = if location.inParens then typ() else if location.inPattern then rejectWildcardType(refinedType()) - else if in.token == LBRACE && followingIsCaptureSet() then - CapturingTypeTree(captureSet(), infixType()) else infixType() /* ----------- EXPRESSIONS ------------------------------------------------ */ @@ -4218,10 +4172,7 @@ object Parsers { val selfTpt = if in.isColon then in.nextToken() - if in.token == LBRACE && followingIsCaptureSet() then - CapturingTypeTree(captureSet(), infixType()) - else - infixType() + infixType() else if selfName == nme.WILDCARD then accept(COLONfollow) TypeTree() diff --git a/tests/neg-custom-args/capt-wf.scala b/tests/neg-custom-args/capt-wf.scala index 3bd80e0d0f68..fe883af0ba8e 100644 --- a/tests/neg-custom-args/capt-wf.scala +++ b/tests/neg-custom-args/capt-wf.scala @@ -1,35 +1,35 @@ class C -type Cap = {*} C +type Cap = C^ object foo def test(c: Cap, other: String): Unit = - val x1: {*} C = ??? // OK - val x2: {other} C = ??? // error: cs is empty + val x1: C^ = ??? // OK + val x2: C^{other} = ??? // error: cs is empty val s1 = () => "abc" - val x3: {s1} C = ??? // error: cs is empty + val x3: C^{s1} = ??? // error: cs is empty val x3a: () -> String = s1 val s2 = () => if x1 == null then "" else "abc" - val x4: {s2} C = ??? // OK - val x5: {c, c} C = ??? // error: redundant - val x6: {c} {c} C = ??? // error: redundant - val x7: {c} Cap = ??? // error: redundant - val x8: {*} {c} C = ??? // OK - val x9: {c, *} C = ??? // error: redundant - val x10: {*, c} C = ??? // error: redundant + val x4: C^{s2} = ??? // OK + val x5: C^{c, c} = ??? // error: redundant + // val x6: C^{c}^{c} = ??? // would be syntax error + val x7: Cap^{c} = ??? // error: redundant + // val x8: C^{c}^{*} = ??? // would be syntax error + val x9: C^{c, *} = ??? // error: redundant + val x10: C^{*, c} = ??? // error: redundant def even(n: Int): Boolean = if n == 0 then true else odd(n - 1) def odd(n: Int): Boolean = if n == 1 then true else even(n - 1) val e1 = even val o1 = odd - val y1: {e1} String = ??? // error cs is empty - val y2: {o1} String = ??? // error cs is empty + val y1: String^{e1} = ??? // error cs is empty + val y2: String^{o1} = ??? // error cs is empty lazy val ev: (Int -> Boolean) = (n: Int) => lazy val od: (Int -> Boolean) = (n: Int) => if n == 1 then true else ev(n - 1) if n == 0 then true else od(n - 1) - val y3: {ev} String = ??? // error cs is empty + val y3: String^{ev} = ??? // error cs is empty () \ No newline at end of file diff --git a/tests/neg-custom-args/captures/boundschecks.scala b/tests/neg-custom-args/captures/boundschecks.scala index cf4eab28f19d..766d89d2f37b 100644 --- a/tests/neg-custom-args/captures/boundschecks.scala +++ b/tests/neg-custom-args/captures/boundschecks.scala @@ -6,13 +6,13 @@ object test { class C[X <: Tree](x: X) - def foo(t: {*} Tree) = + def foo(t: Tree^) = f(t) // error - f[{*} Tree](t) // error + f[Tree^](t) // error f[Tree](t) // error val c1 = C(t) // error - val c2 = C[{*} Tree](t) // error + val c2 = C[Tree^](t) // error val c3 = C[Tree](t) // error - val foo: C[{*} Tree] = ??? + val foo: C[Tree^] = ??? } diff --git a/tests/neg-custom-args/captures/boundschecks2.scala b/tests/neg-custom-args/captures/boundschecks2.scala index f6927b04931b..923758d722f9 100644 --- a/tests/neg-custom-args/captures/boundschecks2.scala +++ b/tests/neg-custom-args/captures/boundschecks2.scala @@ -6,8 +6,8 @@ object test { class C[X <: Tree](x: X) - val foo: C[{*} Tree] = ??? // error - type T = C[{*} Tree] // error + val foo: C[Tree^] = ??? // error + type T = C[Tree^] // error val bar: T -> T = ??? - val baz: C[{*} Tree] -> Unit = ??? // error + val baz: C[Tree^] -> Unit = ??? // error } diff --git a/tests/neg-custom-args/captures/box-adapt-boxing.scala b/tests/neg-custom-args/captures/box-adapt-boxing.scala index 7a624d4225fc..ea133051a21a 100644 --- a/tests/neg-custom-args/captures/box-adapt-boxing.scala +++ b/tests/neg-custom-args/captures/box-adapt-boxing.scala @@ -1,23 +1,23 @@ trait Cap -def main(io: {*} Cap, fs: {*} Cap): Unit = { - val test1: {} Unit -> Unit = _ => { // error - type Op = [T] -> ({io} T -> Unit) -> Unit - val f: ({io} Cap) -> Unit = ??? +def main(io: Cap^, fs: Cap^): Unit = { + val test1: Unit -> Unit = _ => { // error + type Op = [T] -> (T ->{io} Unit) -> Unit + val f: (Cap^{io}) -> Unit = ??? val op: Op = ??? - op[{io} Cap](f) + op[Cap^{io}](f) // expected type of f: {io} (box {io} Cap) -> Unit // actual type: ({io} Cap) -> Unit // adapting f to the expected type will also // charge the environment with {io} } - val test2: {} Unit -> Unit = _ => { + val test2: Unit -> Unit = _ => { type Box[X] = X type Op0[X] = Box[X] -> Unit type Op1[X] = Unit -> Box[X] - val f: Unit -> ({io} Cap) -> Unit = ??? - val test: {} Op1[{io} Op0[{io} Cap]] = f + val f: Unit -> (Cap^{io}) -> Unit = ??? + val test: Op1[Op0[Cap^{io}]^{io}]^{} = f // expected: {} Unit -> box {io} (box {io} Cap) -> Unit // actual: Unit -> ({io} Cap) -> Unit // @@ -31,8 +31,8 @@ def main(io: {*} Cap, fs: {*} Cap): Unit = { type Box[X] = X type Id[X] = Box[X] -> Unit type Op[X] = Unit -> Box[X] - val f: Unit -> ({io} Cap) -> Unit = ??? - val g: Op[{fs} Id[{io} Cap]] = f // error - val h: {} Op[{io} Id[{io} Cap]] = f + val f: Unit -> (Cap^{io}) -> Unit = ??? + val g: Op[Id[Cap^{io}]^{fs}] = f // error + val h: Op[Id[Cap^{io}]^{io}] = f } } diff --git a/tests/neg-custom-args/captures/box-adapt-cases.scala b/tests/neg-custom-args/captures/box-adapt-cases.scala index 049ff385d73c..faaf256e470b 100644 --- a/tests/neg-custom-args/captures/box-adapt-cases.scala +++ b/tests/neg-custom-args/captures/box-adapt-cases.scala @@ -3,27 +3,27 @@ trait Cap { def use(): Int } def test1(): Unit = { type Id[X] = [T] -> (op: X => T) -> T - val x: Id[{*} Cap] = ??? + val x: Id[Cap^] = ??? x(cap => cap.use()) // error } -def test2(io: {*} Cap): Unit = { +def test2(io: Cap^{*}): Unit = { type Id[X] = [T] -> (op: X -> T) -> T - val x: Id[{io} Cap] = ??? + val x: Id[Cap^{io}] = ??? x(cap => cap.use()) // error } -def test3(io: {*} Cap): Unit = { - type Id[X] = [T] -> (op: {io} X -> T) -> T +def test3(io: Cap^{*}): Unit = { + type Id[X] = [T] -> (op: X ->{io} T) -> T - val x: Id[{io} Cap] = ??? + val x: Id[Cap^{io}] = ??? x(cap => cap.use()) // ok } -def test4(io: {*} Cap, fs: {*} Cap): Unit = { - type Id[X] = [T] -> (op: {io} X -> T) -> T +def test4(io: Cap^{*}, fs: Cap^{*}): Unit = { + type Id[X] = [T] -> (op: X ->{io} T) -> T - val x: Id[{io, fs} Cap] = ??? + val x: Id[Cap^{io, fs}] = ??? x(cap => cap.use()) // error } diff --git a/tests/neg-custom-args/captures/box-adapt-cov.scala b/tests/neg-custom-args/captures/box-adapt-cov.scala index 2040a1c4654d..ebdb68cf0ca4 100644 --- a/tests/neg-custom-args/captures/box-adapt-cov.scala +++ b/tests/neg-custom-args/captures/box-adapt-cov.scala @@ -1,14 +1,14 @@ trait Cap -def test1(io: {*} Cap) = { +def test1(io: Cap^{*}) = { type Op[X] = [T] -> Unit -> X - val f: Op[{io} Cap] = ??? - val x: [T] -> Unit -> ({io} Cap) = f // error + val f: Op[Cap^{io}] = ??? + val x: [T] -> Unit -> Cap^{io} = f // error } -def test2(io: {*} Cap) = { - type Op[X] = [T] -> Unit -> {io} X - val f: Op[{io} Cap] = ??? - val x: Unit -> ({io} Cap) = f[Unit] // error - val x1: {io} Unit -> ({io} Cap) = f[Unit] // ok +def test2(io: Cap^{*}) = { + type Op[X] = [T] -> Unit -> X^{io} + val f: Op[Cap^{io}] = ??? + val x: Unit -> Cap^{io} = f[Unit] // error + val x1: Unit ->{io} Cap^{io} = f[Unit] // ok } diff --git a/tests/neg-custom-args/captures/box-adapt-cs.scala b/tests/neg-custom-args/captures/box-adapt-cs.scala index e35388efd203..a5344d52c30f 100644 --- a/tests/neg-custom-args/captures/box-adapt-cs.scala +++ b/tests/neg-custom-args/captures/box-adapt-cs.scala @@ -1,19 +1,17 @@ trait Cap { def use(): Int } -def test1(io: {*} Cap): Unit = { - type Id[X] = [T] -> (op: {io} X -> T) -> T +def test1(io: Cap^{*}): Unit = { + type Id[X] = [T] -> (op: X ->{io} T) -> T - val x: Id[{io} Cap] = ??? - val f: ({*} Cap) -> Unit = ??? + val x: Id[Cap^{io}] = ??? + val f: (Cap^{*}) -> Unit = ??? x(f) // ok - // actual: {*} Cap -> Unit - // expected: {io} box {io} Cap -> Unit } -def test2(io: {*} Cap): Unit = { - type Id[X] = [T] -> (op: {*} X -> T) -> T +def test2(io: Cap^{*}): Unit = { + type Id[X] = [T] -> (op: X => T) -> T - val x: Id[{*} Cap] = ??? - val f: ({io} Cap) -> Unit = ??? + val x: Id[Cap^] = ??? + val f: Cap^{io} -> Unit = ??? x(f) // error } diff --git a/tests/neg-custom-args/captures/box-adapt-depfun.scala b/tests/neg-custom-args/captures/box-adapt-depfun.scala index 294e2c33f7fa..357cfb4a8d9d 100644 --- a/tests/neg-custom-args/captures/box-adapt-depfun.scala +++ b/tests/neg-custom-args/captures/box-adapt-depfun.scala @@ -1,23 +1,23 @@ trait Cap { def use(): Int } -def test1(io: {*} Cap): Unit = { - type Id[X] = [T] -> (op: {io} X -> T) -> T +def test1(io: Cap^): Unit = { + type Id[X] = [T] -> (op: X ->{io} T) -> T - val x: Id[{io} Cap] = ??? + val x: Id[Cap]^{io} = ??? x(cap => cap.use()) // ok } -def test2(io: {*} Cap): Unit = { - type Id[X] = [T] -> (op: {io} (x: X) -> T) -> T +def test2(io: Cap^): Unit = { + type Id[X] = [T] -> (op: (x: X) ->{io} T) -> T - val x: Id[{io} Cap] = ??? + val x: Id[Cap^{io}] = ??? x(cap => cap.use()) // should work when the expected type is a dependent function } -def test3(io: {*} Cap): Unit = { - type Id[X] = [T] -> (op: {} (x: X) -> T) -> T +def test3(io: Cap^{*}): Unit = { + type Id[X] = [T] -> (op: (x: X) ->{} T) -> T - val x: Id[{io} Cap] = ??? + val x: Id[Cap^{io}] = ??? x(cap => cap.use()) // error } diff --git a/tests/neg-custom-args/captures/box-adapt-typefun.scala b/tests/neg-custom-args/captures/box-adapt-typefun.scala index b14b07e72e9b..bbc953f74e9e 100644 --- a/tests/neg-custom-args/captures/box-adapt-typefun.scala +++ b/tests/neg-custom-args/captures/box-adapt-typefun.scala @@ -1,13 +1,13 @@ trait Cap { def use(): Int } -def test1(io: {*} Cap): Unit = { +def test1(io: Cap^{*}): Unit = { type Op[X] = [T] -> X -> Unit - val f: [T] -> ({io} Cap) -> Unit = ??? - val op: Op[{io} Cap] = f // error + val f: [T] -> (Cap^{io}) -> Unit = ??? + val op: Op[Cap^{io}] = f // error } -def test2(io: {*} Cap): Unit = { +def test2(io: Cap^{*}): Unit = { type Lazy[X] = [T] -> Unit -> X - val f: Lazy[{io} Cap] = ??? - val test: [T] -> Unit -> ({io} Cap) = f // error + val f: Lazy[Cap^{io}] = ??? + val test: [T] -> Unit -> (Cap^{io}) = f // error } diff --git a/tests/neg-custom-args/captures/capt-depfun.scala b/tests/neg-custom-args/captures/capt-depfun.scala index c01eed7c4b25..95eed2c29659 100644 --- a/tests/neg-custom-args/captures/capt-depfun.scala +++ b/tests/neg-custom-args/captures/capt-depfun.scala @@ -6,4 +6,4 @@ class Str def f(y: Cap, z: Cap) = def g(): C @retains(y, z) = ??? val ac: ((x: Cap) => Str @retains(x) => Str @retains(x)) = ??? - val dc: (({y, z} Str) => {y, z} Str) = ac(g()) // error + val dc: ((Str^{y, z}) => Str^{y, z}) = ac(g()) // error diff --git a/tests/neg-custom-args/captures/capt-env.scala b/tests/neg-custom-args/captures/capt-env.scala index 52fa4abfdaa8..6602678af167 100644 --- a/tests/neg-custom-args/captures/capt-env.scala +++ b/tests/neg-custom-args/captures/capt-env.scala @@ -1,5 +1,5 @@ class C -type Cap = {*} C +type Cap = C^ class Pair[+A, +B](x: A, y: B): def fst: A = x diff --git a/tests/neg-custom-args/captures/capt-wf-typer.scala b/tests/neg-custom-args/captures/capt-wf-typer.scala index 4fc50caed1f7..09b2841d3c77 100644 --- a/tests/neg-custom-args/captures/capt-wf-typer.scala +++ b/tests/neg-custom-args/captures/capt-wf-typer.scala @@ -1,11 +1,11 @@ import annotation.retains class C -type Cap = {*} C +type Cap = C^ object foo def test(c: Cap, other: String): Unit = - val x7: {c} String = ??? // OK + val x7: String^{c} = ??? // OK val x8: String @retains(x7 + x7) = ??? // error val x9: String @retains(foo) = ??? // error () \ No newline at end of file diff --git a/tests/neg-custom-args/captures/capt-wf2.scala b/tests/neg-custom-args/captures/capt-wf2.scala index ddde535fcab0..6c65e0dc77f7 100644 --- a/tests/neg-custom-args/captures/capt-wf2.scala +++ b/tests/neg-custom-args/captures/capt-wf2.scala @@ -1,5 +1,5 @@ @annotation.capability class C def test(c: C) = - var x: {c} Any = ??? - val y: {x} Any = x // error + var x: Any^{c} = ??? + val y: Any^{x} = x // error diff --git a/tests/neg-custom-args/captures/capt2.scala b/tests/neg-custom-args/captures/capt2.scala index 8b08832dfdb9..cd6f41424a22 100644 --- a/tests/neg-custom-args/captures/capt2.scala +++ b/tests/neg-custom-args/captures/capt2.scala @@ -1,9 +1,9 @@ //import scala.retains class C -type Cap = {*} C +type Cap = C^ -def f1(c: Cap): (() -> {c} C) = () => c // error, but would be OK under capture abbreciations for funciton types -def f2(c: Cap): ({c} () -> C) = () => c // error +def f1(c: Cap): (() -> C^{c}) = () => c // error, but would be OK under capture abbreciations for funciton types +def f2(c: Cap): (() ->{c} C) = () => c // error def h5(x: Cap): () -> C = f1(x) // error diff --git a/tests/neg-custom-args/captures/caseclass/Test_2.scala b/tests/neg-custom-args/captures/caseclass/Test_2.scala index 4eac6a260292..bffc0a295bdc 100644 --- a/tests/neg-custom-args/captures/caseclass/Test_2.scala +++ b/tests/neg-custom-args/captures/caseclass/Test_2.scala @@ -2,7 +2,7 @@ def test(c: C) = val pure: () -> Unit = () => () val impure: () => Unit = pure - val mixed: {c} () -> Unit = pure + val mixed: () ->{c} Unit = pure val x = Ref(impure) val _: Ref = x // error val y = x.copy() @@ -16,10 +16,10 @@ def test(c: C) = val yc2: Ref = y2 val x3 = Ref(mixed) - val _: {c} Ref = x3 + val _: Ref^{c} = x3 val y3 = x3.copy() - val yc3: {c} Ref = y3 + val yc3: Ref^{c} = y3 val y4 = y3 match case Ref(xx) => xx - val y4c: {x3} () -> Unit = y4 + val y4c: () ->{x3} Unit = y4 diff --git a/tests/neg-custom-args/captures/cc-depfun.scala b/tests/neg-custom-args/captures/cc-depfun.scala index c4ef303f4712..106a73dd7ce1 100644 --- a/tests/neg-custom-args/captures/cc-depfun.scala +++ b/tests/neg-custom-args/captures/cc-depfun.scala @@ -1,9 +1,9 @@ trait Cap { def use(): Unit } def main() = { - val f: (io: {*} Cap) -> {} () -> Unit = + val f: (io: Cap^) -> () ->{} Unit = io => () => io.use() // error - val g: ({*} Cap) -> {} () -> Unit = + val g: (Cap^) -> () ->{} Unit = io => () => io.use() // error } diff --git a/tests/neg-custom-args/captures/cc-this2.check b/tests/neg-custom-args/captures/cc-this2.check index 086524d307a2..9db2f4efcc5a 100644 --- a/tests/neg-custom-args/captures/cc-this2.check +++ b/tests/neg-custom-args/captures/cc-this2.check @@ -3,4 +3,4 @@ 2 |class D extends C: // error |^ |reference (scala.caps.* : Any) is not included in allowed capture set {} of pure base class class C -3 | this: {*} D => +3 | this: D^ => diff --git a/tests/neg-custom-args/captures/cc-this2/D_2.scala b/tests/neg-custom-args/captures/cc-this2/D_2.scala index 793f3f6353a9..b22e5e456092 100644 --- a/tests/neg-custom-args/captures/cc-this2/D_2.scala +++ b/tests/neg-custom-args/captures/cc-this2/D_2.scala @@ -1,3 +1,3 @@ class D extends C: // error - this: {*} D => + this: D^ => diff --git a/tests/neg-custom-args/captures/class-constr.scala b/tests/neg-custom-args/captures/class-constr.scala index eeedf1043f37..9afb6972ccfa 100644 --- a/tests/neg-custom-args/captures/class-constr.scala +++ b/tests/neg-custom-args/captures/class-constr.scala @@ -6,10 +6,10 @@ class C(x: Cap, @constructorOnly y: Cap) def test(a: Cap, b: Cap) = val f = () => C(a, b) - val f_ok: {a, b} () -> {a} C = f - val f_no1: {a, b} () -> C = f // error - val f_no2: {a} () -> {a} C = f // error - val f_no3: {b} () -> {a} C = f // error + val f_ok: () ->{a, b} C^{a} = f + val f_no1: () ->{a, b} C = f // error + val f_no2: () ->{a} C^{a} = f // error + val f_no3: () ->{a} C^{a} = f // error class D: val xz = @@ -19,6 +19,6 @@ def test(a: Cap, b: Cap) = println(b) 2 val d = () => new D() - val d_ok1: {a, b} () -> {a, b} D = d - val d_ok2: () -> {a, b} D = d // because of function shorthand - val d_ok3: {a, b} () -> {b} D = d // error, but should work + val d_ok1: () ->{a, b} D^{a, b} = d + val d_ok2: () -> D^{a, b} = d // because of function shorthand + val d_ok3: () ->{a, b} D^{b} = d // error, but should work diff --git a/tests/neg-custom-args/captures/classes.scala b/tests/neg-custom-args/captures/classes.scala index e4c141ea981b..3572e31a6f50 100644 --- a/tests/neg-custom-args/captures/classes.scala +++ b/tests/neg-custom-args/captures/classes.scala @@ -1,12 +1,12 @@ class B -type Cap = {*} B +type Cap = B^ class C0(n: Cap) // was error: class parameter must be a `val`, now OK class C(val n: Cap): - def foo(): {n} B = n + def foo(): B^{n} = n def test(x: Cap, y: Cap) = val c0 = C(x) val c1: C = c0 // error val c2 = if ??? then C(x) else identity(C(y)) - val c3: {x} C { val n: {x, y} B } = c2 // error + val c3: C { val n: B^{x, y} }^{x} = c2 // error diff --git a/tests/neg-custom-args/captures/ctest.scala b/tests/neg-custom-args/captures/ctest.scala index 08bec16d8177..37d6ad034936 100644 --- a/tests/neg-custom-args/captures/ctest.scala +++ b/tests/neg-custom-args/captures/ctest.scala @@ -1,5 +1,5 @@ class CC -type Cap = {*} CC +type Cap = CC^ def test(cap1: Cap, cap2: Cap) = var b: List[String => String] = Nil // was error, now OK diff --git a/tests/neg-custom-args/captures/emptyref-in-self.scala b/tests/neg-custom-args/captures/emptyref-in-self.scala index 60f782deca6b..8bac47212f5b 100644 --- a/tests/neg-custom-args/captures/emptyref-in-self.scala +++ b/tests/neg-custom-args/captures/emptyref-in-self.scala @@ -1,3 +1,3 @@ -class Zip[A, B](underlying: String, other: {*} String) { - this: {underlying, other} Zip[A, B] => // error +class Zip[A, B](underlying: String, other: String^) { + this: Zip[A, B]^{underlying, other} => // error } diff --git a/tests/neg-custom-args/captures/filevar.scala b/tests/neg-custom-args/captures/filevar.scala index 9251be8f1592..474ba36b0e79 100644 --- a/tests/neg-custom-args/captures/filevar.scala +++ b/tests/neg-custom-args/captures/filevar.scala @@ -5,10 +5,10 @@ class File: def write(x: String): Unit = ??? class Service: - var file: {*} File = uninitialized + var file: File^ = uninitialized def log = file.write("log") // error -def withFile[T](op: (f: {*} File) => T): T = +def withFile[T](op: (f: File^) => T): T = op(new File) def test = diff --git a/tests/neg-custom-args/captures/heal-tparam-cs.scala b/tests/neg-custom-args/captures/heal-tparam-cs.scala index 3ff34d0a8a42..f4c293e33d01 100644 --- a/tests/neg-custom-args/captures/heal-tparam-cs.scala +++ b/tests/neg-custom-args/captures/heal-tparam-cs.scala @@ -2,31 +2,31 @@ import language.experimental.captureChecking trait Cap { def use(): Unit } -def localCap[T](op: (cap: {*} Cap) => T): T = ??? +def localCap[T](op: (cap: Cap^{*}) => T): T = ??? -def main(io: {*} Cap, net: {*} Cap): Unit = { +def main(io: Cap^{*}, net: Cap^{*}): Unit = { val test1 = localCap { cap => // error () => { cap.use() } } - val test2: (cap: {*} Cap) -> {cap} () -> Unit = + val test2: (cap: Cap^{*}) -> () ->{cap} Unit = localCap { cap => // should work - (cap1: {*} Cap) => () => { cap1.use() } + (cap1: Cap^{*}) => () => { cap1.use() } } - val test3: (cap: {io} Cap) -> {io} () -> Unit = + val test3: (cap: Cap^{io}) -> () ->{io} Unit = localCap { cap => // should work - (cap1: {io} Cap) => () => { cap1.use() } + (cap1: Cap^{io}) => () => { cap1.use() } } - val test4: (cap: {io} Cap) -> {net} () -> Unit = + val test4: (cap: Cap^{io}) -> () ->{net} Unit = localCap { cap => // error - (cap1: {io} Cap) => () => { cap1.use() } + (cap1: Cap^{io}) => () => { cap1.use() } } - def localCap2[T](op: (cap: {io} Cap) => T): T = ??? + def localCap2[T](op: (cap: Cap^{io}) => T): T = ??? - val test5: {io} () -> Unit = + val test5: () ->{io} Unit = localCap2 { cap => // ok () => { cap.use() } } diff --git a/tests/neg-custom-args/captures/i15049.scala b/tests/neg-custom-args/captures/i15049.scala index 4e32172c025d..f12bb8028797 100644 --- a/tests/neg-custom-args/captures/i15049.scala +++ b/tests/neg-custom-args/captures/i15049.scala @@ -1,10 +1,10 @@ class Session: def request = "Response" class Foo: - private val session: {*} Session = new Session - def withSession[T](f: ({*} Session) => T): T = f(session) + private val session: Session^{*} = new Session + def withSession[T](f: (Session^{*}) => T): T = f(session) def Test = val f = new Foo f.withSession(s => s).request // error - f.withSession[{*} Session](t => t) // error + f.withSession[Session^](t => t) // error diff --git a/tests/neg-custom-args/captures/i15749.scala b/tests/neg-custom-args/captures/i15749.scala index 00d1811498f7..b5aa233e7401 100644 --- a/tests/neg-custom-args/captures/i15749.scala +++ b/tests/neg-custom-args/captures/i15749.scala @@ -1,13 +1,13 @@ class Unit object unit extends Unit -type Top = {*} Any +type Top = Any^{*} -type LazyVal[T] = {*} Unit -> T +type LazyVal[T] = Unit ->{*} T class Foo[T](val x: T) -// Foo[□ {*} Unit -> T] +// Foo[□ Unit => T] type BoxedLazyVal[T] = Foo[LazyVal[T]] def force[A](v: BoxedLazyVal[A]): A = diff --git a/tests/neg-custom-args/captures/i15749a.scala b/tests/neg-custom-args/captures/i15749a.scala index 9e439e28e98c..8a39db98c964 100644 --- a/tests/neg-custom-args/captures/i15749a.scala +++ b/tests/neg-custom-args/captures/i15749a.scala @@ -1,21 +1,21 @@ class Unit object unit extends Unit -type Top = {*} Any +type Top = Any^ -type Wrapper[T] = [X] -> (op: {*} T -> X) -> X +type Wrapper[T] = [X] -> (op: T ->{*} X) -> X def test = def wrapper[T](x: T): Wrapper[T] = - [X] => (op: {*} T -> X) => op(x) + [X] => (op: T ->{*} X) => op(x) - def strictMap[A <: Top, B <: Top](mx: Wrapper[A])(f: {*} A -> B): Wrapper[B] = + def strictMap[A <: Top, B <: Top](mx: Wrapper[A])(f: A ->{*} B): Wrapper[B] = mx((x: A) => wrapper(f(x))) - def force[A](thunk: {*} Unit -> A): A = thunk(unit) + def force[A](thunk: Unit ->{*} A): A = thunk(unit) - def forceWrapper[A](mx: Wrapper[{*} Unit -> A]): Wrapper[A] = + def forceWrapper[A](mx: Wrapper[Unit ->{*} A]): Wrapper[A] = // Γ ⊢ mx: Wrapper[□ {*} Unit => A] // `force` should be typed as ∀(□ {*} Unit -> A) A, but it can not - strictMap[{*} Unit -> A, A](mx)(t => force[A](t)) // error + strictMap[Unit ->{*} A, A](mx)(t => force[A](t)) // error diff --git a/tests/neg-custom-args/captures/i15921.scala b/tests/neg-custom-args/captures/i15921.scala index 291673746e33..233ef23991fc 100644 --- a/tests/neg-custom-args/captures/i15921.scala +++ b/tests/neg-custom-args/captures/i15921.scala @@ -1,7 +1,7 @@ trait Stream { def close(): Unit = (); def write(x: Any): Unit = () } object Test { - def usingLogFile[T](op: (c: {*} Stream) => T): T = + def usingLogFile[T](op: (c: Stream^) => T): T = val logFile = new Stream { } val result = op(logFile) logFile.close() diff --git a/tests/neg-custom-args/captures/i15923-cases.scala b/tests/neg-custom-args/captures/i15923-cases.scala index 5fbb95355a60..2b034009fdc7 100644 --- a/tests/neg-custom-args/captures/i15923-cases.scala +++ b/tests/neg-custom-args/captures/i15923-cases.scala @@ -2,14 +2,14 @@ trait Cap { def use(): Int } type Id[X] = [T] -> (op: X => T) -> T def mkId[X](x: X): Id[X] = [T] => (op: X => T) => op(x) -def foo(x: Id[{*} Cap]) = { +def foo(x: Id[Cap^{*}]) = { x(_.use()) // error } -def bar(io: {*} Cap, x: Id[{io} Cap]) = { +def bar(io: Cap^{*}, x: Id[Cap^{io}]) = { x(_.use()) } -def barAlt(a: {*} Cap, b: {*} Cap, x: Id[{a, b} Cap]) = { +def barAlt(a: Cap^{*}, b: Cap^{*}, x: Id[Cap]^{a, b}) = { x(_.use()) } diff --git a/tests/neg-custom-args/captures/i15923.scala b/tests/neg-custom-args/captures/i15923.scala index ac7ee995150e..3ce9d6121396 100644 --- a/tests/neg-custom-args/captures/i15923.scala +++ b/tests/neg-custom-args/captures/i15923.scala @@ -3,8 +3,8 @@ type Id[X] = [T] -> (op: X => T) -> T def mkId[X](x: X): Id[X] = [T] => (op: X => T) => op(x) def bar() = { - def withCap[X](op: ({*} Cap) => X): X = { - val cap: {*} Cap = new Cap { def use() = { println("cap is used"); 0 } } + def withCap[X](op: (Cap^) => X): X = { + val cap: Cap^ = new Cap { def use() = { println("cap is used"); 0 } } val result = op(cap) result } diff --git a/tests/neg-custom-args/captures/i16114.scala b/tests/neg-custom-args/captures/i16114.scala index cc491226f9df..706196fd7182 100644 --- a/tests/neg-custom-args/captures/i16114.scala +++ b/tests/neg-custom-args/captures/i16114.scala @@ -1,46 +1,46 @@ trait Cap { def use(): Int; def close(): Unit } -def mkCap(): {*} Cap = ??? +def mkCap(): Cap^ = ??? def expect[T](x: T): x.type = x -def withCap[T](op: ({*} Cap) => T): T = { - val cap: {*} Cap = mkCap() +def withCap[T](op: Cap^ => T): T = { + val cap: Cap^ = mkCap() val result = op(cap) cap.close() result } -def main(fs: {*} Cap): Unit = { - def badOp(io: {*} Cap): {} Unit -> Unit = { - val op1: {io} Unit -> Unit = (x: Unit) => // error // limitation - expect[{*} Cap] { +def main(fs: Cap^): Unit = { + def badOp(io: Cap^{*}): Unit ->{} Unit = { + val op1: Unit ->{io} Unit = (x: Unit) => // error // limitation + expect[Cap^] { io.use() fs } - val op2: {fs} Unit -> Unit = (x: Unit) => // error // limitation - expect[{*} Cap] { + val op2: Unit ->{fs} Unit = (x: Unit) => // error // limitation + expect[Cap^] { fs.use() io } - val op3: {io} Unit -> Unit = (x: Unit) => // ok - expect[{*} Cap] { + val op3: Unit ->{io} Unit = (x: Unit) => // ok + expect[Cap^] { io.use() io } - val op4: {} Unit -> Unit = (x: Unit) => // ok - expect[{*} Cap](io) + val op4: Unit ->{} Unit = (x: Unit) => // ok + expect[Cap^](io) - val op: {} Unit -> Unit = (x: Unit) => // error - expect[{*} Cap] { + val op: Unit -> Unit = (x: Unit) => // error + expect[Cap^] { io.use() io } op } - val leaked: {} Unit -> Unit = withCap(badOp) + val leaked: Unit -> Unit = withCap(badOp) leaked(()) } diff --git a/tests/neg-custom-args/captures/impurefuns.scala b/tests/neg-custom-args/captures/impurefuns.scala new file mode 100644 index 000000000000..d15d9a466307 --- /dev/null +++ b/tests/neg-custom-args/captures/impurefuns.scala @@ -0,0 +1,3 @@ +def f(x: Object^): Any = + val f: Int =>{x} Int = ??? // error // error // error + f diff --git a/tests/neg-custom-args/captures/inner-classes.scala b/tests/neg-custom-args/captures/inner-classes.scala index cf4073b36f81..181b830e4996 100644 --- a/tests/neg-custom-args/captures/inner-classes.scala +++ b/tests/neg-custom-args/captures/inner-classes.scala @@ -5,21 +5,21 @@ object test: def foo(fs: FileSystem) = trait LazyList[+A]: - this: {fs} LazyList[A] => + this: LazyList[A]^{fs} => def isEmpty: Boolean def head: A - def tail: {this} LazyList[A] + def tail: LazyList[A]^{this} object LazyNil extends LazyList[Nothing]: def isEmpty: Boolean = true def head = ??? def tail = ??? - final class LazyCons[+T](val x: T, val xs: () => {*} LazyList[T]) extends LazyList[T]: // error + final class LazyCons[+T](val x: T, val xs: () => LazyList[T]^) extends LazyList[T]: // error def isEmpty = false def head = x - def tail: {this} LazyList[T] = xs() + def tail: LazyList[T]^{this} = xs() end LazyCons new LazyCons(1, () => LazyNil) diff --git a/tests/neg-custom-args/captures/lazyref.scala b/tests/neg-custom-args/captures/lazyref.scala index 797781ac5360..99aa10d5d2b2 100644 --- a/tests/neg-custom-args/captures/lazyref.scala +++ b/tests/neg-custom-args/captures/lazyref.scala @@ -1,5 +1,5 @@ class CC -type Cap = {*} CC +type Cap = CC^ class LazyRef[T](val elem: () => T): val get: () => T = elem diff --git a/tests/neg-custom-args/captures/override-adapt-box-selftype.scala b/tests/neg-custom-args/captures/override-adapt-box-selftype.scala index a4dc92429192..32efd57b5b5a 100644 --- a/tests/neg-custom-args/captures/override-adapt-box-selftype.scala +++ b/tests/neg-custom-args/captures/override-adapt-box-selftype.scala @@ -4,45 +4,45 @@ class IO class C object Test1 { - abstract class A[X] { this: {} A[X] => + abstract class A[X] { this: A[X] => def foo(x: X): X } - def test(io: {*} IO) = { - class B extends A[{io} C] { // X =:= {io} C // error - override def foo(x: {io} C): {io} C = ??? + def test(io: IO^) = { + class B extends A[C^{io}] { // X =:= {io} C // error + override def foo(x: C^{io}): C^{io} = ??? } } } -def Test2(io: {*} IO, fs: {io} IO, ct: {*} IO) = { - abstract class A[X] { this: {io} A[X] => +def Test2(io: IO^{*}, fs: IO^{io}, ct: IO^) = { + abstract class A[X] { this: A[X]^{io} => def foo(x: X): X } - class B1 extends A[{io} C] { - override def foo(x: {io} C): {io} C = ??? + class B1 extends A[C^{io}] { + override def foo(x: C^{io}): C^{io} = ??? } - class B2 extends A[{ct} C] { // error - override def foo(x: {ct} C): {ct} C = ??? + class B2 extends A[C^{ct}] { // error + override def foo(x: C^{ct}): C^{ct} = ??? } - class B3 extends A[{fs} C] { - override def foo(x: {fs} C): {fs} C = ??? + class B3 extends A[C^{fs}] { + override def foo(x: C^{fs}): C^{fs} = ??? } } -def Test3(io: {*} IO, ct: {*} IO) = { - abstract class A[X] { this: {*} A[X] => +def Test3(io: IO^, ct: IO^) = { + abstract class A[X] { this: A[X]^ => def foo(x: X): X } - class B1 extends A[{io} C] { - override def foo(x: {io} C): {io} C = ??? + class B1 extends A[C^{io}] { + override def foo(x: C^{io}): C^{io} = ??? } - class B2 extends A[{io, ct} C] { - override def foo(x: {io, ct} C): {io, ct} C = ??? + class B2 extends A[C^{io, ct}] { + override def foo(x: C^{io, ct}): C^{io, ct} = ??? } } diff --git a/tests/neg-custom-args/captures/override-adapt-box.scala b/tests/neg-custom-args/captures/override-adapt-box.scala index 64ba8743bf91..8b6d8e2b8afd 100644 --- a/tests/neg-custom-args/captures/override-adapt-box.scala +++ b/tests/neg-custom-args/captures/override-adapt-box.scala @@ -1,14 +1,14 @@ import language.experimental.captureChecking -abstract class A[X] { this: ({} A[X]) => +abstract class A[X] { this: A[X]^{} => def foo(x: X): X } class IO class C -def test(io: {*} IO) = { - class B extends A[{io} C] { // X =:= {io} C // error - override def foo(x: {io} C): {io} C = ??? +def test(io: IO^{*}) = { + class B extends A[C^{io}] { // X =:= {io} C // error + override def foo(x: C^{io}): C^{io} = ??? } } diff --git a/tests/neg-custom-args/captures/override-boxed.scala b/tests/neg-custom-args/captures/override-boxed.scala index 720b50732f61..1e7afd5e3d09 100644 --- a/tests/neg-custom-args/captures/override-boxed.scala +++ b/tests/neg-custom-args/captures/override-boxed.scala @@ -1,7 +1,8 @@ + class A -def test(x: {*} Any) = +def test(x: Any^{*}) = abstract class Getter: - def get(): {x} A - class PolyGetter[T <: {x} A] extends Getter: + def get(): A^{x} + class PolyGetter[T <: A^{x}] extends Getter: override def get(): T = ??? // error diff --git a/tests/neg-custom-args/captures/stack-alloc.scala b/tests/neg-custom-args/captures/stack-alloc.scala index b646c0736f2c..027f2b3e005c 100644 --- a/tests/neg-custom-args/captures/stack-alloc.scala +++ b/tests/neg-custom-args/captures/stack-alloc.scala @@ -5,7 +5,7 @@ class Pooled val stack = mutable.ArrayBuffer[Pooled]() var nextFree = 0 -def withFreshPooled[T](op: ({*} Pooled) => T): T = +def withFreshPooled[T](op: Pooled^ => T): T = if nextFree >= stack.size then stack.append(new Pooled) val pooled = stack(nextFree) nextFree = nextFree + 1 diff --git a/tests/neg-custom-args/captures/try3.scala b/tests/neg-custom-args/captures/try3.scala index 8c5bc18bf3be..5a569aa5fd8d 100644 --- a/tests/neg-custom-args/captures/try3.scala +++ b/tests/neg-custom-args/captures/try3.scala @@ -1,8 +1,8 @@ import java.io.IOException class CT[E] -type CanThrow[E] = {*} CT[E] -type Top = {*} Any +type CanThrow[E] = CT[E]^ +type Top = Any^ def handle[E <: Exception, T <: Top](op: CanThrow[E] ?=> T)(handler: E => T): T = val x: CanThrow[E] = ??? diff --git a/tests/neg-custom-args/captures/unbox.scala b/tests/neg-custom-args/captures/unbox.scala index c615cf1d9176..18a86a1237ff 100644 --- a/tests/neg-custom-args/captures/unbox.scala +++ b/tests/neg-custom-args/captures/unbox.scala @@ -1,4 +1,4 @@ -type Proc = {*} () => Unit +type Proc = () => Unit val xs: List[Proc] = ??? diff --git a/tests/pending/neg/cc-depfun.scala b/tests/pending/neg/cc-depfun.scala new file mode 100644 index 000000000000..4d600872d208 --- /dev/null +++ b/tests/pending/neg/cc-depfun.scala @@ -0,0 +1,14 @@ +import language.experimental.captureChecking + +// compare with neg-custom-args/captures/depfun.scala, which produces errors +// but the errors go away if ->{} gets replaced by ->. + +trait Cap { def use(): Unit } + +def main() = { + val f: (io: Cap^) -> () -> Unit = + io => () => io.use() // error + + val g: (Cap^) -> () -> Unit = + io => () => io.use() // error +} diff --git a/tests/pos-custom-args/captures/lazylists-mono.scala b/tests/pos-custom-args/captures/lazylists-mono.scala index d0f88d0de6a3..c91bedd8f1cf 100644 --- a/tests/pos-custom-args/captures/lazylists-mono.scala +++ b/tests/pos-custom-args/captures/lazylists-mono.scala @@ -6,12 +6,12 @@ type Cap = CC^ def test(E: Cap) = trait LazyList[+A]: - protected def contents: () ->{E} (A, {E} LazyList[A]) + protected def contents: () ->{E} (A, LazyList[A]^{E}) def isEmpty: Boolean def head: A = contents()._1 - def tail: {E} LazyList[A] = contents()._2 + def tail: LazyList[A]^{E} = contents()._2 - class LazyCons[+A](override val contents: () ->{E} (A, {E} LazyList[A])) + class LazyCons[+A](override val contents: () ->{E} (A, LazyList[A]^{E})) extends LazyList[A]: def isEmpty: Boolean = false diff --git a/tests/pos-custom-args/captures/lists.scala b/tests/pos-custom-args/captures/lists.scala index 4e76ed3d5e1d..56473e68d49f 100644 --- a/tests/pos-custom-args/captures/lists.scala +++ b/tests/pos-custom-args/captures/lists.scala @@ -73,18 +73,18 @@ def test(c: Cap, d: Cap, e: Cap) = val b3 = zs.map(eff[Cap ->{d, y} Unit]) val b3c: LIST[Cap ->{d, y} Unit]^{e} = b3 val b4 = zs.map(eff) - val b4c: LIST[{d, c} Cap -> Unit]^{e} = b4 - val b5 = map[{d, y} Cap -> Unit, {d, y} Cap -> Unit](eff)(zs) - val b5c: LIST[{d, c} Cap -> Unit]^{e} = b5 - val b6 = m1[{d, y} Cap -> Unit, {d, y} Cap -> Unit](eff)(zs) - val b6c: LIST[{d, c} Cap -> Unit]^{e} = b6 + val b4c: LIST[Cap ->{d, c} Unit]^{e} = b4 + val b5 = map[Cap ->{d, y} Unit, Cap ->{d, y} Unit](eff)(zs) + val b5c: LIST[Cap ->{d, c} Unit]^{e} = b5 + val b6 = m1[Cap ->{d, y} Unit, Cap ->{d, y} Unit](eff)(zs) + val b6c: LIST[Cap ->{d, c} Unit]^{e} = b6 - val c0 = eff2[{d, y} Cap -> Unit] - val c0c: ({d, y} Cap -> Unit) ->{e, d, y} Cap ->{d, y} Unit = c0 + val c0 = eff2[Cap ->{d, y} Unit] + val c0c: (Cap ->{d, y} Unit) ->{e, d, y} Cap ->{d, y} Unit = c0 val c1 = zs.map[Cap ->{d, y} Unit](a0) - val c1c: LIST[{d, y} Cap -> Unit]^{e} = c1 + val c1c: LIST[Cap ->{d, y} Unit]^{e} = c1 val c2 = zs.map[Cap ->{d, y} Unit](eff2[Cap ->{d, y} Unit]) - val c2c: LIST[{d, y} Cap -> Unit]^{e} = c2 + val c2c: LIST[Cap ->{d, y} Unit]^{e} = c2 val c3 = zs.map(eff2[Cap ->{d, y} Unit]) val c3c: LIST[Cap ->{d, y} Unit]^{e} = c3 diff --git a/tests/pos-custom-args/captures/trickyTrailingUpArrow.scala b/tests/pos-custom-args/captures/trickyTrailingUpArrow.scala index e9d1f50848c0..71b663de5354 100644 --- a/tests/pos-custom-args/captures/trickyTrailingUpArrow.scala +++ b/tests/pos-custom-args/captures/trickyTrailingUpArrow.scala @@ -4,3 +4,6 @@ object Test: x += 1 inline def ctx(using c: String) = c + + val y: String^ -> Unit = ??? + val z: String^ ?-> Unit = ??? diff --git a/tests/pos-with-compiler-cc/dotc/transform/TreeMapWithStages.scala b/tests/pos-with-compiler-cc/dotc/transform/TreeMapWithStages.scala index c721c838d316..9dd1000af954 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/TreeMapWithStages.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/TreeMapWithStages.scala @@ -18,7 +18,7 @@ import scala.annotation.constructorOnly * and `l == -1` is code inside a top level splice (in an inline method). * @param levels a stacked map from symbols to the levels in which they were defined */ -abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMapWithImplicits { this: {} TreeMapWithStages => +abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMapWithImplicits { this: TreeMapWithStages^{} => import tpd._ import TreeMapWithStages._ diff --git a/tests/pos/boxmap-paper.scala b/tests/pos/boxmap-paper.scala index eb6e5f48d81c..ea4c31f099c4 100644 --- a/tests/pos/boxmap-paper.scala +++ b/tests/pos/boxmap-paper.scala @@ -13,25 +13,25 @@ def map[A, B](c: Cell[A])(f: A => B): Cell[B] def pureMap[A, B](c: Cell[A])(f: A -> B): Cell[B] = c[Cell[B]]((x: A) => cell(f(x))) -def lazyMap[A, B](c: Cell[A])(f: A => B): {f} () -> Cell[B] +def lazyMap[A, B](c: Cell[A])(f: A => B): () ->{f} Cell[B] = () => c[Cell[B]]((x: A) => cell(f(x))) trait IO: def print(s: String): Unit -def test(io: {*} IO) = +def test(io: IO^{*}) = - val loggedOne: {io} () -> Int = () => { io.print("1"); 1 } + val loggedOne: () ->{io} Int = () => { io.print("1"); 1 } - val c: Cell[{io} () -> Int] - = cell[{io} () -> Int](loggedOne) + val c: Cell[() ->{io} Int] + = cell[() ->{io} Int](loggedOne) - val g = (f: {io} () -> Int) => + val g = (f: () ->{io} Int) => val x = f(); io.print(" + ") val y = f(); io.print(s" = ${x + y}") - val r = lazyMap[{io} () -> Int, Unit](c)(f => g(f)) - val r2 = lazyMap[{io} () -> Int, Unit](c)(g) + val r = lazyMap[() ->{io} Int, Unit](c)(f => g(f)) + val r2 = lazyMap[() ->{io} Int, Unit](c)(g) val r3 = lazyMap(c)(g) val _ = r() val _ = r2() From ab2ff3baa2f3bcc24e83cf50ce56cd9f0f4c927d Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 1 May 2023 12:09:51 +0200 Subject: [PATCH 528/657] Change encoding of capturing ByNameTypeTrees --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 16 +++++++++------- .../src/dotty/tools/dotc/parsing/Parsers.scala | 7 ++++--- .../tools/dotc/printing/RefinedPrinter.scala | 13 +++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 5b409b6fec9a..34823a3ba0c8 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -473,13 +473,15 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] */ object ImpureByNameTypeTree: - def apply(tp: ByNameTypeTree)(using Context): untpd.CapturingTypeTree = - untpd.CapturingTypeTree( - untpd.captureRoot.withSpan(tp.span.startPos) :: Nil, tp) - - def unapply(tp: Tree)(using Context): Option[ByNameTypeTree] = tp match - case untpd.CapturingTypeTree(id @ Select(_, nme.CAPTURE_ROOT) :: Nil, bntp: ByNameTypeTree) - if id.span == bntp.span.startPos => Some(bntp) + def apply(tp: Tree)(using Context): untpd.ByNameTypeTree = + untpd.ByNameTypeTree( + untpd.CapturesAndResult( + untpd.captureRoot.withSpan(tp.span.startPos) :: Nil, tp)) + + def unapply(tp: Tree)(using Context): Option[Tree] = tp match + case untpd.ByNameTypeTree( + untpd.CapturesAndResult(id @ Select(_, nme.CAPTURE_ROOT) :: Nil, result)) + if id.span == result.span.startPos => Some(result) case _ => None end ImpureByNameTypeTree } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 5c306302361b..67816b28a722 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1936,9 +1936,10 @@ object Parsers { def paramTypeOf(core: () => Tree): Tree = if in.token == ARROW || isPureArrow(nme.PUREARROW) then val isImpure = in.token == ARROW - val tp = atSpan(in.skipToken()): - ByNameTypeTree(if isImpure then core() else capturesAndResult(core)) - if isImpure && Feature.pureFunsEnabled then ImpureByNameTypeTree(tp) else tp + atSpan(in.skipToken()): + val tp = if isImpure then core() else capturesAndResult(core) + if isImpure && Feature.pureFunsEnabled then ImpureByNameTypeTree(tp) + else ByNameTypeTree(tp) else core() diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 2e2ef69ace3f..eef230496bfc 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -566,9 +566,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { toText(sel) ~ keywordStr(" match ") ~ blockText(cases) ~ (" <: " ~ toText(bound) provided !bound.isEmpty) } + case ImpureByNameTypeTree(tpt) => + "=> " ~ toTextLocal(tpt) case ByNameTypeTree(tpt) => - (if Feature.pureFunsEnabled then "-> " else "=> ") - ~ toTextLocal(tpt) + (if Feature.pureFunsEnabled then "-> " else "=> ") ~ toTextLocal(tpt) case TypeBoundsTree(lo, hi, alias) => if (lo eq hi) && alias.isEmpty then optText(lo)(" = " ~ _) else optText(lo)(" >: " ~ _) ~ optText(hi)(" <: " ~ _) ~ optText(alias)(" = " ~ _) @@ -742,12 +743,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case CapturesAndResult(refs, parent) => changePrec(GlobalPrec)("^{" ~ Text(refs.map(toText), ", ") ~ "} " ~ toText(parent)) case CapturingTypeTree(refs, parent) => - parent match - case ImpureByNameTypeTree(bntpt) => - "=> " ~ toTextLocal(bntpt) - case _ => - toText(parent) ~ "^" - ~ changePrec(GlobalPrec)("{" ~ Text(refs.map(toText), ", ") ~ "}") + toText(parent) ~ "^" + ~ changePrec(GlobalPrec)("{" ~ Text(refs.map(toText), ", ") ~ "}") case _ => tree.fallbackToText(this) } From dbe0ec98c1fcef1345d4eb213889664c1b77b9f7 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 1 May 2023 12:13:27 +0200 Subject: [PATCH 529/657] Drop CapturingTypeTree --- compiler/src/dotty/tools/dotc/ast/Desugar.scala | 10 ---------- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 3 --- compiler/src/dotty/tools/dotc/ast/untpd.scala | 11 ----------- .../dotty/tools/dotc/printing/RefinedPrinter.scala | 3 --- 4 files changed, 27 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index e0cba60950fa..e658a3e9a142 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1824,14 +1824,6 @@ object desugar { flatTree(pats1 map (makePatDef(tree, mods, _, rhs))) case ext: ExtMethods => Block(List(ext), Literal(Constant(())).withSpan(ext.span)) - case CapturingTypeTree(refs, parent) => - // convert `{refs} T` to `T @retains refs` - // `{refs}-> T` to `-> (T @retainsByName refs)` - parent match - case ByNameTypeTree(restpt) => - cpy.ByNameTypeTree(parent)(makeRetaining(restpt, refs, tpnme.retainsByName)) - case _ => - makeRetaining(parent, refs, tpnme.retains) case f: FunctionWithMods if f.hasErasedParams => makeFunctionWithValDefs(f, pt) } desugared.withSpan(tree.span) @@ -1991,8 +1983,6 @@ object desugar { case _ => traverseChildren(tree) } }.traverse(body) - case CapturingTypeTree(refs, parent) => - collect(parent) case _ => } collect(tree) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 34823a3ba0c8..a2e3277c9f81 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -223,9 +223,6 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] => /** Strip `=> T` to `T` and (under pureFunctions) `{refs}-> T` to `T` */ 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 */ diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 910b0f84758a..231454921a36 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -147,9 +147,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case Floating } - /** {x1, ..., xN} T (only relevant under captureChecking) */ - case class CapturingTypeTree(refs: List[Tree], parent: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree - /** {x1, ..., xN} T (only relevant under captureChecking) */ case class CapturesAndResult(refs: List[Tree], parent: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree @@ -668,10 +665,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case tree: CapturesAndResult if (refs eq tree.refs) && (parent eq tree.parent) => tree case _ => finalize(tree, untpd.CapturesAndResult(refs, parent)) - 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)) @@ -735,8 +728,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { cpy.MacroTree(tree)(transform(expr)) case CapturesAndResult(refs, parent) => cpy.CapturesAndResult(tree)(transform(refs), transform(parent)) - case CapturingTypeTree(refs, parent) => - cpy.CapturingTypeTree(tree)(transform(refs), transform(parent)) case _ => super.transformMoreCases(tree) } @@ -796,8 +787,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(x, expr) case CapturesAndResult(refs, parent) => this(this(x, refs), parent) - case CapturingTypeTree(refs, parent) => - this(this(x, refs), parent) case _ => super.foldMoreCases(x, tree) } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index eef230496bfc..e6fe55ccb2fc 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -742,9 +742,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { prefix ~~ idx.toString ~~ "|" ~~ tptText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix case CapturesAndResult(refs, parent) => changePrec(GlobalPrec)("^{" ~ Text(refs.map(toText), ", ") ~ "} " ~ toText(parent)) - case CapturingTypeTree(refs, parent) => - toText(parent) ~ "^" - ~ changePrec(GlobalPrec)("{" ~ Text(refs.map(toText), ", ") ~ "}") case _ => tree.fallbackToText(this) } From c24f875ff3ee39d267bb08d08a66e81a64313592 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 1 May 2023 14:43:37 +0200 Subject: [PATCH 530/657] Change root capability to `cap` --- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 8 +++---- .../dotty/tools/dotc/cc/CheckCaptures.scala | 18 +++++++-------- .../dotty/tools/dotc/core/Definitions.scala | 2 +- .../src/dotty/tools/dotc/core/StdNames.scala | 3 +-- .../src/dotty/tools/dotc/core/Types.scala | 2 +- .../dotty/tools/dotc/parsing/Parsers.scala | 3 +-- .../tools/dotc/printing/PlainPrinter.scala | 2 +- library/src/scala/caps.scala | 3 ++- tests/neg-custom-args/boxmap.scala | 6 ++--- tests/neg-custom-args/capt-wf.scala | 6 ++--- .../captures/box-adapt-cases.scala | 6 ++--- .../captures/box-adapt-cov.scala | 4 ++-- .../captures/box-adapt-cs.scala | 6 ++--- .../captures/box-adapt-depfun.scala | 2 +- .../captures/box-adapt-typefun.scala | 4 ++-- .../captures/capt-depfun.scala | 2 +- .../captures/capt-depfun2.scala | 2 +- .../neg-custom-args/captures/capt-test.scala | 4 ++-- tests/neg-custom-args/captures/capt1.scala | 12 +++++----- tests/neg-custom-args/captures/capt3.scala | 2 +- tests/neg-custom-args/captures/cc-this2.check | 2 +- tests/neg-custom-args/captures/cc1.scala | 2 +- .../captures/exception-definitions.check | 2 +- .../captures/heal-tparam-cs.scala | 8 +++---- tests/neg-custom-args/captures/i15049.scala | 4 ++-- tests/neg-custom-args/captures/i15749.scala | 6 ++--- tests/neg-custom-args/captures/i15749a.scala | 16 +++++++------- .../captures/i15923-cases.scala | 6 ++--- tests/neg-custom-args/captures/i16114.scala | 2 +- tests/neg-custom-args/captures/io.scala | 8 +++---- .../captures/lazylists-exceptions.check | 2 +- .../neg-custom-args/captures/lazylists1.scala | 6 ++--- .../override-adapt-box-selftype.scala | 2 +- .../captures/override-adapt-box.scala | 2 +- .../captures/override-boxed.scala | 2 +- tests/neg-custom-args/captures/real-try.check | 6 ++--- tests/neg-custom-args/captures/real-try.scala | 2 +- tests/neg-custom-args/captures/try.check | 2 +- tests/neg-custom-args/captures/try.scala | 4 ++-- .../captures/usingLogFile.check | 12 +++++----- tests/neg-custom-args/captures/vars.check | 6 ++--- tests/pos-custom-args/captures/byname.scala | 2 +- .../captures/caps-universal.scala | 2 +- .../captures/capt-depfun.scala | 4 ++-- .../captures/capt-depfun2.scala | 2 +- tests/pos-custom-args/captures/capt0.scala | 2 +- tests/pos-custom-args/captures/capt1.scala | 2 +- tests/pos-custom-args/captures/capt2.scala | 2 +- .../pos-custom-args/captures/cc-expand.scala | 2 +- .../pos-custom-args/captures/overrides.scala | 2 +- tests/pos-custom-args/captures/try.scala | 2 +- .../pos-with-compiler-cc/dotc/ast/Trees.scala | 4 ++-- .../pos-with-compiler-cc/dotc/ast/untpd.scala | 2 +- .../dotc/cc/CaptureAnnotation.scala | 2 +- .../dotc/core/Annotations.scala | 2 +- .../dotc/core/Contexts.scala | 2 +- .../dotc/core/Decorators.scala | 8 +++---- .../dotc/core/GadtConstraint.scala | 4 ++-- .../dotc/core/Names.scala | 10 ++++----- .../dotc/core/Substituters.scala | 18 +++++++-------- .../dotc/core/TypeOps.scala | 2 +- .../dotc/core/Types.scala | 14 ++++++------ .../dotc/typer/ProtoTypes.scala | 22 +++++++++---------- tests/pos/boxmap-paper.scala | 2 +- .../captures/colltest5/Test_2.scala | 4 ++-- 65 files changed, 158 insertions(+), 159 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 2b9fe9d3d923..509fecd4d3b4 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -70,7 +70,7 @@ sealed abstract class CaptureSet extends Showable: assert(!isConst) asInstanceOf[Var] - /** Does this capture set contain the root reference `*` as element? */ + /** Does this capture set contain the root reference `cap` as element? */ final def isUniversal(using Context) = elems.exists { case ref: TermRef => ref.symbol == defn.captureRoot @@ -133,7 +133,7 @@ sealed abstract class CaptureSet extends Showable: * for `x` in a state where we assume all supersets of `x` have just the elements * known at this point. On the other hand if x's capture set has no known elements, * a set `cs` might account for `x` only if it subsumes `x` or it contains the - * root capability `*`. + * root capability `cap`. */ def mightAccountFor(x: CaptureRef)(using Context): Boolean = reporting.trace(i"$this mightAccountFor $x, ${x.captureSetOfInfo}?", show = true) { @@ -270,7 +270,7 @@ sealed abstract class CaptureSet extends Showable: def substParams(tl: BindingType, to: List[Type])(using Context) = map(Substituters.SubstParamsMap(tl, to)) - /** Invoke handler if this set has (or later aquires) the root capability `*` */ + /** Invoke handler if this set has (or later aquires) the root capability `cap` */ def disallowRootCapability(handler: () => Context ?=> Unit)(using Context): this.type = if isUniversal then handler() this @@ -372,7 +372,7 @@ object CaptureSet: def isConst = isSolved def isAlwaysEmpty = false - /** A handler to be invoked if the root reference `*` is added to this set */ + /** A handler to be invoked if the root reference `cap` is added to this set */ var rootAddedHandler: () => Context ?=> Unit = () => () var description: String = "" diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index f9401a0c509f..e77c72c422ca 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -433,7 +433,7 @@ class CheckCaptures extends Recheck, SymTransformer: case defn.FunctionOf(ptformals, _, _) if ptformals.nonEmpty && ptformals.forall(_.captureSet.isAlwaysEmpty) => // Redo setup of the anonymous function so that formal parameters don't - // get capture sets. This is important to avoid false widenings to `*` + // get capture sets. This is important to avoid false widenings to `cap` // when taking the base type of the actual closures's dependent function // type so that it conforms to the expected non-dependent function type. // See withLogFile.scala for a test case. @@ -582,7 +582,7 @@ class CheckCaptures extends Recheck, SymTransformer: refs.disallowRootCapability { () => val kind = if tree.isInstanceOf[ValDef] then "mutable variable" else "expression" report.error( - em"""The $kind's type $wtp is not allowed to capture the root capability `*`. + em"""The $kind's type $wtp is not allowed to capture the root capability `cap`. |This usually means that a capability persists longer than its allowed lifetime.""", tree.srcPos) } @@ -768,9 +768,9 @@ class CheckCaptures extends Recheck, SymTransformer: styp1.capturing(if alwaysConst then CaptureSet(cs1.elems) else cs1).forceBoxStatus(resultBoxed) if needsAdaptation then - val criticalSet = // the set which is not allowed to have `*` - if covariant then cs1 // can't box with `*` - else expected.captureSet // can't unbox with `*` + val criticalSet = // the set which is not allowed to have `cap` + if covariant then cs1 // can't box with `cap` + else expected.captureSet // can't unbox with `cap` if criticalSet.isUniversal && expected.isValueType then // We can't box/unbox the universal capability. Leave `actual` as it is // so we get an error in checkConforms. This tends to give better error @@ -779,11 +779,11 @@ class CheckCaptures extends Recheck, SymTransformer: println(i"cannot box/unbox $actual vs $expected") actual else - // Disallow future addition of `*` to `criticalSet`. + // Disallow future addition of `cap` to `criticalSet`. criticalSet.disallowRootCapability { () => report.error( em"""$actual cannot be box-converted to $expected - |since one of their capture sets contains the root capability `*`""", + |since one of their capture sets contains the root capability `cap`""", pos) } if !insertBox then // unboxing @@ -922,8 +922,8 @@ class CheckCaptures extends Recheck, SymTransformer: * usingLogFile[box ?1 () -> Unit] { (f: {*} File) => () => { f.write(0) } } * * We may propagate `f` into ?1, making ?1 ill-formed. - * This also causes soundness issues, since `f` in ?1 should be widened to `*`, - * giving rise to an error that `*` cannot be included in a boxed capture set. + * This also causes soundness issues, since `f` in ?1 should be widened to `cap`, + * giving rise to an error that `cap` cannot be included in a boxed capture set. * * To solve this, we still allow ?1 to capture parameter refs like `f`, but * compensate this by pushing the widened capture set of `f` into ?1. diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index b7211b3ce5e3..82b1a6354321 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -968,7 +968,7 @@ class Definitions { @tu lazy val BreakClass: Symbol = requiredClass("scala.util.boundary.Break") @tu lazy val CapsModule: Symbol = requiredModule("scala.caps") - @tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("*") + @tu lazy val captureRoot: TermSymbol = CapsModule.requiredValue("cap") @tu lazy val CapsUnsafeModule: Symbol = requiredModule("scala.caps.unsafe") @tu lazy val Caps_unsafeBox: Symbol = CapsUnsafeModule.requiredMethod("unsafeBox") @tu lazy val Caps_unsafeUnbox: Symbol = CapsUnsafeModule.requiredMethod("unsafeUnbox") diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index e5ca0710447d..5c718ef26289 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -286,8 +286,7 @@ object StdNames { // ----- Term names ----------------------------------------- // Compiler-internal - val CAPTURE_ROOT: N = "*" - val CAPTURE_ROOT_ALT: N = "any" + val CAPTURE_ROOT: N = "cap" val CONSTRUCTOR: N = "" val STATIC_CONSTRUCTOR: N = "" val EVT2U: N = "evt2u$" diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 165037d65979..0a374cadd2ec 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2102,7 +2102,7 @@ object Types { */ final def isTracked(using Context): Boolean = canBeTracked && !captureSetOfInfo.isAlwaysEmpty - /** Is this reference the root capability `*` ? */ + /** Is this reference the root capability `cap` ? */ def isRootCapability(using Context): Boolean = false /** Normalize reference so that it can be compared with `eq` for equality */ diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 67816b28a722..9376b995b011 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1444,7 +1444,6 @@ object Parsers { if in.token == THIS then simpleRef() else termIdent() match case Ident(nme.CAPTURE_ROOT) => captureRoot - case Ident(nme.CAPTURE_ROOT_ALT) => captureRoot case id => id /** CaptureSet ::= `{` CaptureRef {`,` CaptureRef} `}` -- under captureChecking @@ -1681,7 +1680,7 @@ object Parsers { def refinedType() = refinedTypeRest(withType()) - /** Disambiguation: a `^` is treated as a postfix operator meaning `^{any}` + /** Disambiguation: a `^` is treated as a postfix operator meaning `^{cap}` * if followed by `{`, `->`, or `?->`, * or followed by a new line (significant or not), * or followed by a token that cannot start an infix type. diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 91e152c57048..fa58c467bfa5 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -386,7 +386,7 @@ class PlainPrinter(_ctx: Context) extends Printer { def toTextCaptureRef(tp: Type): Text = homogenize(tp) match - case tp: TermRef if tp.symbol == defn.captureRoot => Str("*") + case tp: TermRef if tp.symbol == defn.captureRoot => Str("cap") case tp: SingletonType => toTextRef(tp) case _ => toText(tp) diff --git a/library/src/scala/caps.scala b/library/src/scala/caps.scala index fb1721f98b35..921b370ff5b0 100644 --- a/library/src/scala/caps.scala +++ b/library/src/scala/caps.scala @@ -5,7 +5,8 @@ import annotation.experimental @experimental object caps: /** The universal capture reference */ - val `*`: Any = () + val `*`: Any = () // OLD + val cap: Any = () // NEW object unsafe: diff --git a/tests/neg-custom-args/boxmap.scala b/tests/neg-custom-args/boxmap.scala index e66b0a8ec808..cdc8253acf2a 100644 --- a/tests/neg-custom-args/boxmap.scala +++ b/tests/neg-custom-args/boxmap.scala @@ -1,5 +1,5 @@ import annotation.retains -type Top = Any @retains(caps.*) +type Top = Any @retains(caps.cap) type Box[+T <: Top] = ([K <: Top] -> (T => K) -> K) @@ -16,6 +16,6 @@ def test[A <: Top, B <: Top] = def lazymap[A <: Top, B <: Top](b: Box[A])(f: A => B) = () => b[Box[B]]((x: A) => box(f(x))) val x0: (b: Box[A]) -> (f: A => B) -> (() -> Box[B]) = lazymap[A, B] // error - val x: (b: Box[A]) -> (f: A => B) -> {b, f} (() -> Box[B]) = lazymap[A, B] // works - val y: (b: Box[A]) -> (f: A => B) -> {*} (() -> Box[B]) = lazymap[A, B] // works + val x: (b: Box[A]) -> (f: A => B) ->{b, f} (() -> Box[B]) = lazymap[A, B] // works + val y: (b: Box[A]) -> (f: A => B) ->{cap} (() -> Box[B]) = lazymap[A, B] // works () diff --git a/tests/neg-custom-args/capt-wf.scala b/tests/neg-custom-args/capt-wf.scala index fe883af0ba8e..67e1bc9906fe 100644 --- a/tests/neg-custom-args/capt-wf.scala +++ b/tests/neg-custom-args/capt-wf.scala @@ -14,9 +14,9 @@ def test(c: Cap, other: String): Unit = val x5: C^{c, c} = ??? // error: redundant // val x6: C^{c}^{c} = ??? // would be syntax error val x7: Cap^{c} = ??? // error: redundant - // val x8: C^{c}^{*} = ??? // would be syntax error - val x9: C^{c, *} = ??? // error: redundant - val x10: C^{*, c} = ??? // error: redundant + // val x8: C^{c}^{cap} = ??? // would be syntax error + val x9: C^{c, cap} = ??? // error: redundant + val x10: C^{cap, c} = ??? // error: redundant def even(n: Int): Boolean = if n == 0 then true else odd(n - 1) def odd(n: Int): Boolean = if n == 1 then true else even(n - 1) diff --git a/tests/neg-custom-args/captures/box-adapt-cases.scala b/tests/neg-custom-args/captures/box-adapt-cases.scala index faaf256e470b..351fab2bd8a5 100644 --- a/tests/neg-custom-args/captures/box-adapt-cases.scala +++ b/tests/neg-custom-args/captures/box-adapt-cases.scala @@ -7,21 +7,21 @@ def test1(): Unit = { x(cap => cap.use()) // error } -def test2(io: Cap^{*}): Unit = { +def test2(io: Cap^{cap}): Unit = { type Id[X] = [T] -> (op: X -> T) -> T val x: Id[Cap^{io}] = ??? x(cap => cap.use()) // error } -def test3(io: Cap^{*}): Unit = { +def test3(io: Cap^{cap}): Unit = { type Id[X] = [T] -> (op: X ->{io} T) -> T val x: Id[Cap^{io}] = ??? x(cap => cap.use()) // ok } -def test4(io: Cap^{*}, fs: Cap^{*}): Unit = { +def test4(io: Cap^{cap}, fs: Cap^{cap}): Unit = { type Id[X] = [T] -> (op: X ->{io} T) -> T val x: Id[Cap^{io, fs}] = ??? diff --git a/tests/neg-custom-args/captures/box-adapt-cov.scala b/tests/neg-custom-args/captures/box-adapt-cov.scala index ebdb68cf0ca4..96901e81458d 100644 --- a/tests/neg-custom-args/captures/box-adapt-cov.scala +++ b/tests/neg-custom-args/captures/box-adapt-cov.scala @@ -1,12 +1,12 @@ trait Cap -def test1(io: Cap^{*}) = { +def test1(io: Cap^{cap}) = { type Op[X] = [T] -> Unit -> X val f: Op[Cap^{io}] = ??? val x: [T] -> Unit -> Cap^{io} = f // error } -def test2(io: Cap^{*}) = { +def test2(io: Cap^{cap}) = { type Op[X] = [T] -> Unit -> X^{io} val f: Op[Cap^{io}] = ??? val x: Unit -> Cap^{io} = f[Unit] // error diff --git a/tests/neg-custom-args/captures/box-adapt-cs.scala b/tests/neg-custom-args/captures/box-adapt-cs.scala index a5344d52c30f..a39ed0200151 100644 --- a/tests/neg-custom-args/captures/box-adapt-cs.scala +++ b/tests/neg-custom-args/captures/box-adapt-cs.scala @@ -1,14 +1,14 @@ trait Cap { def use(): Int } -def test1(io: Cap^{*}): Unit = { +def test1(io: Cap^{cap}): Unit = { type Id[X] = [T] -> (op: X ->{io} T) -> T val x: Id[Cap^{io}] = ??? - val f: (Cap^{*}) -> Unit = ??? + val f: (Cap^{cap}) -> Unit = ??? x(f) // ok } -def test2(io: Cap^{*}): Unit = { +def test2(io: Cap^{cap}): Unit = { type Id[X] = [T] -> (op: X => T) -> T val x: Id[Cap^] = ??? diff --git a/tests/neg-custom-args/captures/box-adapt-depfun.scala b/tests/neg-custom-args/captures/box-adapt-depfun.scala index 357cfb4a8d9d..9416ffa040ab 100644 --- a/tests/neg-custom-args/captures/box-adapt-depfun.scala +++ b/tests/neg-custom-args/captures/box-adapt-depfun.scala @@ -15,7 +15,7 @@ def test2(io: Cap^): Unit = { // should work when the expected type is a dependent function } -def test3(io: Cap^{*}): Unit = { +def test3(io: Cap^{cap}): Unit = { type Id[X] = [T] -> (op: (x: X) ->{} T) -> T val x: Id[Cap^{io}] = ??? diff --git a/tests/neg-custom-args/captures/box-adapt-typefun.scala b/tests/neg-custom-args/captures/box-adapt-typefun.scala index bbc953f74e9e..65a06cd68ed9 100644 --- a/tests/neg-custom-args/captures/box-adapt-typefun.scala +++ b/tests/neg-custom-args/captures/box-adapt-typefun.scala @@ -1,12 +1,12 @@ trait Cap { def use(): Int } -def test1(io: Cap^{*}): Unit = { +def test1(io: Cap^{cap}): Unit = { type Op[X] = [T] -> X -> Unit val f: [T] -> (Cap^{io}) -> Unit = ??? val op: Op[Cap^{io}] = f // error } -def test2(io: Cap^{*}): Unit = { +def test2(io: Cap^{cap}): Unit = { type Lazy[X] = [T] -> Unit -> X val f: Lazy[Cap^{io}] = ??? val test: [T] -> Unit -> (Cap^{io}) = f // error diff --git a/tests/neg-custom-args/captures/capt-depfun.scala b/tests/neg-custom-args/captures/capt-depfun.scala index 95eed2c29659..20226b239198 100644 --- a/tests/neg-custom-args/captures/capt-depfun.scala +++ b/tests/neg-custom-args/captures/capt-depfun.scala @@ -1,6 +1,6 @@ import annotation.retains class C -type Cap = C @retains(caps.*) +type Cap = C @retains(caps.cap) class Str def f(y: Cap, z: Cap) = diff --git a/tests/neg-custom-args/captures/capt-depfun2.scala b/tests/neg-custom-args/captures/capt-depfun2.scala index 52dd74aabf9f..cb4bc5f9634d 100644 --- a/tests/neg-custom-args/captures/capt-depfun2.scala +++ b/tests/neg-custom-args/captures/capt-depfun2.scala @@ -1,6 +1,6 @@ import annotation.retains class C -type Cap = C @retains(caps.*) +type Cap = C @retains(caps.cap) class Str def f(y: Cap, z: Cap) = diff --git a/tests/neg-custom-args/captures/capt-test.scala b/tests/neg-custom-args/captures/capt-test.scala index 1799fc5073ca..13368aeea190 100644 --- a/tests/neg-custom-args/captures/capt-test.scala +++ b/tests/neg-custom-args/captures/capt-test.scala @@ -2,8 +2,8 @@ import annotation.retains import language.experimental.erasedDefinitions class CT[E <: Exception] -type CanThrow[E <: Exception] = CT[E] @retains(caps.*) -type Top = Any @retains(caps.*) +type CanThrow[E <: Exception] = CT[E] @retains(caps.cap) +type Top = Any @retains(caps.cap) infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?=> R diff --git a/tests/neg-custom-args/captures/capt1.scala b/tests/neg-custom-args/captures/capt1.scala index 59ba874b02f5..69b4c9984494 100644 --- a/tests/neg-custom-args/captures/capt1.scala +++ b/tests/neg-custom-args/captures/capt1.scala @@ -1,21 +1,21 @@ import annotation.retains class C -def f(x: C @retains(caps.*), y: C): () -> C = +def f(x: C @retains(caps.cap), y: C): () -> C = () => if x == null then y else y // error -def g(x: C @retains(caps.*), y: C): Matchable = +def g(x: C @retains(caps.cap), y: C): Matchable = () => if x == null then y else y // error -def h1(x: C @retains(caps.*), y: C): Any = +def h1(x: C @retains(caps.cap), y: C): Any = def f() = if x == null then y else y () => f() // ok -def h2(x: C @retains(caps.*)): Matchable = +def h2(x: C @retains(caps.cap)): Matchable = def f(y: Int) = if x == null then y else y // error f class A -type Cap = C @retains(caps.*) +type Cap = C @retains(caps.cap) def h3(x: Cap): A = class F(y: Int) extends A: // error @@ -27,7 +27,7 @@ def h4(x: Cap, y: Int): A = def m() = if x == null then y else y def foo() = - val x: C @retains(caps.*) = ??? + val x: C @retains(caps.cap) = ??? def h[X](a: X)(b: X) = a val z2 = h[() -> Cap](() => x) // error (() => C()) // error diff --git a/tests/neg-custom-args/captures/capt3.scala b/tests/neg-custom-args/captures/capt3.scala index 84164d433029..44a7ffdc6c4a 100644 --- a/tests/neg-custom-args/captures/capt3.scala +++ b/tests/neg-custom-args/captures/capt3.scala @@ -1,6 +1,6 @@ import annotation.retains class C -type Cap = C @retains(caps.*) +type Cap = C @retains(caps.cap) def test1() = val x: Cap = C() diff --git a/tests/neg-custom-args/captures/cc-this2.check b/tests/neg-custom-args/captures/cc-this2.check index 9db2f4efcc5a..e0df7c857c85 100644 --- a/tests/neg-custom-args/captures/cc-this2.check +++ b/tests/neg-custom-args/captures/cc-this2.check @@ -2,5 +2,5 @@ -- Error: tests/neg-custom-args/captures/cc-this2/D_2.scala:2:6 -------------------------------------------------------- 2 |class D extends C: // error |^ - |reference (scala.caps.* : Any) is not included in allowed capture set {} of pure base class class C + |reference (scala.caps.cap : Any) is not included in allowed capture set {} of pure base class class C 3 | this: D^ => diff --git a/tests/neg-custom-args/captures/cc1.scala b/tests/neg-custom-args/captures/cc1.scala index 10a9793eabe8..6787b417a3b2 100644 --- a/tests/neg-custom-args/captures/cc1.scala +++ b/tests/neg-custom-args/captures/cc1.scala @@ -1,5 +1,5 @@ import annotation.retains object Test: - def f[A <: Matchable @retains(caps.*)](x: A): Matchable = x // error + def f[A <: Matchable @retains(caps.cap)](x: A): Matchable = x // error diff --git a/tests/neg-custom-args/captures/exception-definitions.check b/tests/neg-custom-args/captures/exception-definitions.check index 23dbe0fb6529..8dca91bc8e43 100644 --- a/tests/neg-custom-args/captures/exception-definitions.check +++ b/tests/neg-custom-args/captures/exception-definitions.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/exception-definitions.scala:2:6 ----------------------------------------------- 2 |class Err extends Exception: // error |^ - |reference (scala.caps.* : Any) is not included in allowed capture set {} of pure base class class Throwable + |reference (scala.caps.cap : Any) is not included in allowed capture set {} of pure base class class Throwable 3 | self: Err^ => -- Error: tests/neg-custom-args/captures/exception-definitions.scala:10:6 ---------------------------------------------- 10 |class Err4(c: Any^) extends AnyVal // error diff --git a/tests/neg-custom-args/captures/heal-tparam-cs.scala b/tests/neg-custom-args/captures/heal-tparam-cs.scala index f4c293e33d01..173de2b1cbb5 100644 --- a/tests/neg-custom-args/captures/heal-tparam-cs.scala +++ b/tests/neg-custom-args/captures/heal-tparam-cs.scala @@ -2,16 +2,16 @@ import language.experimental.captureChecking trait Cap { def use(): Unit } -def localCap[T](op: (cap: Cap^{*}) => T): T = ??? +def localCap[T](op: (cap: Cap^{cap}) => T): T = ??? -def main(io: Cap^{*}, net: Cap^{*}): Unit = { +def main(io: Cap^{cap}, net: Cap^{cap}): Unit = { val test1 = localCap { cap => // error () => { cap.use() } } - val test2: (cap: Cap^{*}) -> () ->{cap} Unit = + val test2: (cap: Cap^{cap}) -> () ->{cap} Unit = localCap { cap => // should work - (cap1: Cap^{*}) => () => { cap1.use() } + (cap1: Cap^{cap}) => () => { cap1.use() } } val test3: (cap: Cap^{io}) -> () ->{io} Unit = diff --git a/tests/neg-custom-args/captures/i15049.scala b/tests/neg-custom-args/captures/i15049.scala index f12bb8028797..bc65db2147f8 100644 --- a/tests/neg-custom-args/captures/i15049.scala +++ b/tests/neg-custom-args/captures/i15049.scala @@ -1,8 +1,8 @@ class Session: def request = "Response" class Foo: - private val session: Session^{*} = new Session - def withSession[T](f: (Session^{*}) => T): T = f(session) + private val session: Session^{cap} = new Session + def withSession[T](f: (Session^{cap}) => T): T = f(session) def Test = val f = new Foo diff --git a/tests/neg-custom-args/captures/i15749.scala b/tests/neg-custom-args/captures/i15749.scala index b5aa233e7401..32947c146021 100644 --- a/tests/neg-custom-args/captures/i15749.scala +++ b/tests/neg-custom-args/captures/i15749.scala @@ -1,9 +1,9 @@ class Unit object unit extends Unit -type Top = Any^{*} +type Top = Any^{cap} -type LazyVal[T] = Unit ->{*} T +type LazyVal[T] = Unit ->{cap} T class Foo[T](val x: T) @@ -11,5 +11,5 @@ class Foo[T](val x: T) type BoxedLazyVal[T] = Foo[LazyVal[T]] def force[A](v: BoxedLazyVal[A]): A = - // Γ ⊢ v.x : □ {*} Unit -> A + // Γ ⊢ v.x : □ {cap} Unit -> A v.x(unit) // error: (unbox v.x)(unit), where (unbox v.x) should be untypable \ No newline at end of file diff --git a/tests/neg-custom-args/captures/i15749a.scala b/tests/neg-custom-args/captures/i15749a.scala index 8a39db98c964..e4d70efa5206 100644 --- a/tests/neg-custom-args/captures/i15749a.scala +++ b/tests/neg-custom-args/captures/i15749a.scala @@ -3,19 +3,19 @@ object unit extends Unit type Top = Any^ -type Wrapper[T] = [X] -> (op: T ->{*} X) -> X +type Wrapper[T] = [X] -> (op: T ->{cap} X) -> X def test = def wrapper[T](x: T): Wrapper[T] = - [X] => (op: T ->{*} X) => op(x) + [X] => (op: T ->{cap} X) => op(x) - def strictMap[A <: Top, B <: Top](mx: Wrapper[A])(f: A ->{*} B): Wrapper[B] = + def strictMap[A <: Top, B <: Top](mx: Wrapper[A])(f: A ->{cap} B): Wrapper[B] = mx((x: A) => wrapper(f(x))) - def force[A](thunk: Unit ->{*} A): A = thunk(unit) + def force[A](thunk: Unit ->{cap} A): A = thunk(unit) - def forceWrapper[A](mx: Wrapper[Unit ->{*} A]): Wrapper[A] = - // Γ ⊢ mx: Wrapper[□ {*} Unit => A] - // `force` should be typed as ∀(□ {*} Unit -> A) A, but it can not - strictMap[Unit ->{*} A, A](mx)(t => force[A](t)) // error + def forceWrapper[A](mx: Wrapper[Unit ->{cap} A]): Wrapper[A] = + // Γ ⊢ mx: Wrapper[□ {cap} Unit => A] + // `force` should be typed as ∀(□ {cap} Unit -> A) A, but it can not + strictMap[Unit ->{cap} A, A](mx)(t => force[A](t)) // error diff --git a/tests/neg-custom-args/captures/i15923-cases.scala b/tests/neg-custom-args/captures/i15923-cases.scala index 2b034009fdc7..9a103c976a8e 100644 --- a/tests/neg-custom-args/captures/i15923-cases.scala +++ b/tests/neg-custom-args/captures/i15923-cases.scala @@ -2,14 +2,14 @@ trait Cap { def use(): Int } type Id[X] = [T] -> (op: X => T) -> T def mkId[X](x: X): Id[X] = [T] => (op: X => T) => op(x) -def foo(x: Id[Cap^{*}]) = { +def foo(x: Id[Cap^{cap}]) = { x(_.use()) // error } -def bar(io: Cap^{*}, x: Id[Cap^{io}]) = { +def bar(io: Cap^{cap}, x: Id[Cap^{io}]) = { x(_.use()) } -def barAlt(a: Cap^{*}, b: Cap^{*}, x: Id[Cap]^{a, b}) = { +def barAlt(a: Cap^{cap}, b: Cap^{cap}, x: Id[Cap]^{a, b}) = { x(_.use()) } diff --git a/tests/neg-custom-args/captures/i16114.scala b/tests/neg-custom-args/captures/i16114.scala index 706196fd7182..d22c7f02d5fb 100644 --- a/tests/neg-custom-args/captures/i16114.scala +++ b/tests/neg-custom-args/captures/i16114.scala @@ -11,7 +11,7 @@ def withCap[T](op: Cap^ => T): T = { } def main(fs: Cap^): Unit = { - def badOp(io: Cap^{*}): Unit ->{} Unit = { + def badOp(io: Cap^{cap}): Unit ->{} Unit = { val op1: Unit ->{io} Unit = (x: Unit) => // error // limitation expect[Cap^] { io.use() diff --git a/tests/neg-custom-args/captures/io.scala b/tests/neg-custom-args/captures/io.scala index ae686d6b154e..f481bf357fc8 100644 --- a/tests/neg-custom-args/captures/io.scala +++ b/tests/neg-custom-args/captures/io.scala @@ -3,17 +3,17 @@ sealed trait IO: def puts(msg: Any): Unit = println(msg) def test1 = - val IO : IO @retains(caps.*) = new IO {} + val IO : IO @retains(caps.cap) = new IO {} def foo = {IO; IO.puts("hello") } val x : () -> Unit = () => foo // error: Found: (() -> Unit) retains IO; Required: () -> Unit def test2 = - val IO : IO @retains(caps.*) = new IO {} - def puts(msg: Any, io: IO @retains(caps.*)) = println(msg) + val IO : IO @retains(caps.cap) = new IO {} + def puts(msg: Any, io: IO @retains(caps.cap)) = println(msg) def foo() = puts("hello", IO) val x : () -> Unit = () => foo() // error: Found: (() -> Unit) retains IO; Required: () -> Unit -type Capability[T] = T @retains(caps.*) +type Capability[T] = T @retains(caps.cap) def test3 = val IO : Capability[IO] = new IO {} diff --git a/tests/neg-custom-args/captures/lazylists-exceptions.check b/tests/neg-custom-args/captures/lazylists-exceptions.check index 4c451b1fea8a..03278f25e8dc 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.check +++ b/tests/neg-custom-args/captures/lazylists-exceptions.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/lazylists-exceptions.scala:36:2 ----------------------------------------------- 36 | try // error | ^ - | The expression's type LazyList[Int]^ is not allowed to capture the root capability `*`. + | The expression's type LazyList[Int]^ is not allowed to capture the root capability `cap`. | This usually means that a capability persists longer than its allowed lifetime. 37 | tabulate(10) { i => 38 | if i > 9 then throw Ex1() diff --git a/tests/neg-custom-args/captures/lazylists1.scala b/tests/neg-custom-args/captures/lazylists1.scala index 88d9e5672b5d..99472c13ebec 100644 --- a/tests/neg-custom-args/captures/lazylists1.scala +++ b/tests/neg-custom-args/captures/lazylists1.scala @@ -1,8 +1,8 @@ class CC -type Cap = CC^{*} +type Cap = CC^{cap} trait LazyList[+A]: - this: LazyList[A]^{*} => + this: LazyList[A]^{cap} => def isEmpty: Boolean def head: A @@ -13,7 +13,7 @@ object LazyNil extends LazyList[Nothing]: def head = ??? def tail = ??? -extension [A](xs: LazyList[A]^{*}) +extension [A](xs: LazyList[A]^{cap}) def map[B](f: A => B): LazyList[B]^{xs, f} = final class Mapped extends LazyList[B]: this: (Mapped^{xs, f}) => diff --git a/tests/neg-custom-args/captures/override-adapt-box-selftype.scala b/tests/neg-custom-args/captures/override-adapt-box-selftype.scala index 32efd57b5b5a..f44add78e246 100644 --- a/tests/neg-custom-args/captures/override-adapt-box-selftype.scala +++ b/tests/neg-custom-args/captures/override-adapt-box-selftype.scala @@ -15,7 +15,7 @@ object Test1 { } } -def Test2(io: IO^{*}, fs: IO^{io}, ct: IO^) = { +def Test2(io: IO^{cap}, fs: IO^{io}, ct: IO^) = { abstract class A[X] { this: A[X]^{io} => def foo(x: X): X } diff --git a/tests/neg-custom-args/captures/override-adapt-box.scala b/tests/neg-custom-args/captures/override-adapt-box.scala index 8b6d8e2b8afd..70023dfbc941 100644 --- a/tests/neg-custom-args/captures/override-adapt-box.scala +++ b/tests/neg-custom-args/captures/override-adapt-box.scala @@ -7,7 +7,7 @@ abstract class A[X] { this: A[X]^{} => class IO class C -def test(io: IO^{*}) = { +def test(io: IO^{cap}) = { class B extends A[C^{io}] { // X =:= {io} C // error override def foo(x: C^{io}): C^{io} = ??? } diff --git a/tests/neg-custom-args/captures/override-boxed.scala b/tests/neg-custom-args/captures/override-boxed.scala index 1e7afd5e3d09..d66d28d15aaa 100644 --- a/tests/neg-custom-args/captures/override-boxed.scala +++ b/tests/neg-custom-args/captures/override-boxed.scala @@ -1,7 +1,7 @@ class A -def test(x: Any^{*}) = +def test(x: Any^{cap}) = abstract class Getter: def get(): A^{x} class PolyGetter[T <: A^{x}] extends Getter: diff --git a/tests/neg-custom-args/captures/real-try.check b/tests/neg-custom-args/captures/real-try.check index c46d1e075075..0b4da5af27b5 100644 --- a/tests/neg-custom-args/captures/real-try.check +++ b/tests/neg-custom-args/captures/real-try.check @@ -1,7 +1,7 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:12:2 ----------------------------------------------------------- 12 | try // error | ^ - | The expression's type () => Unit is not allowed to capture the root capability `*`. + | The expression's type () => Unit is not allowed to capture the root capability `cap`. | This usually means that a capability persists longer than its allowed lifetime. 13 | () => foo(1) 14 | catch @@ -10,7 +10,7 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:18:2 ----------------------------------------------------------- 18 | try // error | ^ - | The expression's type () => Cell[Unit]^? is not allowed to capture the root capability `*`. + | The expression's type () => Cell[Unit]^? is not allowed to capture the root capability `cap`. | This usually means that a capability persists longer than its allowed lifetime. 19 | () => Cell(foo(1)) 20 | catch @@ -19,5 +19,5 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:30:4 ----------------------------------------------------------- 30 | b.x // error | ^^^ - | The expression's type box () => Unit is not allowed to capture the root capability `*`. + | The expression's type box () => Unit is not allowed to capture the root capability `cap`. | This usually means that a capability persists longer than its allowed lifetime. diff --git a/tests/neg-custom-args/captures/real-try.scala b/tests/neg-custom-args/captures/real-try.scala index 94e1eafd9af2..03e168032122 100644 --- a/tests/neg-custom-args/captures/real-try.scala +++ b/tests/neg-custom-args/captures/real-try.scala @@ -22,7 +22,7 @@ def test() = case _: Ex2 => ??? val b = try // ok here, but error on use - Cell(() => foo(1))//: Cell[box {ev} () => Unit] <: Cell[box {*} () => Unit] + Cell(() => foo(1))//: Cell[box {ev} () => Unit] <: Cell[box {cap} () => Unit] catch case _: Ex1 => ??? case _: Ex2 => ??? diff --git a/tests/neg-custom-args/captures/try.check b/tests/neg-custom-args/captures/try.check index 604f7bb1efb4..f85ca468e6e5 100644 --- a/tests/neg-custom-args/captures/try.check +++ b/tests/neg-custom-args/captures/try.check @@ -38,7 +38,7 @@ 39 | 22 40 | } { // error | ^ - | The expression's type box () => Int is not allowed to capture the root capability `*`. + | The expression's type box () => Int is not allowed to capture the root capability `cap`. | This usually means that a capability persists longer than its allowed lifetime. 41 | (ex: Exception) => () => 22 42 | } diff --git a/tests/neg-custom-args/captures/try.scala b/tests/neg-custom-args/captures/try.scala index 9489766d41be..ef375c62b817 100644 --- a/tests/neg-custom-args/captures/try.scala +++ b/tests/neg-custom-args/captures/try.scala @@ -2,8 +2,8 @@ import annotation.retains import language.experimental.erasedDefinitions class CT[E <: Exception] -type CanThrow[E <: Exception] = CT[E] @retains(caps.*) -type Top = Any @retains(caps.*) +type CanThrow[E <: Exception] = CT[E] @retains(caps.cap) +type Top = Any @retains(caps.cap) infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?=> R diff --git a/tests/neg-custom-args/captures/usingLogFile.check b/tests/neg-custom-args/captures/usingLogFile.check index 99e895c74ee7..17b9146ce71c 100644 --- a/tests/neg-custom-args/captures/usingLogFile.check +++ b/tests/neg-custom-args/captures/usingLogFile.check @@ -2,11 +2,11 @@ 33 | later3() // error | ^^^^^^ | box () => Unit cannot be box-converted to a type that can be selected or applied - | since one of their capture sets contains the root capability `*` + | since one of their capture sets contains the root capability `cap` -- Error: tests/neg-custom-args/captures/usingLogFile.scala:37:9 ------------------------------------------------------- 37 | later4.x() // error | ^^^^^^^^ - | The expression's type box () => Unit is not allowed to capture the root capability `*`. + | The expression's type box () => Unit is not allowed to capture the root capability `cap`. | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:23:6 ------------------------------------------------------- 23 | val later = usingLogFile { f => () => f.write(0) } // error @@ -18,7 +18,7 @@ -- Error: tests/neg-custom-args/captures/usingLogFile.scala:29:9 ------------------------------------------------------- 29 | later2.x() // error | ^^^^^^^^ - | The expression's type box () => Unit is not allowed to capture the root capability `*`. + | The expression's type box () => Unit is not allowed to capture the root capability `cap`. | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:47:6 ------------------------------------------------------- 47 | val later = usingLogFile { f => () => f.write(0) } // error @@ -30,10 +30,10 @@ -- Error: tests/neg-custom-args/captures/usingLogFile.scala:62:25 ------------------------------------------------------ 62 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | The expression's type box (x$0: Int) => Unit is not allowed to capture the root capability `*`. - | This usually means that a capability persists longer than its allowed lifetime. + | The expression's type box (x$0: Int) => Unit is not allowed to capture the root capability `cap`. + | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:71:25 ------------------------------------------------------ 71 | val later = usingFile("logfile", usingLogger(_, l => () => l.log("test"))) // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | The expression's type box () => Unit is not allowed to capture the root capability `*`. + | The expression's type box () => Unit is not allowed to capture the root capability `cap`. | This usually means that a capability persists longer than its allowed lifetime. diff --git a/tests/neg-custom-args/captures/vars.check b/tests/neg-custom-args/captures/vars.check index b0420c7e5179..565101359673 100644 --- a/tests/neg-custom-args/captures/vars.check +++ b/tests/neg-custom-args/captures/vars.check @@ -16,16 +16,16 @@ 16 | a("") // error | ^ | box String => String cannot be box-converted to a type that can be selected or applied - | since one of their capture sets contains the root capability `*` + | since one of their capture sets contains the root capability `cap` -- Error: tests/neg-custom-args/captures/vars.scala:17:4 --------------------------------------------------------------- 17 | b.head // error | ^^^^^^ - | The expression's type box String => String is not allowed to capture the root capability `*`. + | The expression's type box String => String is not allowed to capture the root capability `cap`. | This usually means that a capability persists longer than its allowed lifetime. -- Error: tests/neg-custom-args/captures/vars.scala:32:8 --------------------------------------------------------------- 32 | local { cap3 => // error | ^ - | The expression's type box (x$0: String) => String is not allowed to capture the root capability `*`. + | The expression's type box (x$0: String) => String is not allowed to capture the root capability `cap`. | This usually means that a capability persists longer than its allowed lifetime. 33 | def g(x: String): String = if cap3 == cap3 then "" else "a" 34 | g diff --git a/tests/pos-custom-args/captures/byname.scala b/tests/pos-custom-args/captures/byname.scala index 780214ebdffd..efd76618469d 100644 --- a/tests/pos-custom-args/captures/byname.scala +++ b/tests/pos-custom-args/captures/byname.scala @@ -6,7 +6,7 @@ class I def test(cap1: Cap, cap2: Cap): I^{cap1} = def f() = if cap1 == cap1 then I() else I() - def h(x: ->{any} I) = x + def h(x: ->{cap} I) = x h(f()) // OK def hh(x: -> I @retainsByName(cap1)) = x h(f()) diff --git a/tests/pos-custom-args/captures/caps-universal.scala b/tests/pos-custom-args/captures/caps-universal.scala index 77ce10c4e42e..3768c640fd68 100644 --- a/tests/pos-custom-args/captures/caps-universal.scala +++ b/tests/pos-custom-args/captures/caps-universal.scala @@ -1,7 +1,7 @@ import annotation.retains val foo: Int => Int = x => x -val bar: (Int -> Int) @retains(caps.*) = foo +val bar: (Int -> Int) @retains(caps.cap) = foo val baz: Int => Int = bar diff --git a/tests/pos-custom-args/captures/capt-depfun.scala b/tests/pos-custom-args/captures/capt-depfun.scala index 3a896b8acb8e..e3abbe0994c5 100644 --- a/tests/pos-custom-args/captures/capt-depfun.scala +++ b/tests/pos-custom-args/captures/capt-depfun.scala @@ -1,6 +1,6 @@ import annotation.retains class C -type Cap = C @retains(caps.*) +type Cap = C @retains(caps.cap) type T = (x: Cap) -> String @retains(x) @@ -8,7 +8,7 @@ type ID[X] = X val aa: ((x: Cap) -> String @retains(x)) = (x: Cap) => "" -def f(y: Cap, z: Cap): String @retains(caps.*) = +def f(y: Cap, z: Cap): String @retains(caps.cap) = val a: ((x: Cap) -> String @retains(x)) = (x: Cap) => "" val b = a(y) val c: String @retains(y) = b diff --git a/tests/pos-custom-args/captures/capt-depfun2.scala b/tests/pos-custom-args/captures/capt-depfun2.scala index 96e97de95d72..e4645cfcc920 100644 --- a/tests/pos-custom-args/captures/capt-depfun2.scala +++ b/tests/pos-custom-args/captures/capt-depfun2.scala @@ -1,6 +1,6 @@ import annotation.retains class C -type Cap = C @retains(caps.*) +type Cap = C @retains(caps.cap) def f(y: Cap, z: Cap) = def g(): C @retains(y, z) = ??? diff --git a/tests/pos-custom-args/captures/capt0.scala b/tests/pos-custom-args/captures/capt0.scala index fca48170b6f5..013ff3a4ee19 100644 --- a/tests/pos-custom-args/captures/capt0.scala +++ b/tests/pos-custom-args/captures/capt0.scala @@ -3,5 +3,5 @@ object Test: def test() = val x: Any^ = "abc" val y: Object @scala.annotation.retains(x) = ??? - val z: Object @scala.annotation.retains(x, caps.*) = y: Object @annotation.retains(x) + val z: Object @scala.annotation.retains(x, caps.cap) = y: Object @annotation.retains(x) diff --git a/tests/pos-custom-args/captures/capt1.scala b/tests/pos-custom-args/captures/capt1.scala index 865b382f4e9f..8d2285f1fa50 100644 --- a/tests/pos-custom-args/captures/capt1.scala +++ b/tests/pos-custom-args/captures/capt1.scala @@ -3,7 +3,7 @@ type Cap = C^ def f1(c: Cap): () ->{c} c.type = () => c // ok def f2: Int = - val g: Boolean ->{any} Int = ??? + val g: Boolean ->{cap} Int = ??? val x = g(true) x diff --git a/tests/pos-custom-args/captures/capt2.scala b/tests/pos-custom-args/captures/capt2.scala index 498dbd371ca0..45381bf602ed 100644 --- a/tests/pos-custom-args/captures/capt2.scala +++ b/tests/pos-custom-args/captures/capt2.scala @@ -1,6 +1,6 @@ import annotation.retains class C -type Cap = C @retains(caps.*) +type Cap = C @retains(caps.cap) def test1() = val y: String^ = "" diff --git a/tests/pos-custom-args/captures/cc-expand.scala b/tests/pos-custom-args/captures/cc-expand.scala index 1d398353a5ec..1bed7b1cf001 100644 --- a/tests/pos-custom-args/captures/cc-expand.scala +++ b/tests/pos-custom-args/captures/cc-expand.scala @@ -5,7 +5,7 @@ object Test: class B class C class CTC - type CT = CTC @retains(caps.*) + type CT = CTC @retains(caps.cap) def test(ct: CT, dt: CT) = diff --git a/tests/pos-custom-args/captures/overrides.scala b/tests/pos-custom-args/captures/overrides.scala index 7e70afe7a327..ac5b9cd9ddc4 100644 --- a/tests/pos-custom-args/captures/overrides.scala +++ b/tests/pos-custom-args/captures/overrides.scala @@ -1,4 +1,4 @@ -import caps.* +import caps.cap abstract class Foo: def foo: () => Unit = () => () diff --git a/tests/pos-custom-args/captures/try.scala b/tests/pos-custom-args/captures/try.scala index b2dcf6f11dd0..05c41be69001 100644 --- a/tests/pos-custom-args/captures/try.scala +++ b/tests/pos-custom-args/captures/try.scala @@ -2,7 +2,7 @@ import annotation.retains import language.experimental.erasedDefinitions class CT[E <: Exception] -type CanThrow[E <: Exception] = CT[E] @retains(caps.*) +type CanThrow[E <: Exception] = CT[E] @retains(caps.cap) infix type throws[R, E <: Exception] = (erased CanThrow[E]) ?-> R diff --git a/tests/pos-with-compiler-cc/dotc/ast/Trees.scala b/tests/pos-with-compiler-cc/dotc/ast/Trees.scala index aa1c06a7ca85..0b1842603316 100644 --- a/tests/pos-with-compiler-cc/dotc/ast/Trees.scala +++ b/tests/pos-with-compiler-cc/dotc/ast/Trees.scala @@ -1394,7 +1394,7 @@ object Trees { case _ => sourced - abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { self: TreeMap @retains(caps.*) => + abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { self: TreeMap @retains(caps.cap) => def transform(tree: Tree)(using Context): Tree = { inContext(transformCtx(tree)) { Stats.record(s"TreeMap.transform/$getClass") @@ -1520,7 +1520,7 @@ object Trees { } } - abstract class TreeAccumulator[X] { self: TreeAccumulator[X] @retains(caps.*) => + abstract class TreeAccumulator[X] { self: TreeAccumulator[X] @retains(caps.cap) => // Ties the knot of the traversal: call `foldOver(x, tree))` to dive in the `tree` node. def apply(x: X, tree: Tree)(using Context): X diff --git a/tests/pos-with-compiler-cc/dotc/ast/untpd.scala b/tests/pos-with-compiler-cc/dotc/ast/untpd.scala index b4dc6d0622c0..a6d3bc5a072c 100644 --- a/tests/pos-with-compiler-cc/dotc/ast/untpd.scala +++ b/tests/pos-with-compiler-cc/dotc/ast/untpd.scala @@ -742,7 +742,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { } abstract class UntypedTreeAccumulator[X] extends TreeAccumulator[X] { - self: UntypedTreeAccumulator[X] @retains(caps.*) => + self: UntypedTreeAccumulator[X] @retains(caps.cap) => override def foldMoreCases(x: X, tree: Tree)(using Context): X = tree match { case ModuleDef(name, impl) => this(x, impl) diff --git a/tests/pos-with-compiler-cc/dotc/cc/CaptureAnnotation.scala b/tests/pos-with-compiler-cc/dotc/cc/CaptureAnnotation.scala index 2e750865f407..67222f07efbb 100644 --- a/tests/pos-with-compiler-cc/dotc/cc/CaptureAnnotation.scala +++ b/tests/pos-with-compiler-cc/dotc/cc/CaptureAnnotation.scala @@ -51,7 +51,7 @@ case class CaptureAnnotation(refs: CaptureSet, boxed: Boolean)(cls: Symbol) exte this.refs == refs && this.boxed == boxed && this.symbol == that.symbol case _ => false - override def mapWith(tm: TypeMap @retains(caps.*))(using Context) = + override def mapWith(tm: TypeMap @retains(caps.cap))(using Context) = val elems = refs.elems.toList val elems1 = elems.mapConserve(tm) if elems1 eq elems then this diff --git a/tests/pos-with-compiler-cc/dotc/core/Annotations.scala b/tests/pos-with-compiler-cc/dotc/core/Annotations.scala index f3fee3da78ec..f307d4a36697 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Annotations.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Annotations.scala @@ -55,7 +55,7 @@ object Annotations { * be overridden. Returns EmptyAnnotation if type type map produces a range * type, since ranges cannot be types of trees. */ - def mapWith(tm: TypeMap @retains(caps.*))(using Context) = + def mapWith(tm: TypeMap @retains(caps.cap))(using Context) = val args = arguments if args.isEmpty then this else diff --git a/tests/pos-with-compiler-cc/dotc/core/Contexts.scala b/tests/pos-with-compiler-cc/dotc/core/Contexts.scala index 2ce714937f97..37fde2d7b604 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Contexts.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Contexts.scala @@ -110,7 +110,7 @@ object Contexts { inline def inDetachedContext[T](inline op: DetachedContext ?-> T)(using ctx: Context): T = op(using ctx.detach) - type Context = ContextCls @retains(caps.*) + type Context = ContextCls @retains(caps.cap) /** A context is passed basically everywhere in dotc. * This is convenient but carries the risk of captured contexts in diff --git a/tests/pos-with-compiler-cc/dotc/core/Decorators.scala b/tests/pos-with-compiler-cc/dotc/core/Decorators.scala index a4c3938a0909..f9844c6eaab6 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Decorators.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Decorators.scala @@ -84,7 +84,7 @@ object Decorators { * on lists that avoid duplication of list nodes where feasible. */ extension [T](xs: List[T]) - final def collectCC[U](pf: PartialFunction[T, U] @retains(caps.*)): List[U] = + final def collectCC[U](pf: PartialFunction[T, U] @retains(caps.cap)): List[U] = xs.collect(pf.asInstanceOf) final def mapconserve[U](f: T => U): List[U] = { @@ -230,11 +230,11 @@ object Decorators { end extension extension [T](xs: Seq[T]) - final def collectCC[U](pf: PartialFunction[T, U] @retains(caps.*)): Seq[U] = + final def collectCC[U](pf: PartialFunction[T, U] @retains(caps.cap)): Seq[U] = xs.collect(pf.asInstanceOf) - extension [A, B](f: PartialFunction[A, B] @retains(caps.*)) - def orElseCC(g: PartialFunction[A, B] @retains(caps.*)): PartialFunction[A, B] @retains(f, g) = + extension [A, B](f: PartialFunction[A, B] @retains(caps.cap)) + def orElseCC(g: PartialFunction[A, B] @retains(caps.cap)): PartialFunction[A, B] @retains(f, g) = f.orElse(g.asInstanceOf).asInstanceOf extension (text: Text) diff --git a/tests/pos-with-compiler-cc/dotc/core/GadtConstraint.scala b/tests/pos-with-compiler-cc/dotc/core/GadtConstraint.scala index 46b7e07649b8..fcf1215c604f 100644 --- a/tests/pos-with-compiler-cc/dotc/core/GadtConstraint.scala +++ b/tests/pos-with-compiler-cc/dotc/core/GadtConstraint.scala @@ -225,7 +225,7 @@ sealed trait GadtConstraint ( // ---- Private ---------------------------------------------------------- - private def externalize(tp: Type, theMap: TypeMap @retains(caps.*) | Null = null)(using Context): Type = tp match + private def externalize(tp: Type, theMap: TypeMap @retains(caps.cap) | Null = null)(using Context): Type = tp match case param: TypeParamRef => reverseMapping(param) match case sym: Symbol => sym.typeRef case null => param @@ -238,7 +238,7 @@ sealed trait GadtConstraint ( private def tvarOrError(sym: Symbol)(using Context): TypeVar = mapping(sym).ensuring(_ != null, i"not a constrainable symbol: $sym").uncheckedNN - private def containsNoInternalTypes(tp: Type, theAcc: TypeAccumulator[Boolean] @retains(caps.*) | Null = null)(using Context): Boolean = tp match { + private def containsNoInternalTypes(tp: Type, theAcc: TypeAccumulator[Boolean] @retains(caps.cap) | Null = null)(using Context): Boolean = tp match { case tpr: TypeParamRef => !reverseMapping.contains(tpr) case tv: TypeVar => !reverseMapping.contains(tv.origin) case tp => diff --git a/tests/pos-with-compiler-cc/dotc/core/Names.scala b/tests/pos-with-compiler-cc/dotc/core/Names.scala index cb68299101ad..e6ea66f4025b 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Names.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Names.scala @@ -74,10 +74,10 @@ object Names { * Stops at DerivedNames with infos of kind QualifiedInfo. * If `f` does not apply to any part, return name unchanged. */ - def replace(f: PartialFunction[Name, Name] @retains(caps.*)): ThisName + def replace(f: PartialFunction[Name, Name] @retains(caps.cap)): ThisName /** Same as replace, but does not stop at DerivedNames with infos of kind QualifiedInfo. */ - def replaceDeep(f: PartialFunction[Name, Name] @retains(caps.*)): ThisName = + def replaceDeep(f: PartialFunction[Name, Name] @retains(caps.cap)): ThisName = replace(f.orElseCC { case DerivedName(underlying, info: QualifiedInfo) => underlying.replaceDeep(f).derived(info) @@ -340,7 +340,7 @@ object Names { override def toSimpleName: SimpleName = this override final def mangle: SimpleName = encode - override def replace(f: PartialFunction[Name, Name] @retains(caps.*)): ThisName = + override def replace(f: PartialFunction[Name, Name] @retains(caps.cap)): ThisName = if (f.isDefinedAt(this)) likeSpaced(f(this)) else this override def collect[T](f: PartialFunction[Name, T]): Option[T] = f.lift(this) override def mapLast(f: SimpleName => SimpleName): SimpleName = f(this) @@ -440,7 +440,7 @@ object Names { override def mangled: TypeName = toTermName.mangled.toTypeName override def mangledString: String = toTermName.mangledString - override def replace(f: PartialFunction[Name, Name] @retains(caps.*)): ThisName = toTermName.replace(f).toTypeName + override def replace(f: PartialFunction[Name, Name] @retains(caps.cap)): ThisName = toTermName.replace(f).toTypeName override def collect[T](f: PartialFunction[Name, T]): Option[T] = toTermName.collect(f) override def mapLast(f: SimpleName => SimpleName): TypeName = toTermName.mapLast(f).toTypeName override def mapParts(f: SimpleName => SimpleName): TypeName = toTermName.mapParts(f).toTypeName @@ -473,7 +473,7 @@ object Names { override def toSimpleName: SimpleName = termName(toString) override final def mangle: SimpleName = encode.toSimpleName - override def replace(f: PartialFunction[Name, Name] @retains(caps.*)): ThisName = + override def replace(f: PartialFunction[Name, Name] @retains(caps.cap)): ThisName = if (f.isDefinedAt(this)) likeSpaced(f(this)) else info match { case qual: QualifiedInfo => this diff --git a/tests/pos-with-compiler-cc/dotc/core/Substituters.scala b/tests/pos-with-compiler-cc/dotc/core/Substituters.scala index 1e86274f663e..6f7e02ec4dde 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Substituters.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Substituters.scala @@ -10,7 +10,7 @@ import annotation.retains */ object Substituters: - final def subst(tp: Type, from: BindingType, to: BindingType, theMap: SubstBindingMap @retains(caps.*) | Null)(using Context): Type = + final def subst(tp: Type, from: BindingType, to: BindingType, theMap: SubstBindingMap @retains(caps.cap) | Null)(using Context): Type = tp match { case tp: BoundType => if (tp.binder eq from) tp.copyBoundType(to.asInstanceOf[tp.BT]) else tp @@ -26,7 +26,7 @@ object Substituters: .mapOver(tp) } - final def subst1(tp: Type, from: Symbol, to: Type, theMap: Subst1Map @retains(caps.*) | Null)(using Context): Type = + final def subst1(tp: Type, from: Symbol, to: Type, theMap: Subst1Map @retains(caps.cap) | Null)(using Context): Type = tp match { case tp: NamedType => val sym = tp.symbol @@ -40,7 +40,7 @@ object Substituters: .mapOver(tp) } - final def subst2(tp: Type, from1: Symbol, to1: Type, from2: Symbol, to2: Type, theMap: Subst2Map @retains(caps.*) | Null)(using Context): Type = + final def subst2(tp: Type, from1: Symbol, to1: Type, from2: Symbol, to2: Type, theMap: Subst2Map @retains(caps.cap) | Null)(using Context): Type = tp match { case tp: NamedType => val sym = tp.symbol @@ -55,7 +55,7 @@ object Substituters: .mapOver(tp) } - final def subst(tp: Type, from: List[Symbol], to: List[Type], theMap: SubstMap @retains(caps.*) | Null)(using Context): Type = + final def subst(tp: Type, from: List[Symbol], to: List[Type], theMap: SubstMap @retains(caps.cap) | Null)(using Context): Type = tp match { case tp: NamedType => val sym = tp.symbol @@ -75,7 +75,7 @@ object Substituters: .mapOver(tp) } - final def substSym(tp: Type, from: List[Symbol], to: List[Symbol], theMap: SubstSymMap @retains(caps.*) | Null)(using Context): Type = + final def substSym(tp: Type, from: List[Symbol], to: List[Symbol], theMap: SubstSymMap @retains(caps.cap) | Null)(using Context): Type = tp match { case tp: NamedType => val sym = tp.symbol @@ -106,7 +106,7 @@ object Substituters: .mapOver(tp) } - final def substThis(tp: Type, from: ClassSymbol, to: Type, theMap: SubstThisMap @retains(caps.*) | Null)(using Context): Type = + final def substThis(tp: Type, from: ClassSymbol, to: Type, theMap: SubstThisMap @retains(caps.cap) | Null)(using Context): Type = tp match { case tp: ThisType => if (tp.cls eq from) to else tp @@ -120,7 +120,7 @@ object Substituters: .mapOver(tp) } - final def substRecThis(tp: Type, from: Type, to: Type, theMap: SubstRecThisMap @retains(caps.*) | Null)(using Context): Type = + final def substRecThis(tp: Type, from: Type, to: Type, theMap: SubstRecThisMap @retains(caps.cap) | Null)(using Context): Type = tp match { case tp @ RecThis(binder) => if (binder eq from) to else tp @@ -134,7 +134,7 @@ object Substituters: .mapOver(tp) } - final def substParam(tp: Type, from: ParamRef, to: Type, theMap: SubstParamMap @retains(caps.*) | Null)(using Context): Type = + final def substParam(tp: Type, from: ParamRef, to: Type, theMap: SubstParamMap @retains(caps.cap) | Null)(using Context): Type = tp match { case tp: BoundType => if (tp == from) to else tp @@ -148,7 +148,7 @@ object Substituters: .mapOver(tp) } - final def substParams(tp: Type, from: BindingType, to: List[Type], theMap: SubstParamsMap @retains(caps.*) | Null)(using Context): Type = + final def substParams(tp: Type, from: BindingType, to: List[Type], theMap: SubstParamsMap @retains(caps.cap) | Null)(using Context): Type = tp match { case tp: ParamRef => if (tp.binder == from) to(tp.paramNum) else tp diff --git a/tests/pos-with-compiler-cc/dotc/core/TypeOps.scala b/tests/pos-with-compiler-cc/dotc/core/TypeOps.scala index ad71f3100817..717da533a439 100644 --- a/tests/pos-with-compiler-cc/dotc/core/TypeOps.scala +++ b/tests/pos-with-compiler-cc/dotc/core/TypeOps.scala @@ -128,7 +128,7 @@ object TypeOps: pre.isStable || !ctx.phase.isTyper /** Implementation of Types#simplified */ - def simplify(tp: Type, theMap: SimplifyMap @retains(caps.*) | Null)(using Context): Type = { + def simplify(tp: Type, theMap: SimplifyMap @retains(caps.cap) | Null)(using Context): Type = { def mapOver = (if (theMap != null) theMap else new SimplifyMap).mapOver(tp) tp match { case tp: NamedType => diff --git a/tests/pos-with-compiler-cc/dotc/core/Types.scala b/tests/pos-with-compiler-cc/dotc/core/Types.scala index f94e9ac6d645..e4b30888a5dc 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Types.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Types.scala @@ -115,7 +115,7 @@ object Types { private def testProvisional(using Context): Boolean = class ProAcc extends TypeAccumulator[Boolean]: override def apply(x: Boolean, t: Type) = x || test(t, this) - def test(t: Type, theAcc: TypeAccumulator[Boolean] @retains(caps.*) | Null): Boolean = + def test(t: Type, theAcc: TypeAccumulator[Boolean] @retains(caps.cap) | Null): Boolean = if t.mightBeProvisional then t.mightBeProvisional = t match case t: TypeRef => @@ -2158,8 +2158,8 @@ object Types { /** A trait for proto-types, used as expected types in typer */ trait ProtoType extends Type { def isMatchedBy(tp: Type, keepConstraint: Boolean = false)(using Context): Boolean - def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.*))(using Context): T - def map(tm: TypeMap @retains(caps.*))(using Context): ProtoType + def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.cap))(using Context): T + def map(tm: TypeMap @retains(caps.cap))(using Context): ProtoType /** If this prototype captures a context, the same prototype except that the result * captures the given context `ctx`. @@ -3773,7 +3773,7 @@ object Types { val status = (x & StatusMask) max (y & StatusMask) val provisional = (x | y) & Provisional (if status == TrueDeps then status else status | provisional).toByte - def compute(status: DependencyStatus, tp: Type, theAcc: TypeAccumulator[DependencyStatus] @retains(caps.*) | Null): DependencyStatus = + def compute(status: DependencyStatus, tp: Type, theAcc: TypeAccumulator[DependencyStatus] @retains(caps.cap) | Null): DependencyStatus = def applyPrefix(tp: NamedType) = if tp.isInstanceOf[SingletonType] && tp.currentSymbol.isStatic then status // Note: a type ref with static symbol can still be dependent since the symbol might be refined in the enclosing type. See pos/15331.scala. @@ -4351,7 +4351,7 @@ object Types { private var myEvalRunId: RunId = NoRunId private var myEvalued: Type = uninitialized - def isGround(acc: TypeAccumulator[Boolean] @retains(caps.*))(using Context): Boolean = + def isGround(acc: TypeAccumulator[Boolean] @retains(caps.cap))(using Context): Boolean = if myGround == 0 then myGround = if acc.foldOver(true, this) then 1 else -1 myGround > 0 @@ -5552,7 +5552,7 @@ object Types { * BiTypeMaps should map capture references to capture references. */ trait BiTypeMap extends TypeMap: - thisMap: BiTypeMap @retains(caps.*) => + thisMap: BiTypeMap @retains(caps.cap) => /** The inverse of the type map as a function */ def inverse(tp: Type): Type @@ -6106,7 +6106,7 @@ object Types { abstract class TypeAccumulator[T](implicit protected val accCtx: Context) extends VariantTraversal with ((T, Type) => T) { - this: TypeAccumulator[T] @annotation.retains(caps.*) => + this: TypeAccumulator[T] @annotation.retains(caps.cap) => def apply(x: T, tp: Type): T diff --git a/tests/pos-with-compiler-cc/dotc/typer/ProtoTypes.scala b/tests/pos-with-compiler-cc/dotc/typer/ProtoTypes.scala index 838cca3f6fa7..77fd2c1d6d66 100644 --- a/tests/pos-with-compiler-cc/dotc/typer/ProtoTypes.scala +++ b/tests/pos-with-compiler-cc/dotc/typer/ProtoTypes.scala @@ -125,8 +125,8 @@ object ProtoTypes { /** A trait for prototypes that match all types */ trait MatchAlways extends ProtoType, caps.Pure { def isMatchedBy(tp1: Type, keepConstraint: Boolean)(using Context): Boolean = true - def map(tm: TypeMap @retains(caps.*))(using Context): ProtoType = this - def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.*))(using Context): T = x + def map(tm: TypeMap @retains(caps.cap))(using Context): ProtoType = this + def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.cap))(using Context): T = x override def toString: String = getClass.toString } @@ -239,8 +239,8 @@ object ProtoTypes { override def unusableForInference(using Context): Boolean = memberProto.unusableForInference - def map(tm: TypeMap @retains(caps.*))(using Context): SelectionProto = derivedSelectionProto(name, tm(memberProto), compat) - def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.*))(using Context): T = ta(x, memberProto) + def map(tm: TypeMap @retains(caps.cap))(using Context): SelectionProto = derivedSelectionProto(name, tm(memberProto), compat) + def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.cap))(using Context): T = ta(x, memberProto) override def deepenProto(using Context): SelectionProto = derivedSelectionProto(name, memberProto.deepenProto, compat) @@ -542,10 +542,10 @@ object ProtoTypes { override def toString: String = s"FunProto(${args mkString ","} => $resultType)" - def map(tm: TypeMap @retains(caps.*))(using Context): FunProto = + def map(tm: TypeMap @retains(caps.cap))(using Context): FunProto = derivedFunProto(args, tm(resultType), typer) - def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.*))(using Context): T = + def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.cap))(using Context): T = ta(ta.foldOver(x, typedArgs().tpes), resultType) override def deepenProto(using Context): FunProto = @@ -600,9 +600,9 @@ object ProtoTypes { override def unusableForInference(using Context): Boolean = argType.unusableForInference || resType.unusableForInference - def map(tm: TypeMap @retains(caps.*))(using Context): ViewProto = derivedViewProto(tm(argType), tm(resultType)) + def map(tm: TypeMap @retains(caps.cap))(using Context): ViewProto = derivedViewProto(tm(argType), tm(resultType)) - def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.*))(using Context): T = + def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.cap))(using Context): T = ta(ta(x, argType), resultType) override def deepenProto(using Context): ViewProto = @@ -653,10 +653,10 @@ object ProtoTypes { override def unusableForInference(using Context): Boolean = targs.exists(_.tpe.unusableForInference) - def map(tm: TypeMap @retains(caps.*))(using Context): PolyProto = + def map(tm: TypeMap @retains(caps.cap))(using Context): PolyProto = derivedPolyProto(targs, tm(resultType)) - def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.*))(using Context): T = + def fold[T](x: T, ta: TypeAccumulator[T] @retains(caps.cap))(using Context): T = ta(ta.foldOver(x, targs.tpes), resultType) override def deepenProto(using Context): PolyProto = @@ -834,7 +834,7 @@ object ProtoTypes { /** Approximate occurrences of parameter types and uninstantiated typevars * by wildcard types. */ - private def wildApprox(tp: Type, theMap: WildApproxMap @retains(caps.*) | Null, seen: Set[TypeParamRef], internal: Set[TypeLambda])(using Context): Type = + private def wildApprox(tp: Type, theMap: WildApproxMap @retains(caps.cap) | Null, seen: Set[TypeParamRef], internal: Set[TypeLambda])(using Context): Type = tp match { case tp: NamedType => // default case, inlined for speed val isPatternBoundTypeRef = tp.isInstanceOf[TypeRef] && tp.symbol.isPatternBound diff --git a/tests/pos/boxmap-paper.scala b/tests/pos/boxmap-paper.scala index ea4c31f099c4..7c2c005e6a61 100644 --- a/tests/pos/boxmap-paper.scala +++ b/tests/pos/boxmap-paper.scala @@ -19,7 +19,7 @@ def lazyMap[A, B](c: Cell[A])(f: A => B): () ->{f} Cell[B] trait IO: def print(s: String): Unit -def test(io: IO^{*}) = +def test(io: IO^{cap}) = val loggedOne: () ->{io} Int = () => { io.print("1"); 1 } diff --git a/tests/run-custom-args/captures/colltest5/Test_2.scala b/tests/run-custom-args/captures/colltest5/Test_2.scala index d6b4e43483e6..fbb22039c327 100644 --- a/tests/run-custom-args/captures/colltest5/Test_2.scala +++ b/tests/run-custom-args/captures/colltest5/Test_2.scala @@ -5,7 +5,7 @@ object Test { import colltest5.strawman.collections.* import CollectionStrawMan5.* - def seqOps(xs: Seq[Int]) = { // try with Seq[Int]^{any} + def seqOps(xs: Seq[Int]) = { // try with Seq[Int]^{cap} val strPlusInt: (String, Int) => String = _ + _ val intPlusStr: (Int, String) => String = _ + _ val isEven: Int => Boolean = _ % 2 == 0 @@ -61,7 +61,7 @@ object Test { println(xs16) } - def viewOps(xs: View[Int]^{any}) = { + def viewOps(xs: View[Int]^{cap}) = { val strPlusInt: (String, Int) => String = _ + _ val intPlusStr: (Int, String) => String = _ + _ val isEven: Int => Boolean = _ % 2 == 0 From f01ad50bd5bdf0c5d5be54a9c452cbb069defddc Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 2 May 2023 10:08:55 +0200 Subject: [PATCH 531/657] Deprecate caps.* and update MimaFilters --- library/src/scala/caps.scala | 7 +++++-- project/MiMaFilters.scala | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/library/src/scala/caps.scala b/library/src/scala/caps.scala index 921b370ff5b0..6b7b085bbc06 100644 --- a/library/src/scala/caps.scala +++ b/library/src/scala/caps.scala @@ -4,9 +4,12 @@ import annotation.experimental @experimental object caps: + /** The universal capture reference (deprecated) */ + @deprecated("Use `cap` instead") + val `*`: Any = () + /** The universal capture reference */ - val `*`: Any = () // OLD - val cap: Any = () // NEW + val cap: Any = () object unsafe: diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index d53eeb7077a4..5009d68a7247 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -5,6 +5,7 @@ object MiMaFilters { val Library: Seq[ProblemFilter] = Seq( ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.unsafeBox"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.unsafeUnbox"), + ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.cap"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.CanEqual.canEqualMap"), ProblemFilters.exclude[MissingClassProblem]("scala.caps$Pure"), ProblemFilters.exclude[MissingClassProblem]("scala.caps$unsafe$"), From 3927cec7b525fae49a902b8c59dfa6bee621ae33 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 3 May 2023 12:07:33 +0200 Subject: [PATCH 532/657] Change to new syntax in doc comments ... and fix a missing change when printing trees --- .../src/dotty/tools/dotc/cc/CheckCaptures.scala | 12 ++++++------ .../src/dotty/tools/dotc/parsing/Parsers.scala | 15 +++++++-------- .../tools/dotc/printing/RefinedPrinter.scala | 4 ++-- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index e77c72c422ca..72a166dbbecc 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -52,12 +52,12 @@ object CheckCaptures: * @param outer0 the next enclosing environment */ case class Env( - owner: Symbol, - nestedInOwner: Boolean, - captured: CaptureSet, - isBoxed: Boolean, - outer0: Env | Null - ): + owner: Symbol, + nestedInOwner: Boolean, + captured: CaptureSet, + isBoxed: Boolean, + outer0: Env | Null): + def outer = outer0.nn def isOutermost = outer0 == null diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 9376b995b011..1dab95d75225 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1462,12 +1462,11 @@ object Parsers { * | FunParamClause ‘=>>’ Type * | MatchType * | InfixType - * | CaptureSet Type -- under captureChecking * FunType ::= (MonoFunType | PolyFunType) * MonoFunType ::= FunTypeArgs (‘=>’ | ‘?=>’) Type - * | (‘->’ | ‘?->’ ) Type -- under pureFunctions + * | (‘->’ | ‘?->’ ) [CaptureSet] Type -- under pureFunctions * PolyFunType ::= HKTypeParamClause '=>' Type - * | HKTypeParamClause ‘->’ Type -- under pureFunctions + * | HKTypeParamClause ‘->’ [CaptureSet] Type -- under pureFunctions * FunTypeArgs ::= InfixType * | `(' [ [ ‘[using]’ ‘['erased'] FunArgType {`,' FunArgType } ] `)' * | '(' [ ‘[using]’ ‘['erased'] TypedFunParam {',' TypedFunParam } ')' @@ -1951,13 +1950,13 @@ object Parsers { /** FunArgType ::= Type * | `=>' Type - * | [CaptureSet] `->' Type + * | `->' [CaptureSet] Type */ val funArgType: () => Tree = () => paramTypeOf(typ) /** ParamType ::= ParamValueType * | `=>' ParamValueType - * | [CaptureSet] `->' ParamValueType + * | `->' [CaptureSet] ParamValueType */ def paramType(): Tree = paramTypeOf(paramValueType) @@ -2096,7 +2095,7 @@ object Parsers { * | ‘inline’ InfixExpr MatchClause * Bindings ::= `(' [Binding {`,' Binding}] `)' * Binding ::= (id | `_') [`:' Type] - * Ascription ::= `:' [CaptureSet] InfixType + * Ascription ::= `:' InfixType * | `:' Annotation {Annotation} * | `:' `_' `*' * Catches ::= ‘catch’ (Expr | ExprCaseClause) @@ -4155,8 +4154,8 @@ object Parsers { stats.toList } - /** SelfType ::= id [‘:’ [CaptureSet] InfixType] ‘=>’ - * | ‘this’ ‘:’ [CaptureSet] InfixType ‘=>’ + /** SelfType ::= id [‘:’ InfixType] ‘=>’ + * | ‘this’ ‘:’ InfixType ‘=>’ */ def selfType(): ValDef = if (in.isIdent || in.token == THIS) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index e6fe55ccb2fc..0a059fdf00eb 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -628,7 +628,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def toTextAnnot = toTextLocal(arg) ~~ annotText(annot.symbol.enclosingClass, annot) def toTextRetainsAnnot = - try changePrec(GlobalPrec)(toTextCaptureSet(captureSet) ~ " " ~ toText(arg)) + try changePrec(GlobalPrec)(toText(arg) ~ "^" ~ toTextCaptureSet(captureSet)) catch case ex: IllegalCaptureRef => toTextAnnot if annot.symbol.maybeOwner == defn.RetainsAnnot && Feature.ccEnabled && Config.printCaptureSetsAsPrefix && !printDebug @@ -741,7 +741,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val tptText = toTextGlobal(tpt) prefix ~~ idx.toString ~~ "|" ~~ tptText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix case CapturesAndResult(refs, parent) => - changePrec(GlobalPrec)("^{" ~ Text(refs.map(toText), ", ") ~ "} " ~ toText(parent)) + changePrec(GlobalPrec)("^{" ~ Text(refs.map(toText), ", ") ~ "}" ~ toText(parent)) case _ => tree.fallbackToText(this) } From ed9f4b21d9f6f67c6e745acbe1afe92c2ac256ed Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 3 May 2023 18:41:02 +0200 Subject: [PATCH 533/657] Allow new capture syntax only if capture checking is enabled --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 1dab95d75225..33bd46f619aa 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1453,7 +1453,7 @@ object Parsers { } def capturesAndResult(core: () => Tree): Tree = - if in.token == LBRACE && in.offset == in.lastOffset + if Feature.ccEnabled && in.token == LBRACE && in.offset == in.lastOffset then CapturesAndResult(captureSet(), core()) else core() @@ -1685,7 +1685,7 @@ object Parsers { * or followed by a token that cannot start an infix type. * Otherwise it is treated as an infix operator. */ - private def isTrailingUpArrow = + private def isCaptureUpArrow = val ahead = in.lookahead ahead.token == LBRACE || ahead.isIdent(nme.PUREARROW) @@ -1699,7 +1699,7 @@ object Parsers { refinedTypeRest(atSpan(startOffset(t)) { RefinedTypeTree(rejectWildcardType(t), refinement(indentOK = true)) }) - else if in.isIdent(nme.UPARROW) && isTrailingUpArrow then + else if Feature.ccEnabled && in.isIdent(nme.UPARROW) && isCaptureUpArrow then val upArrowStart = in.offset in.nextToken() def cs = From 77f537881d5cbfb548720237f853352c184eee9b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 May 2023 18:22:26 +0200 Subject: [PATCH 534/657] Fix typo (#17416) [skip ci] --- staging/src/scala/quoted/staging/Compiler.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/staging/src/scala/quoted/staging/Compiler.scala b/staging/src/scala/quoted/staging/Compiler.scala index c9abe3fa75c3..fbe6a3915a08 100644 --- a/staging/src/scala/quoted/staging/Compiler.scala +++ b/staging/src/scala/quoted/staging/Compiler.scala @@ -13,7 +13,7 @@ object Compiler: /** Create a new instance of the compiler using the the classloader of the application. * - * Usuage: + * Usage: * ``` * import scala.quoted.staging._ * given Compiler = Compiler.make(getClass.getClassLoader) From 90f4253bf871cce9b79073759757696b001f5634 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 5 May 2023 12:49:59 +0200 Subject: [PATCH 535/657] Add test --- tests/pos/i17381.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/pos/i17381.scala diff --git a/tests/pos/i17381.scala b/tests/pos/i17381.scala new file mode 100644 index 000000000000..3d8aac8e9f67 --- /dev/null +++ b/tests/pos/i17381.scala @@ -0,0 +1,9 @@ +import reflect.Selectable.reflectiveSelectable + +type Y = { type T = String; def u(): T } + +trait Test { + + val y1: Y + val y2 = y1.u() +} \ No newline at end of file From 830230fa2a47ff18994b5820283332c40f65eeb3 Mon Sep 17 00:00:00 2001 From: som-snytt Date: Mon, 8 May 2023 00:14:01 -0700 Subject: [PATCH 536/657] Avoid deprecated URL ctor in scaladoc (#17426) Follow-up to https://github.com/lampepfl/dotty/pull/17403 includes scaladoc. --- scaladoc/src/dotty/tools/scaladoc/ExternalDocLink.scala | 8 ++++---- .../src/dotty/tools/scaladoc/renderers/Resources.scala | 4 ++-- .../src/dotty/tools/scaladoc/renderers/SiteRenderer.scala | 5 ++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/scaladoc/src/dotty/tools/scaladoc/ExternalDocLink.scala b/scaladoc/src/dotty/tools/scaladoc/ExternalDocLink.scala index fd4963bcce4a..536d759388f3 100644 --- a/scaladoc/src/dotty/tools/scaladoc/ExternalDocLink.scala +++ b/scaladoc/src/dotty/tools/scaladoc/ExternalDocLink.scala @@ -1,6 +1,6 @@ package dotty.tools.scaladoc -import java.net.URL +import java.net.{URI, URL} import scala.util.matching._ import scala.util.{ Try, Success, Failure } @@ -30,7 +30,7 @@ object ExternalDocLink: def parseLegacy(mapping: String): Either[String, ExternalDocLink] = mapping.split("#").toList match case path :: apiUrl :: Nil => for { - url <- tryParse(mapping, "url")(URL(stripIndex(apiUrl))) + url <- tryParse(mapping, "url")(URI(stripIndex(apiUrl)).toURL) } yield ExternalDocLink( List(s"${Regex.quote(path)}.*".r), url, @@ -42,7 +42,7 @@ object ExternalDocLink: def parse(mapping: String): Either[String, ExternalDocLink] = def parsePackageList(elements: List[String]) = elements match - case List(urlStr) => tryParse(mapping, "packageList")(Some(URL(urlStr))) + case List(urlStr) => tryParse(mapping, "packageList")(Some(URI(urlStr).toURL)) case Nil => Right(None) case other => fail(mapping, s"Provided multiple package lists: $other") @@ -57,7 +57,7 @@ object ExternalDocLink: case regexStr :: docToolStr :: urlStr :: rest => for { regex <- tryParse(mapping, "regex")(regexStr.r) - url <- tryParse(mapping, "url")(URL(stripIndex(urlStr))) + url <- tryParse(mapping, "url")(URI(stripIndex(urlStr)).toURL) doctool <- doctoolByName(docToolStr) packageList <- parsePackageList(rest) } yield ExternalDocLink( diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala index 27af3ac5dee6..b84c07b4bade 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala @@ -2,7 +2,7 @@ package dotty.tools.scaladoc package renderers import util.HTML._ -import java.net.URL +import java.net.{URI, URL} import java.nio.file.Paths import java.nio.file.Path import java.nio.file.Files @@ -565,4 +565,4 @@ trait Resources(using ctx: DocContext) extends Locations, Writer: case Resource.URL(url) => Nil case Resource.URLToCopy(url, dest) => - Seq(copy(new URL(url).openStream(), dest)) + Seq(copy(URI(url).toURL.openStream(), dest)) diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala index ef80b4f2d327..ef7c06416e27 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/SiteRenderer.scala @@ -3,8 +3,7 @@ package renderers import util.HTML._ import scala.jdk.CollectionConverters._ -import java.net.URI -import java.net.URL +import java.net.{URI, URL} import dotty.tools.scaladoc.site._ import scala.util.Try import org.jsoup.Jsoup @@ -40,7 +39,7 @@ trait SiteRenderer(using DocContext) extends Locations: def processLocalLink(str: String): String = val staticSiteRootPath = content.ctx.root.toPath.toAbsolutePath - def asValidURL: Option[String] = Try(URL(str)).toOption.map(_ => str) + def asValidURL: Option[String] = Try(URI(str).toURL).toOption.map(_ => str) def asAsset: Option[String] = Option.when( Files.exists(staticSiteRootPath.resolve("_assets").resolve(str.stripPrefix("/"))) )( From c9dc8e72cc406d6bb7407fff8a97acc130e9ad6a Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Mon, 8 May 2023 23:12:18 +0200 Subject: [PATCH 537/657] Fix #17435: A simpler fix This commit replaces #17382 with a simpler fix. --- .../tools/dotc/transform/TypeTestsCasts.scala | 63 +++---------------- 1 file changed, 8 insertions(+), 55 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 979538b56fc5..f5cb8eab73a4 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -53,7 +53,7 @@ object TypeTestsCasts { * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). * 7. if `P` is a refinement type, "it's a refinement type" * 8. if `P` is a local class which is not statically reachable from the scope where `X` is defined, "it's a local class" - * 9. if `X` is `T1 | T2`, checkable(T1, P) && checkable(T2, P) or (isCheckDefinitelyFalse(T1, P) && checkable(T2, P)) or (checkable(T1, P) && isCheckDefinitelyFalse(T2, P)). + * 9. if `X` is `T1 | T2`, checkable(T1, P) && checkable(T2, P). * 10. otherwise, "" */ def whyUncheckable(X: Type, P: Type, span: Span)(using Context): String = atPhase(Phases.refchecksPhase.next) { @@ -132,42 +132,8 @@ object TypeTestsCasts { } - /** Whether the check X.isInstanceOf[P] is definitely false? */ - def isCheckDefinitelyFalse(X: Type, P: Type)(using Context): Boolean = trace(s"isCheckDefinitelyFalse(${X.show}, ${P.show})") { - X.widenDealias match - case AndType(x1, x2) => - isCheckDefinitelyFalse(x1, P) || isCheckDefinitelyFalse(x2, P) - - case x => - P.widenDealias match - case AndType(p1, p2) => - isCheckDefinitelyFalse(x, p1) || isCheckDefinitelyFalse(x, p2) - - case p => - val pSpace = Typ(p) - val xSpace = Typ(x) - if pSpace.canDecompose then - val ps = pSpace.decompose.map(_.tp) - ps.forall(p => isCheckDefinitelyFalse(x, p)) - else if xSpace.canDecompose then - val xs = xSpace.decompose.map(_.tp) - xs.forall(x => isCheckDefinitelyFalse(x, p)) - else - if x.typeSymbol.isClass && p.typeSymbol.isClass then - val xClass = effectiveClass(x) - val pClass = effectiveClass(p) - - val bothAreClasses = !xClass.is(Trait) && !pClass.is(Trait) - val notXsubP = !xClass.derivesFrom(pClass) - val notPsubX = !pClass.derivesFrom(xClass) - bothAreClasses && notXsubP && notPsubX - || xClass.is(Final) && notXsubP - || pClass.is(Final) && notPsubX - else - false - } - - def recur(X: Type, P: Type): String = (X <:< P) ||| (P.dealias match { + def recur(X: Type, P: Type): String = trace(s"recur(${X.show}, ${P.show})") { + (X <:< P) ||| P.dealias.match case _: SingletonType => "" case _: TypeProxy if isAbstract(P) => i"it refers to an abstract type member or type parameter" @@ -176,7 +142,7 @@ object TypeTestsCasts { case defn.ArrayOf(tpE) => recur(tpE, tpT) case _ => recur(defn.AnyType, tpT) } - case tpe: AppliedType => + case tpe @ AppliedType(tycon, targs) => X.widenDealias match { case OrType(tp1, tp2) => // This case is required to retrofit type inference, @@ -184,24 +150,11 @@ object TypeTestsCasts { // - T1 <:< T2 | T3 // - T1 & T2 <:< T3 // See TypeComparer#either - val res1 = recur(tp1, P) - val res2 = recur(tp2, P) - - if res1.isEmpty && res2.isEmpty then - res1 - else if res2.isEmpty then - if isCheckDefinitelyFalse(tp1, P) then res2 - else res1 - else if res1.isEmpty then - if isCheckDefinitelyFalse(tp2, P) then res1 - else res2 - else - res1 + recur(tp1, P) && recur(tp2, P) - case _ => + case x => // always false test warnings are emitted elsewhere - X.classSymbol.exists && P.classSymbol.exists && - !X.classSymbol.asClass.mayHaveCommonChild(P.classSymbol.asClass) + TypeComparer.provablyDisjoint(x, tpe.derivedAppliedType(tycon, targs.map(_ => WildcardType))) || typeArgsTrivial(X, tpe) ||| i"its type arguments can't be determined from $X" } @@ -215,7 +168,7 @@ object TypeTestsCasts { if P.classSymbol.isLocal && foundClasses(X).exists(P.classSymbol.isInaccessibleChildOf) => // 8 i"it's a local class" case _ => "" - }) + } val res = recur(X.widen, replaceP(P)) From 4aa062be648043c9300e807daaf633c5026d8a24 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Tue, 9 May 2023 07:51:35 +0200 Subject: [PATCH 538/657] Add test --- .../neg-custom-args/isInstanceOf/i17435.scala | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/neg-custom-args/isInstanceOf/i17435.scala diff --git a/tests/neg-custom-args/isInstanceOf/i17435.scala b/tests/neg-custom-args/isInstanceOf/i17435.scala new file mode 100644 index 000000000000..e32149db3137 --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/i17435.scala @@ -0,0 +1,23 @@ +import scala.collection.mutable + +object Test: + type JsonPrimitive = String | Int | Double | Boolean | None.type + + type Rec[JA[_], JO[_], A] = A match + case JsonPrimitive => JsonPrimitive | JA[Rec[JA, JO, JsonPrimitive]] | JO[Rec[JA, JO, JsonPrimitive]] + case _ => A | JA[Rec[JA, JO, A]] | JO[Rec[JA, JO, A]] + + type Json = Rec[[A] =>> mutable.Buffer[A], [A] =>> mutable.Map[String, A], JsonPrimitive] + + type JsonObject = mutable.Map[String, Json] + + type JsonArray = mutable.Buffer[Json] + + def encode(x: Json): Int = x match + case str: String => 1 + case b: Boolean => 2 + case i: Int => 3 + case d: Double => 4 + case arr: JsonArray => 5 // error + case obj: JsonObject => 6 // error + case _ => 7 From 982ce3fc36535eb7cb96cf7d25694937b9005ced Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 10:47:47 +0200 Subject: [PATCH 539/657] Sync with the stable documentation branch (#17419) This pull request is syncing the main with changes from language-reference-stable. It was created automatically after bd9e0f7484167a07ae13c4e264a4cc2974aca223 by @bishabosha --------- Co-authored-by: Jamie Thompson --- .../reference/contextual/derivation-macro.md | 280 +++++++++--------- docs/_docs/reference/contextual/derivation.md | 148 +++++---- 2 files changed, 226 insertions(+), 202 deletions(-) diff --git a/docs/_docs/reference/contextual/derivation-macro.md b/docs/_docs/reference/contextual/derivation-macro.md index be7565616913..4b8dcffec846 100644 --- a/docs/_docs/reference/contextual/derivation-macro.md +++ b/docs/_docs/reference/contextual/derivation-macro.md @@ -4,15 +4,11 @@ title: "How to write a type class `derived` method using macros" nightlyOf: https://docs.scala-lang.org/scala3/reference/contextual/derivation-macro.html --- -In the main [derivation](./derivation.md) documentation page, we explained the -details behind `Mirror`s and type class derivation. Here we demonstrate how to -implement a type class `derived` method using macros only. We follow the same -example of deriving `Eq` instances and for simplicity we support a `Product` -type e.g., a case class `Person`. The low-level method we will use to implement -the `derived` method exploits quotes, splices of both expressions and types and -the `scala.quoted.Expr.summon` method which is the equivalent of -`summonFrom`. The former is suitable for use in a quote context, used within -macros. +In the main [derivation](./derivation.md) documentation page, we explained the details behind `Mirror`s and type class derivation. +Here we demonstrate how to implement a type class `derived` method using macros only. +We follow the same example of deriving `Eq` instances and for simplicity we support a `Product` type e.g., a case class `Person`. +The low-level technique that we will use to implement the `derived` method exploits quotes, splices of both expressions and types and the `scala.quoted.Expr.summon` method which is the equivalent of `scala.compiletime.summonFrom`. +The former is suitable for use in a quote context, used within macros. As in the original code, the type class definition is the same: @@ -21,185 +17,187 @@ trait Eq[T]: def eqv(x: T, y: T): Boolean ``` -we need to implement a method `Eq.derived` on the companion object of `Eq` that -produces a quoted instance for `Eq[T]`. Here is a possible signature, +We need to implement an inline method `Eq.derived` on the companion object of `Eq` that calls into a macro to produce a quoted instance for `Eq[T]`. +Here is a possible signature: + ```scala -given derived[T: Type](using Quotes): Expr[Eq[T]] +inline def derived[T]: Eq[T] = ${ derivedMacro[T] } + +def derivedMacro[T: Type](using Quotes): Expr[Eq[T]] = ??? ``` -and for comparison reasons we give the same signature we had with `inline`: +Note, that since a type is used in a subsequent macro compilation stage it will need to be lifted to a `quoted.Type` by using the corresponding context bound (seen in `derivedMacro`). + +For comparison, here is the signature of the inline `derived` method from the [main derivation page](./derivation.md): ```scala -inline given derived[T](using Mirror.Of[T]): Eq[T] = ??? +inline def derived[T](using m: Mirror.Of[T]): Eq[T] = ??? ``` -Note, that since a type is used in a subsequent stage it will need to be lifted -to a `Type` by using the corresponding context bound. Also, note that we can -summon the quoted `Mirror` inside the body of the `derived` thus we can omit it -from the signature. The body of the `derived` method is shown below: +Note that the macro-based `derived` signature does not have a `Mirror` parameter. +This is because we can summon the `Mirror` inside the body of `derivedMacro` thus we can omit it from the signature. + +One additional possibility with the body of `derivedMacro` here as opposed to the one with `inline` is that with macros it is simpler to create a fully optimised method body for `eqv`. +Let's say we wanted to derive an `Eq` instance for the following case class `Person`, +```scala +case class Person(name: String, age: Int) derives Eq +``` + +the equality check we are going to generate is the following: ```scala -given derived[T: Type](using Quotes): Expr[Eq[T]] = - import quotes.reflect.* +(x: Person, y: Person) => + summon[Eq[String]].eqv(x.productElement(0), y.productElement(0)) + && summon[Eq[Int]].eqv(x.productElement(1), y.productElement(1)) +``` + +> Note that it is possible, by using the [reflection API](../metaprogramming/reflection.md), to further optimise and directly reference the fields of `Person`, but for clear understanding we will only use quoted expressions. + +The code to generates this body can be seen in the `eqProductBody` method, shown here as part of the definition for the `derivedMacro` method: + + +```scala +def derivedMacro[T: Type](using Quotes): Expr[Eq[T]] = val ev: Expr[Mirror.Of[T]] = Expr.summon[Mirror.Of[T]].get ev match case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }} => - val elemInstances = summonAll[elementTypes] + val elemInstances = summonInstances[T, elementTypes] def eqProductBody(x: Expr[Product], y: Expr[Product])(using Quotes): Expr[Boolean] = { - elemInstances.zipWithIndex.foldLeft(Expr(true)) { - case (acc, ('{ $elem: Eq[t] }, index)) => - val indexExpr = Expr(index) - val e1 = '{ $x.productElement($indexExpr).asInstanceOf[t] } - val e2 = '{ $y.productElement($indexExpr).asInstanceOf[t] } - '{ $acc && $elem.eqv($e1, $e2) } - } + if elemInstances.isEmpty then + Expr(true) + else + elemInstances.zipWithIndex.map { + case ('{ $elem: Eq[t] }, index) => + val indexExpr = Expr(index) + val e1 = '{ $x.productElement($indexExpr).asInstanceOf[t] } + val e2 = '{ $y.productElement($indexExpr).asInstanceOf[t] } + '{ $elem.eqv($e1, $e2) } + }.reduce((acc, elem) => '{ $acc && $elem }) + end if } '{ eqProduct((x: T, y: T) => ${eqProductBody('x.asExprOf[Product], 'y.asExprOf[Product])}) } - // case for Mirror.ProductOf[T] - // ... + // case for Mirror.SumOf[T] ... ``` -Note, that in the `inline` case we can merely write -`summonAll[m.MirroredElemTypes]` inside the inline method but here, since -`Expr.summon` is required, we can extract the element types in a macro fashion. -Being inside a macro, our first reaction would be to write the code below. Since -the path inside the type argument is not stable this cannot be used: +Note, that in the version without macros, we can merely write `summonInstances[T, m.MirroredElemTypes]` inside the inline method but here, since `Expr.summon` is required, we can extract the element types in a macro fashion. +Being inside a macro, our first reaction would be to write the code below: ```scala '{ - summonAll[$m.MirroredElemTypes] + summonInstances[T, $m.MirroredElemTypes] } ``` -Instead we extract the tuple-type for element types using pattern matching over -quotes and more specifically of the refined type: +However, since the path inside the type argument is not stable this cannot be used. +Instead we extract the tuple-type for element types using pattern matching over quotes and more specifically of the refined type: ```scala case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }} => ... ``` -Shown below is the implementation of `summonAll` as a macro. We assume that -given instances for our primitive types exist. - -```scala -def summonAll[T: Type](using Quotes): List[Expr[Eq[_]]] = - Type.of[T] match - case '[String *: tpes] => '{ summon[Eq[String]] } :: summonAll[tpes] - case '[Int *: tpes] => '{ summon[Eq[Int]] } :: summonAll[tpes] - case '[tpe *: tpes] => derived[tpe] :: summonAll[tpes] - case '[EmptyTuple] => Nil -``` - -One additional difference with the body of `derived` here as opposed to the one -with `inline` is that with macros we need to synthesize the body of the code during the -macro-expansion time. That is the rationale behind the `eqProductBody` function. -Assuming that we calculate the equality of two `Person`s defined with a case -class that holds a name of type [`String`](https://scala-lang.org/api/3.x/scala/Predef$.html#String-0) -and an age of type `Int`, the equality check we want to generate is the following: - -```scala - true - && Eq[String].eqv(x.productElement(0),y.productElement(0)) - && Eq[Int].eqv(x.productElement(1), y.productElement(1)) -``` - -## Calling the derived method inside the macro +Shown below is the implementation of `summonInstances` as a macro, which for each type `elem` in the tuple type, calls +`deriveOrSummon[T, elem]`. -Following the rules in [Macros](../metaprogramming/metaprogramming.md) we create two methods. -One that hosts the top-level splice `eqv` and one that is the implementation. -Alternatively and what is shown below is that we can call the `eqv` method -directly. The `eqGen` can trigger the derivation. +To understand `deriveOrSummon`, consider that if `elem` derives from the parent `T` type, then it is a recursive derivation. +Recursive derivation usually happens for types such as `scala.collection.immutable.::`. If `elem` does not derive from `T`, then there must exist a contextual `Eq[elem]` instance. ```scala -extension [T](inline x: T) - inline def === (inline y: T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) - -inline given eqGen[T]: Eq[T] = ${ Eq.derived[T] } -``` - -Note, that we use inline method syntax and we can compare instance such as -`Sm(Person("Test", 23)) === Sm(Person("Test", 24))` for e.g., the following two -types: - -```scala -case class Person(name: String, age: Int) - -enum Opt[+T]: - case Sm(t: T) - case Nn +def summonInstances[T: Type, Elems: Type](using Quotes): List[Expr[Eq[?]]] = + Type.of[Elems] match + case '[elem *: elems] => deriveOrSummon[T, elem] :: summonInstances[T, elems] + case '[EmptyTuple] => Nil + +def deriveOrSummon[T: Type, Elem: Type](using Quotes): Expr[Eq[Elem]] = + Type.of[Elem] match + case '[T] => deriveRec[T, Elem] + case _ => '{ summonInline[Eq[Elem]] } + +def deriveRec[T: Type, Elem: Type](using Quotes): Expr[Eq[Elem]] = + Type.of[T] match + case '[Elem] => '{ error("infinite recursive derivation") } + case _ => derivedMacro[Elem] // recursive derivation ``` The full code is shown below: ```scala +import compiletime.* import scala.deriving.* import scala.quoted.* trait Eq[T]: - def eqv(x: T, y: T): Boolean + def eqv(x: T, y: T): Boolean object Eq: - given Eq[String] with - def eqv(x: String, y: String) = x == y - - given Eq[Int] with - def eqv(x: Int, y: Int) = x == y - - def eqProduct[T](body: (T, T) => Boolean): Eq[T] = - new Eq[T]: - def eqv(x: T, y: T): Boolean = body(x, y) - - def eqSum[T](body: (T, T) => Boolean): Eq[T] = - new Eq[T]: - def eqv(x: T, y: T): Boolean = body(x, y) - - def summonAll[T: Type](using Quotes): List[Expr[Eq[_]]] = - Type.of[T] match - case '[String *: tpes] => '{ summon[Eq[String]] } :: summonAll[tpes] - case '[Int *: tpes] => '{ summon[Eq[Int]] } :: summonAll[tpes] - case '[tpe *: tpes] => derived[tpe] :: summonAll[tpes] - case '[EmptyTuple] => Nil - - given derived[T: Type](using q: Quotes): Expr[Eq[T]] = - import quotes.reflect.* - - val ev: Expr[Mirror.Of[T]] = Expr.summon[Mirror.Of[T]].get - - ev match - case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }} => - val elemInstances = summonAll[elementTypes] - val eqProductBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => - elemInstances.zipWithIndex.foldLeft(Expr(true: Boolean)) { - case (acc, (elem, index)) => - val e1 = '{$x.asInstanceOf[Product].productElement(${Expr(index)})} - val e2 = '{$y.asInstanceOf[Product].productElement(${Expr(index)})} - - '{ $acc && $elem.asInstanceOf[Eq[Any]].eqv($e1, $e2) } - } - '{ eqProduct((x: T, y: T) => ${eqProductBody('x, 'y)}) } - - case '{ $m: Mirror.SumOf[T] { type MirroredElemTypes = elementTypes }} => - val elemInstances = summonAll[elementTypes] - val eqSumBody: (Expr[T], Expr[T]) => Expr[Boolean] = (x, y) => - val ordx = '{ $m.ordinal($x) } - val ordy = '{ $m.ordinal($y) } - - val elements = Expr.ofList(elemInstances) - '{ $ordx == $ordy && $elements($ordx).asInstanceOf[Eq[Any]].eqv($x, $y) } - - '{ eqSum((x: T, y: T) => ${eqSumBody('x, 'y)}) } - end derived + given Eq[String] with + def eqv(x: String, y: String) = x == y + + given Eq[Int] with + def eqv(x: Int, y: Int) = x == y + + def eqProduct[T](body: (T, T) => Boolean): Eq[T] = + new Eq[T]: + def eqv(x: T, y: T): Boolean = body(x, y) + + def eqSum[T](body: (T, T) => Boolean): Eq[T] = + new Eq[T]: + def eqv(x: T, y: T): Boolean = body(x, y) + + def summonInstances[T: Type, Elems: Type](using Quotes): List[Expr[Eq[?]]] = + Type.of[Elems] match + case '[elem *: elems] => deriveOrSummon[T, elem] :: summonInstances[T, elems] + case '[EmptyTuple] => Nil + + def deriveOrSummon[T: Type, Elem: Type](using Quotes): Expr[Eq[Elem]] = + Type.of[Elem] match + case '[T] => deriveRec[T, Elem] + case _ => '{ summonInline[Eq[Elem]] } + + def deriveRec[T: Type, Elem: Type](using Quotes): Expr[Eq[Elem]] = + Type.of[T] match + case '[Elem] => '{ error("infinite recursive derivation") } + case _ => derivedMacro[Elem] // recursive derivation + + inline def derived[T]: Eq[T] = ${ derivedMacro[T] } + + def derivedMacro[T: Type](using Quotes): Expr[Eq[T]] = + + val ev: Expr[Mirror.Of[T]] = Expr.summon[Mirror.Of[T]].get + + ev match + case '{ $m: Mirror.ProductOf[T] { type MirroredElemTypes = elementTypes }} => + val elemInstances = summonInstances[T, elementTypes] + def eqProductBody(x: Expr[Product], y: Expr[Product])(using Quotes): Expr[Boolean] = { + if elemInstances.isEmpty then + Expr(true) + else + elemInstances.zipWithIndex.map { + case ('{ $elem: Eq[t] }, index) => + val indexExpr = Expr(index) + val e1 = '{ $x.productElement($indexExpr).asInstanceOf[t] } + val e2 = '{ $y.productElement($indexExpr).asInstanceOf[t] } + '{ $elem.eqv($e1, $e2) } + }.reduce((acc, elem) => '{ $acc && $elem }) + end if + } + '{ eqProduct((x: T, y: T) => ${eqProductBody('x.asExprOf[Product], 'y.asExprOf[Product])}) } + + case '{ $m: Mirror.SumOf[T] { type MirroredElemTypes = elementTypes }} => + val elemInstances = summonInstances[T, elementTypes] + val elements = Expr.ofList(elemInstances) + + def eqSumBody(x: Expr[T], y: Expr[T])(using Quotes): Expr[Boolean] = + val ordx = '{ $m.ordinal($x) } + val ordy = '{ $m.ordinal($y) } + '{ $ordx == $ordy && $elements($ordx).asInstanceOf[Eq[Any]].eqv($x, $y) } + + '{ eqSum((x: T, y: T) => ${eqSumBody('x, 'y)}) } + end derivedMacro end Eq - -object Macro3: - extension [T](inline x: T) - inline def === (inline y: T)(using eq: Eq[T]): Boolean = eq.eqv(x, y) - - inline given eqGen[T]: Eq[T] = ${ Eq.derived[T] } ``` diff --git a/docs/_docs/reference/contextual/derivation.md b/docs/_docs/reference/contextual/derivation.md index 853f7868aa9a..66d0cf3fdf38 100644 --- a/docs/_docs/reference/contextual/derivation.md +++ b/docs/_docs/reference/contextual/derivation.md @@ -312,9 +312,8 @@ worked out example of such a library, see [Shapeless 3](https://github.com/miles ## How to write a type class `derived` method using low level mechanisms -The low-level method we will use to implement a type class `derived` method in this example exploits three new -type-level constructs in Scala 3: inline methods, inline matches, and implicit searches via `summonInline` or `summonFrom`. Given this definition of the -`Eq` type class, +The low-level method we will use to implement a type class `derived` method in this example exploits three new type-level constructs in Scala 3: inline methods, inline matches, and implicit searches via `summonInline` or `summonFrom`. +Given this definition of the `Eq` type class, ```scala trait Eq[T]: @@ -322,77 +321,98 @@ trait Eq[T]: ``` we need to implement a method `Eq.derived` on the companion object of `Eq` that produces a given instance for `Eq[T]` given -a `Mirror[T]`. Here is a possible implementation, +a `Mirror[T]`. +Here is a possible implementation, ```scala import scala.deriving.Mirror -inline given derived[T](using m: Mirror.Of[T]): Eq[T] = - val elemInstances = summonAll[m.MirroredElemTypes] // (1) - inline m match // (2) +inline def derived[T](using m: Mirror.Of[T]): Eq[T] = + lazy val elemInstances = summonInstances[T, m.MirroredElemTypes] // (1) + inline m match // (2) case s: Mirror.SumOf[T] => eqSum(s, elemInstances) case p: Mirror.ProductOf[T] => eqProduct(p, elemInstances) ``` -Note that `derived` is defined as an `inline` given. This means that the method will be expanded at -call sites (for instance the compiler generated instance definitions in the companion objects of ADTs which have a -`derived Eq` clause), and also that it can be used recursively if necessary, to compute instances for children. +Note that `derived` is defined as an `inline def`. +This means that the method will be inlined at all call sites (for instance the compiler generated instance definitions in the companion objects of ADTs which have a `deriving Eq` clause). -The body of this method (1) first materializes the `Eq` instances for all the child types of type the instance is -being derived for. This is either all the branches of a sum type or all the fields of a product type. The -implementation of `summonAll` is `inline` and uses Scala 3's `summonInline` construct to collect the instances as a -`List`, +> Inlining of complex code is potentially expensive if overused (meaning slower compile times) so we should be careful to limit how many times `derived` is called for the same type. +> For example, when computing an instance for a sum type, it may be necessary to call `derived` recursively to compute an instance for a one of its child cases. +> That child case may in turn be a product type, that declares a field referring back to the parent sum type. +> To compute the instance for this field, we should not call `derived` recursively, but instead summon from the context. +> Typically the found given instance will be the root given instance that initially called `derived`. + +The body of `derived` (1) first materializes the `Eq` instances for all the child types of type the instance is being derived for. +This is either all the branches of a sum type or all the fields of a product type. +The implementation of `summonInstances` is `inline` and uses Scala 3's `summonInline` construct to collect the instances as a `List`, ```scala -inline def summonAll[T <: Tuple]: List[Eq[_]] = - inline erasedValue[T] match +inline def summonInstances[T, Elems <: Tuple]: List[Eq[?]] = + inline erasedValue[Elems] match + case _: (elem *: elems) => deriveOrSummon[T, elem] :: summonInstances[T, elems] case _: EmptyTuple => Nil - case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts] + +inline def deriveOrSummon[T, Elem]: Eq[Elem] = + inline erasedValue[Elem] match + case _: T => deriveRec[T, Elem] + case _ => summonInline[Eq[Elem]] + +inline def deriveRec[T, Elem]: Eq[Elem] = + inline erasedValue[T] match + case _: Elem => error("infinite recursive derivation") + case _ => Eq.derived[Elem](using summonInline[Mirror.Of[Elem]]) // recursive derivation ``` with the instances for children in hand the `derived` method uses an `inline match` to dispatch to methods which can -construct instances for either sums or products (2). Note that because `derived` is `inline` the match will be -resolved at compile-time and only the left-hand side of the matching case will be inlined into the generated code with -types refined as revealed by the match. +construct instances for either sums or products (2). +Note that because `derived` is `inline` the match will be resolved at compile-time and only the right-hand side of the matching case will be inlined into the generated code with types refined as revealed by the match. -In the sum case, `eqSum`, we use the runtime `ordinal` values of the arguments to `eqv` to first check if the two -values are of the same subtype of the ADT (3) and then, if they are, to further test for equality based on the `Eq` -instance for the appropriate ADT subtype using the auxiliary method `check` (4). +In the sum case, `eqSum`, we use the runtime `ordinal` values of the arguments to `eqv` to first check if the two values are of the same subtype of the ADT (3) and then, if they are, to further test for equality based on the `Eq` instance for the appropriate ADT subtype using the auxiliary method `check` (4). ```scala import scala.deriving.Mirror -def eqSum[T](s: Mirror.SumOf[T], elems: List[Eq[_]]): Eq[T] = +def eqSum[T](s: Mirror.SumOf[T], elems: => List[Eq[?]]): Eq[T] = new Eq[T]: def eqv(x: T, y: T): Boolean = val ordx = s.ordinal(x) // (3) - (s.ordinal(y) == ordx) && check(elems(ordx))(x, y) // (4) + (s.ordinal(y) == ordx) && check(x, y, elems(ordx)) // (4) ``` -In the product case, `eqProduct` we test the runtime values of the arguments to `eqv` for equality as products based -on the `Eq` instances for the fields of the data type (5), +In the product case, `eqProduct` we test the runtime values of the arguments to `eqv` for equality as products based on the `Eq` instances for the fields of the data type (5), ```scala import scala.deriving.Mirror -def eqProduct[T](p: Mirror.ProductOf[T], elems: List[Eq[_]]): Eq[T] = +def eqProduct[T](p: Mirror.ProductOf[T], elems: => List[Eq[?]]): Eq[T] = new Eq[T]: def eqv(x: T, y: T): Boolean = - iterator(x).zip(iterator(y)).zip(elems.iterator).forall { // (5) - case ((x, y), elem) => check(elem)(x, y) - } + iterable(x).lazyZip(iterable(y)).lazyZip(elems).forall(check) ``` +Both `eqSum` and `eqProduct` have a by-name parameter `elems`, because the argument passed is the reference to the lazy `elemInstances` value. + Pulling this all together we have the following complete implementation, ```scala import scala.deriving.* -import scala.compiletime.{erasedValue, summonInline} +import scala.compiletime.{error, erasedValue, summonInline} -inline def summonAll[T <: Tuple]: List[Eq[_]] = - inline erasedValue[T] match +inline def summonInstances[T, Elems <: Tuple]: List[Eq[?]] = + inline erasedValue[Elems] match + case _: (elem *: elems) => deriveOrSummon[T, elem] :: summonInstances[T, elems] case _: EmptyTuple => Nil - case _: (t *: ts) => summonInline[Eq[t]] :: summonAll[ts] + +inline def deriveOrSummon[T, Elem]: Eq[Elem] = + inline erasedValue[Elem] match + case _: T => deriveRec[T, Elem] + case _ => summonInline[Eq[Elem]] + +inline def deriveRec[T, Elem]: Eq[Elem] = + inline erasedValue[T] match + case _: Elem => error("infinite recursive derivation") + case _ => Eq.derived[Elem](using summonInline[Mirror.Of[Elem]]) // recursive derivation trait Eq[T]: def eqv(x: T, y: T): Boolean @@ -401,26 +421,25 @@ object Eq: given Eq[Int] with def eqv(x: Int, y: Int) = x == y - def check(elem: Eq[_])(x: Any, y: Any): Boolean = + def check(x: Any, y: Any, elem: Eq[?]): Boolean = elem.asInstanceOf[Eq[Any]].eqv(x, y) - def iterator[T](p: T) = p.asInstanceOf[Product].productIterator + def iterable[T](p: T): Iterable[Any] = new AbstractIterable[Any]: + def iterator: Iterator[Any] = p.asInstanceOf[Product].productIterator - def eqSum[T](s: Mirror.SumOf[T], elems: => List[Eq[_]]): Eq[T] = + def eqSum[T](s: Mirror.SumOf[T], elems: => List[Eq[?]]): Eq[T] = new Eq[T]: def eqv(x: T, y: T): Boolean = val ordx = s.ordinal(x) - (s.ordinal(y) == ordx) && check(elems(ordx))(x, y) + (s.ordinal(y) == ordx) && check(x, y, elems(ordx)) - def eqProduct[T](p: Mirror.ProductOf[T], elems: => List[Eq[_]]): Eq[T] = + def eqProduct[T](p: Mirror.ProductOf[T], elems: => List[Eq[?]]): Eq[T] = new Eq[T]: def eqv(x: T, y: T): Boolean = - iterator(x).zip(iterator(y)).zip(elems.iterator).forall { - case ((x, y), elem) => check(elem)(x, y) - } + iterable(x).lazyZip(iterable(y)).lazyZip(elems).forall(check) - inline given derived[T](using m: Mirror.Of[T]): Eq[T] = - lazy val elemInstances = summonAll[m.MirroredElemTypes] + inline def derived[T](using m: Mirror.Of[T]): Eq[T] = + lazy val elemInstances = summonInstances[T, m.MirroredElemTypes] inline m match case s: Mirror.SumOf[T] => eqSum(s, elemInstances) case p: Mirror.ProductOf[T] => eqProduct(p, elemInstances) @@ -430,32 +449,39 @@ end Eq we can test this relative to a simple ADT like so, ```scala -enum Opt[+T] derives Eq: - case Sm(t: T) - case Nn +enum Lst[+T] derives Eq: + case Cns(t: T, ts: Lst[T]) + case Nl + +extension [T](t: T) def ::(ts: Lst[T]): Lst[T] = Lst.Cns(t, ts) @main def test(): Unit = - import Opt.* - val eqoi = summon[Eq[Opt[Int]]] - assert(eqoi.eqv(Sm(23), Sm(23))) - assert(!eqoi.eqv(Sm(23), Sm(13))) - assert(!eqoi.eqv(Sm(23), Nn)) + import Lst.* + val eqoi = summon[Eq[Lst[Int]]] + assert(eqoi.eqv(23 :: 47 :: Nl, 23 :: 47 :: Nl)) + assert(!eqoi.eqv(23 :: Nl, 7 :: Nl)) + assert(!eqoi.eqv(23 :: Nl, Nl)) ``` -In this case the code that is generated by the inline expansion for the derived `Eq` instance for `Opt` looks like the +In this case the code that is generated by the inline expansion for the derived `Eq` instance for `Lst` looks like the following, after a little polishing, ```scala -given derived$Eq[T](using eqT: Eq[T]): Eq[Opt[T]] = - eqSum( - summon[Mirror[Opt[T]]], +given derived$Eq[T](using eqT: Eq[T]): Eq[Lst[T]] = + eqSum(summon[Mirror.Of[Lst[T]]], {/* cached lazily */ List( - eqProduct(summon[Mirror[Sm[T]]], List(summon[Eq[T]])), - eqProduct(summon[Mirror[Nn.type]], Nil) + eqProduct(summon[Mirror.Of[Cns[T]]], {/* cached lazily */ + List(summon[Eq[T]], summon[Eq[Lst[T]]]) + }), + eqProduct(summon[Mirror.Of[Nl.type]], {/* cached lazily */ + Nil + }) ) - ) + }) ``` +The `lazy` modifier on `elemInstances` is necessary for preventing infinite recursion in the derived instance for recursive types such as `Lst`. + Alternative approaches can be taken to the way that `derived` methods can be defined. For example, more aggressively inlined variants using Scala 3 macros, whilst being more involved for type class authors to write than the example above, can produce code for type classes like `Eq` which eliminate all the abstraction artefacts (eg. the `Lists` of @@ -469,7 +495,7 @@ given eqSum[A](using inst: => K0.CoproductInstances[Eq, A]): Eq[A] with [t] => (eqt: Eq[t], t0: t, t1: t) => eqt.eqv(t0, t1) ) -given eqProduct[A](using inst: K0.ProductInstances[Eq, A]): Eq[A] with +given eqProduct[A](using inst: => K0.ProductInstances[Eq, A]): Eq[A] with def eqv(x: A, y: A): Boolean = inst.foldLeft2(x, y)(true: Boolean)( [t] => (acc: Boolean, eqt: Eq[t], t0: t, t1: t) => Complete(!eqt.eqv(t0, t1))(false)(true) From 46cd9c9bf0d67a338f3fc824bd1fa23aa3af1f93 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 May 2023 12:59:39 +0200 Subject: [PATCH 540/657] Remove redundant case override in TreeTypeMap This case is handled in the same way by TreeMap. --- compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala | 5 ----- compiler/src/dotty/tools/dotc/ast/Trees.scala | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index faeafae97f5e..955892b2ae22 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -146,11 +146,6 @@ class TreeTypeMap( val bind1 = tmap.transformSub(bind) val expr1 = tmap.transform(expr) cpy.Labeled(labeled)(bind1, expr1) - case tree @ Hole(_, _, args, content, tpt) => - val args1 = args.mapConserve(transform) - val content1 = transform(content) - val tpt1 = transform(tpt) - cpy.Hole(tree)(args = args1, content = content1, tpt = tpt1) case tree1 => super.transform(tree1) } diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 77583a9a77e2..704425fbf25b 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1563,8 +1563,8 @@ object Trees { cpy.Quote(tree)(transform(body)(using quoteContext)) case tree @ Splice(expr) => cpy.Splice(tree)(transform(expr)(using spliceContext)) - case tree @ Hole(_, _, args, content, tpt) => - cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) + case tree @ Hole(isTerm, idx, args, content, tpt) => + cpy.Hole(tree)(isTerm, idx, transform(args), transform(content), transform(tpt)) case _ => transformMoreCases(tree) } From 50d630312bbdb8a0926da831a6b2a2d2f2ab531c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 May 2023 13:05:18 +0200 Subject: [PATCH 541/657] Remove unnecessary isTermHole method from Hole --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 7 +++---- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 ++-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../src/dotty/tools/dotc/printing/RefinedPrinter.scala | 4 ++-- compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala | 4 ++-- compiler/src/dotty/tools/dotc/transform/TreeChecker.scala | 6 +++--- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 704425fbf25b..61009f48a8f0 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1033,16 +1033,15 @@ object Trees { /** Tree that replaces a level 1 splices in pickled (level 0) quotes. * It is only used when picking quotes (will never be in a TASTy file). * - * @param isTermHole If this hole is a term, otherwise it is a type hole. + * @param isTerm If this hole is a term, otherwise it is a type hole. * @param idx The index of the hole in it's enclosing level 0 quote. * @param args The arguments of the splice to compute its content * @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle. * @param tpt Type of the hole */ - case class Hole[+T <: Untyped](isTermHole: Boolean, idx: Int, args: List[Tree[T]], content: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] { + case class Hole[+T <: Untyped](override val isTerm: Boolean, idx: Int, args: List[Tree[T]], content: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] { type ThisTree[+T <: Untyped] <: Hole[T] - override def isTerm: Boolean = isTermHole - override def isType: Boolean = !isTermHole + override def isType: Boolean = !isTerm } def flatten[T <: Untyped](trees: List[Tree[T]]): List[Tree[T]] = { diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 3a957c8f4612..d26735bdeb05 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -397,8 +397,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Throw(expr: Tree)(using Context): Tree = ref(defn.throwMethod).appliedTo(expr) - def Hole(isTermHole: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = - ta.assignType(untpd.Hole(isTermHole, idx, args, content, tpt), tpt) + def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = + ta.assignType(untpd.Hole(isTerm, idx, args, content, tpt), tpt) // ------ Making references ------------------------------------------------------ diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 30f58fba44ec..895423638eb2 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -424,7 +424,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def Export(expr: Tree, selectors: List[ImportSelector])(implicit src: SourceFile): Export = new Export(expr, selectors) def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats) def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot) - def Hole(isTermHole: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(implicit src: SourceFile): Hole = new Hole(isTermHole, idx, args, content, tpt) + def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(implicit src: SourceFile): Hole = new Hole(isTerm, idx, args, content, tpt) // ------ Additional creation methods for untyped only ----------------- diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 93de33778750..6cad9b500940 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -724,8 +724,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case Splice(expr) => val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") - case Hole(isTermHole, idx, args, content, tpt) => - val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") + case Hole(isTerm, idx, args, content, tpt) => + val (prefix, postfix) = if isTerm then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") val contentText = toTextGlobal(content) val tptText = toTextGlobal(tpt) diff --git a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala index 43e2aae4c58c..89efcb4272ec 100644 --- a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala @@ -100,9 +100,9 @@ object PickledQuotes { private def spliceTerms(tree: Tree, typeHole: TypeHole, termHole: ExprHole)(using Context): Tree = { def evaluateHoles = new TreeMap { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match { - case Hole(isTermHole, idx, args, _, _) => + case Hole(isTerm, idx, args, _, _) => inContext(SpliceScope.contextWithNewSpliceScope(tree.sourcePos)) { - if isTermHole then + if isTerm then val quotedExpr = termHole match case ExprHole.V1(evalHole) => evalHole.nn.apply(idx, reifyExprHoleV1Args(args), QuotesImpl()) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index f9240d6091c4..6d066ff4dc76 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -658,7 +658,7 @@ object TreeChecker { super.typedPackageDef(tree) override def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = { - val tree1 @ Hole(isTermHole, _, args, content, tpt) = super.typedHole(tree, pt): @unchecked + val tree1 @ Hole(isTerm, _, args, content, tpt) = super.typedHole(tree, pt): @unchecked // Check that we only add the captured type `T` instead of a more complex type like `List[T]`. // If we have `F[T]` with captured `F` and `T`, we should list `F` and `T` separately in the args. @@ -666,7 +666,7 @@ object TreeChecker { assert(arg.isTerm || arg.tpe.isInstanceOf[TypeRef], "Expected TypeRef in Hole type args but got: " + arg.tpe) // Check result type of the hole - if isTermHole then assert(tpt.typeOpt <:< pt) + if isTerm then assert(tpt.typeOpt <:< pt) else assert(tpt.typeOpt =:= pt) // Check that the types of the args conform to the types of the contents of the hole @@ -682,7 +682,7 @@ object TreeChecker { else defn.QuotedTypeClass.typeRef.appliedTo(arg.typeOpt.widenTermRefExpr) } val expectedResultType = - if isTermHole then defn.QuotedExprClass.typeRef.appliedTo(tpt.typeOpt) + if isTerm then defn.QuotedExprClass.typeRef.appliedTo(tpt.typeOpt) else defn.QuotedTypeClass.typeRef.appliedTo(tpt.typeOpt) val contextualResult = defn.FunctionOf(List(defn.QuotesClass.typeRef), expectedResultType, isContextual = true) From c909024fa54c225007a29eb9d539695b86405fbd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 May 2023 08:39:03 +0200 Subject: [PATCH 542/657] Only check newVal/newMethod privateWithin on -Xcheck-macros Closes #17432 --- .../src/scala/quoted/runtime/impl/QuotesImpl.scala | 10 ++++++---- tests/{neg => neg-macros}/i17351/Macro_1.scala | 0 tests/{neg => neg-macros}/i17351/Test_2.scala | 0 3 files changed, 6 insertions(+), 4 deletions(-) rename tests/{neg => neg-macros}/i17351/Macro_1.scala (100%) rename tests/{neg => neg-macros}/i17351/Test_2.scala (100%) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index df7ec3da51af..4231781e2754 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2540,13 +2540,15 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def newMethod(owner: Symbol, name: String, tpe: TypeRepr): Symbol = newMethod(owner, name, tpe, Flags.EmptyFlags, noSymbol) def newMethod(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol = - assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`") + xCheckMacroAssert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`") + val privateWithin1 = if privateWithin.isTerm then Symbol.noSymbol else privateWithin checkValidFlags(flags.toTermFlags, Flags.validMethodFlags) - dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin) + dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin1) def newVal(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol = - assert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`") + xCheckMacroAssert(!privateWithin.exists || privateWithin.isType, "privateWithin must be a type symbol or `Symbol.noSymbol`") + val privateWithin1 = if privateWithin.isTerm then Symbol.noSymbol else privateWithin checkValidFlags(flags.toTermFlags, Flags.validValFlags) - dotc.core.Symbols.newSymbol(owner, name.toTermName, flags, tpe, privateWithin) + dotc.core.Symbols.newSymbol(owner, name.toTermName, flags, tpe, privateWithin1) def newBind(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol = checkValidFlags(flags.toTermFlags, Flags.validBindFlags) dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Case, tpe) diff --git a/tests/neg/i17351/Macro_1.scala b/tests/neg-macros/i17351/Macro_1.scala similarity index 100% rename from tests/neg/i17351/Macro_1.scala rename to tests/neg-macros/i17351/Macro_1.scala diff --git a/tests/neg/i17351/Test_2.scala b/tests/neg-macros/i17351/Test_2.scala similarity index 100% rename from tests/neg/i17351/Test_2.scala rename to tests/neg-macros/i17351/Test_2.scala From 968ebab3a525555fe50054fc5d6f504efe6db73c Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 4 May 2023 10:52:01 +0200 Subject: [PATCH 543/657] Add test for BlogParser and Can parse a String --- .../tools/scaladoc/site/BlogParser.scala | 33 +++++++++---------- .../tools/scaladoc/site/BlogParserTest.scala | 19 +++++++++++ 2 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 scaladoc/test/dotty/tools/scaladoc/site/BlogParserTest.scala diff --git a/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala b/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala index 6fd3d3db0c8d..68e709a339b2 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/BlogParser.scala @@ -1,29 +1,26 @@ -package dotty.tools.scaladoc -package site +package dotty.tools.scaladoc.site import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.fasterxml.jackson.databind.DeserializationFeature import java.io.File -import scala.beans._ +import scala.beans.{BooleanBeanProperty, BeanProperty} +import scala.util.Try case class BlogConfig( - @BeanProperty var input: String, - @BeanProperty var output: String, - @BooleanBeanProperty var hidden: Boolean + @BeanProperty input: String, + @BeanProperty output: String, + @BooleanBeanProperty hidden: Boolean ): - def this() = this(null, null, false) + def this() = this(null, null, false) object BlogParser: - def readYml(root: File): BlogConfig = - val ymlFile = root.toPath - .resolve("blog.yml") - .toFile + def readYml(content: File | String): BlogConfig = + val mapper = ObjectMapper(YAMLFactory()) + .findAndRegisterModules() - if ymlFile.exists then - val mapper = new ObjectMapper(new YAMLFactory()) - mapper.findAndRegisterModules(); - - val blogConfig: BlogConfig = mapper.readValue(ymlFile, classOf[BlogConfig]) - blogConfig - else new BlogConfig + content match + case f: File => + val ymlFile = f.toPath.resolve("blog.yml").toFile + if ymlFile.exists then mapper.readValue(ymlFile, classOf[BlogConfig]) else new BlogConfig + case s: String => Try(mapper.readValue(s, classOf[BlogConfig])).getOrElse(new BlogConfig) diff --git a/scaladoc/test/dotty/tools/scaladoc/site/BlogParserTest.scala b/scaladoc/test/dotty/tools/scaladoc/site/BlogParserTest.scala new file mode 100644 index 000000000000..e27c257c8e4a --- /dev/null +++ b/scaladoc/test/dotty/tools/scaladoc/site/BlogParserTest.scala @@ -0,0 +1,19 @@ +package dotty.tools.scaladoc +package site + +import org.junit.Test +import org.junit.Assert._ + +class BlogParserTest: + + private val blogConfig = + """input: blog + |output: blog + |hidden: false + |""".stripMargin + + @Test + def loadBlog(): Unit = assertEquals( + BlogConfig("blog", "blog", false), + BlogParser.readYml(blogConfig) + ) \ No newline at end of file From 156ec83c59dbed4b1d8e4adb118d82c0de1e1d8d Mon Sep 17 00:00:00 2001 From: odersky Date: Tue, 9 May 2023 17:28:38 +0200 Subject: [PATCH 544/657] Address review comments --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- tests/neg-custom-args/boxmap.scala | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 33bd46f619aa..2f51af8549ae 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1673,7 +1673,7 @@ object Parsers { infixOps(t, canStartInfixTypeTokens, refinedTypeFn, Location.ElseWhere, ParseKind.Type, isOperator = !followingIsVararg() && !isPureArrow) - /** RefinedType ::= WithType {[nl] (Refinement} [`^` CaptureSet] + /** RefinedType ::= WithType {[nl] Refinement} [`^` CaptureSet] */ val refinedTypeFn: Location => Tree = _ => refinedType() diff --git a/tests/neg-custom-args/boxmap.scala b/tests/neg-custom-args/boxmap.scala index cdc8253acf2a..1696ac3505e4 100644 --- a/tests/neg-custom-args/boxmap.scala +++ b/tests/neg-custom-args/boxmap.scala @@ -16,6 +16,6 @@ def test[A <: Top, B <: Top] = def lazymap[A <: Top, B <: Top](b: Box[A])(f: A => B) = () => b[Box[B]]((x: A) => box(f(x))) val x0: (b: Box[A]) -> (f: A => B) -> (() -> Box[B]) = lazymap[A, B] // error - val x: (b: Box[A]) -> (f: A => B) ->{b, f} (() -> Box[B]) = lazymap[A, B] // works - val y: (b: Box[A]) -> (f: A => B) ->{cap} (() -> Box[B]) = lazymap[A, B] // works + val x: (b: Box[A]) -> (f: A => B) -> (() ->{b, f} Box[B]) = lazymap[A, B] // works + val y: (b: Box[A]) -> (f: A => B) -> (() ->{cap} Box[B]) = lazymap[A, B] // works () From da01fd7514aee6ac09407088e0dbfea7c82692b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 9 May 2023 19:39:08 +0200 Subject: [PATCH 545/657] Do not run scaladoc tests on queue branches (#16923) Instead, run it on the `merge_group` trigger. This is a very minor change that allows us for easier filtering of actions. --- .github/workflows/scaladoc.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/scaladoc.yaml b/.github/workflows/scaladoc.yaml index 9ccbe34788ce..3108f2b94562 100644 --- a/.github/workflows/scaladoc.yaml +++ b/.github/workflows/scaladoc.yaml @@ -4,9 +4,11 @@ on: push: branches-ignore: - 'language-reference-stable' + - 'gh-readonly-queue/**' pull_request: branches-ignore: - 'language-reference-stable' + merge_group: permissions: contents: read @@ -15,7 +17,8 @@ jobs: env: AZURE_STORAGE_SAS_TOKEN: ${{ secrets.AZURE_STORAGE_SAS_TOKEN }} runs-on: ubuntu-latest - if: "( github.event_name == 'pull_request' + if: "github.event_name == 'merge_group' + || ( github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') && !contains(github.event.pull_request.body, '[skip docs]') ) From 68e720ced5685fdc26c7d887c33c1c3aed269e15 Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Sun, 5 Mar 2023 19:12:47 +0100 Subject: [PATCH 546/657] Support records in JavaParsers This is a port of scala/scala#9551 --- .../src/dotty/tools/dotc/core/StdNames.scala | 7 ++ .../tools/dotc/parsing/JavaParsers.scala | 82 +++++++++++++++++-- .../dotty/tools/dotc/parsing/JavaTokens.scala | 3 + tests/pos/java-records/FromScala.scala | 15 ++++ tests/pos/java-records/R1.java | 9 ++ 5 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 tests/pos/java-records/FromScala.scala create mode 100644 tests/pos/java-records/R1.java diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 27e97a92b48e..c54a60f5a8fa 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -204,6 +204,7 @@ object StdNames { final val Null: N = "Null" final val Object: N = "Object" final val FromJavaObject: N = "" + final val Record: N = "Record" final val Product: N = "Product" final val PartialFunction: N = "PartialFunction" final val PrefixType: N = "PrefixType" @@ -912,6 +913,10 @@ object StdNames { final val VOLATILEkw: N = kw("volatile") final val WHILEkw: N = kw("while") + final val RECORDid: N = "record" + final val VARid: N = "var" + final val YIELDid: N = "yield" + final val BoxedBoolean: N = "java.lang.Boolean" final val BoxedByte: N = "java.lang.Byte" final val BoxedCharacter: N = "java.lang.Character" @@ -944,6 +949,8 @@ object StdNames { final val JavaSerializable: N = "java.io.Serializable" } + + class JavaTermNames extends JavaNames[TermName] { protected def fromString(s: String): TermName = termName(s) } diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index daeebcbcc17c..71aeee05fe10 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -20,7 +20,9 @@ import StdNames._ import reporting._ import dotty.tools.dotc.util.SourceFile import util.Spans._ + import scala.collection.mutable.ListBuffer +import scala.collection.immutable.ListMap object JavaParsers { @@ -96,8 +98,12 @@ object JavaParsers { def javaLangDot(name: Name): Tree = Select(javaDot(nme.lang), name) + /** Tree representing `java.lang.Object` */ def javaLangObject(): Tree = javaLangDot(tpnme.Object) + /** Tree representing `java.lang.Record` */ + def javaLangRecord(): Tree = javaLangDot(tpnme.Record) + def arrayOf(tpt: Tree): AppliedTypeTree = AppliedTypeTree(scalaDot(tpnme.Array), List(tpt)) @@ -555,6 +561,14 @@ object JavaParsers { def definesInterface(token: Int): Boolean = token == INTERFACE || token == AT + /** If the next token is the identifier "record", convert it into the RECORD token. + * This makes it easier to handle records in various parts of the code, + * in particular when a `parentToken` is passed to some functions. + */ + def adaptRecordIdentifier(): Unit = + if in.token == IDENTIFIER && in.name == jnme.RECORDid then + in.token = RECORD + def termDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = { val inInterface = definesInterface(parentToken) val tparams = if (in.token == LT) typeParams(Flags.JavaDefined | Flags.Param) else List() @@ -581,6 +595,16 @@ object JavaParsers { TypeTree(), methodBody()).withMods(mods) } } + } else if (in.token == LBRACE && rtptName != nme.EMPTY && parentToken == RECORD) { + /* + record RecordName(T param1, ...) { + RecordName { // <- here + // methodBody + } + } + */ + methodBody() + Nil } else { var mods1 = mods @@ -717,12 +741,11 @@ object JavaParsers { ValDef(name, tpt2, if (mods.is(Flags.Param)) EmptyTree else unimplementedExpr).withMods(mods1) } - def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match { - case CLASS | ENUM | INTERFACE | AT => - typeDecl(start, if (definesInterface(parentToken)) mods | Flags.JavaStatic else mods) + def memberDecl(start: Offset, mods: Modifiers, parentToken: Int, parentTParams: List[TypeDef]): List[Tree] = in.token match + case CLASS | ENUM | RECORD | INTERFACE | AT => + typeDecl(start, if definesInterface(parentToken) then mods | Flags.JavaStatic else mods) case _ => termDecl(start, mods, parentToken, parentTParams) - } def makeCompanionObject(cdef: TypeDef, statics: List[Tree]): Tree = atSpan(cdef.span) { @@ -804,6 +827,49 @@ object JavaParsers { addCompanionObject(statics, cls) } + def recordDecl(start: Offset, mods: Modifiers): List[Tree] = + accept(RECORD) + val nameOffset = in.offset + val name = identForType() + val tparams = typeParams() + val header = formalParams() + val superclass = javaLangRecord() // records always extend java.lang.Record + val interfaces = interfacesOpt() // records may implement interfaces + val (statics, body) = typeBody(RECORD, name, tparams) + + // We need to generate accessors for every param, if no method with the same name is already defined + + var fieldsByName = header.map(v => (v.name, (v.tpt, v.mods.annotations))).to(ListMap) + + for case DefDef(name, paramss, _, _) <- body + if paramss.isEmpty && fieldsByName.contains(name) + do + fieldsByName -= name + end for + + val accessors = + (for (name, (tpt, annots)) <- fieldsByName yield + DefDef(name, Nil, tpt, unimplementedExpr) + .withMods(Modifiers(Flags.JavaDefined | Flags.Method | Flags.Synthetic)) + ).toList + + // generate the canonical constructor + val canonicalConstructor = makeConstructor(header, tparams) + + // return the trees + val recordTypeDef = atSpan(start, nameOffset) { + TypeDef(name, + makeTemplate( + parents = superclass :: interfaces, + stats = canonicalConstructor :: accessors ::: body, + tparams = tparams, + false + ) + ) + } + addCompanionObject(statics, recordTypeDef) + end recordDecl + def interfaceDecl(start: Offset, mods: Modifiers): List[Tree] = { accept(INTERFACE) val nameOffset = in.offset @@ -846,7 +912,8 @@ object JavaParsers { else if (in.token == SEMI) in.nextToken() else { - if (in.token == ENUM || definesInterface(in.token)) mods |= Flags.JavaStatic + adaptRecordIdentifier() + if (in.token == ENUM || in.token == RECORD || definesInterface(in.token)) mods |= Flags.JavaStatic val decls = memberDecl(start, mods, parentToken, parentTParams) (if (mods.is(Flags.JavaStatic) || inInterface && !(decls exists (_.isInstanceOf[DefDef]))) statics @@ -947,13 +1014,13 @@ object JavaParsers { } } - def typeDecl(start: Offset, mods: Modifiers): List[Tree] = in.token match { + def typeDecl(start: Offset, mods: Modifiers): List[Tree] = in.token match case ENUM => enumDecl(start, mods) case INTERFACE => interfaceDecl(start, mods) case AT => annotationDecl(start, mods) case CLASS => classDecl(start, mods) + case RECORD => recordDecl(start, mods) case _ => in.nextToken(); syntaxError(em"illegal start of type declaration", skipIt = true); List(errorTypeTree) - } def tryConstant: Option[Constant] = { val negate = in.token match { @@ -1004,6 +1071,7 @@ object JavaParsers { if (in.token != EOF) { val start = in.offset val mods = modifiers(inInterface = false) + adaptRecordIdentifier() // needed for typeDecl buf ++= typeDecl(start, mods) } } diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaTokens.scala b/compiler/src/dotty/tools/dotc/parsing/JavaTokens.scala index 3e73b6d95adb..2b7882173e00 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaTokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaTokens.scala @@ -41,6 +41,9 @@ object JavaTokens extends TokensCommon { inline val SWITCH = 133; enter(SWITCH, "switch") inline val ASSERT = 134; enter(ASSERT, "assert") + /** contextual keywords (turned into keywords in certain conditions, see JLS 3.9 of Java 9+) */ + inline val RECORD = 135; enter(RECORD, "record") + /** special symbols */ inline val EQEQ = 140 inline val BANGEQ = 141 diff --git a/tests/pos/java-records/FromScala.scala b/tests/pos/java-records/FromScala.scala new file mode 100644 index 000000000000..d9b9f4d2e129 --- /dev/null +++ b/tests/pos/java-records/FromScala.scala @@ -0,0 +1,15 @@ +object C: + def useR1 = + // constructor signature + val r = R1(123, "hello") + + // accessors + val i: Int = r.i + val s: String = r.s + + // methods + val iRes: Int = r.getInt() + val sRes: String = r.getString() + + // supertype + val record: java.lang.Record = r diff --git a/tests/pos/java-records/R1.java b/tests/pos/java-records/R1.java new file mode 100644 index 000000000000..832d288547ab --- /dev/null +++ b/tests/pos/java-records/R1.java @@ -0,0 +1,9 @@ +public record R1(int i, String s) { + public String getString() { + return s + i; + } + + public int getInt() { + return 0; + } +} From ed126d9657c8eb4886bee3bb2e05310c8f41a8ef Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Tue, 7 Mar 2023 17:46:27 +0100 Subject: [PATCH 547/657] Fix record's constructor Co-authored-by: Guillaume Martres --- compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala | 5 +++-- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 71aeee05fe10..5ab187871542 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -854,7 +854,8 @@ object JavaParsers { ).toList // generate the canonical constructor - val canonicalConstructor = makeConstructor(header, tparams) + val canonicalConstructor = + DefDef(nme.CONSTRUCTOR, joinParams(tparams, List(header)), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined, mods.privateWithin)) // return the trees val recordTypeDef = atSpan(start, nameOffset) { @@ -863,7 +864,7 @@ object JavaParsers { parents = superclass :: interfaces, stats = canonicalConstructor :: accessors ::: body, tparams = tparams, - false + true ) ) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 7eb8519739c6..02df12ecc0bc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3589,7 +3589,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer adapt(tree, pt, ctx.typerState.ownedVars) private def adapt1(tree: Tree, pt: Type, locked: TypeVars)(using Context): Tree = { - assert(pt.exists && !pt.isInstanceOf[ExprType] || ctx.reporter.errorsReported) + assert(pt.exists && !pt.isInstanceOf[ExprType] || ctx.reporter.errorsReported, i"tree: $tree, pt: $pt") def methodStr = err.refStr(methPart(tree).tpe) def readapt(tree: Tree)(using Context) = adapt(tree, pt, locked) From d1df3eed906ffd5b79a0d4fb0051d6dad7e92ff2 Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Tue, 7 Mar 2023 18:08:51 +0100 Subject: [PATCH 548/657] Add more record tests and only run them on java >= 16 Co-authored-by: Guillaume Martres --- .../dotty/tools/dotc/CompilationTests.scala | 11 +++-- .../pos-java16+/java-records/FromScala.scala | 43 +++++++++++++++++++ tests/pos-java16+/java-records/IntLike.scala | 2 + .../{pos => pos-java16+}/java-records/R1.java | 0 tests/pos-java16+/java-records/R2.java | 13 ++++++ tests/pos-java16+/java-records/R3.java | 22 ++++++++++ tests/pos/java-records/FromScala.scala | 15 ------- 7 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 tests/pos-java16+/java-records/FromScala.scala create mode 100644 tests/pos-java16+/java-records/IntLike.scala rename tests/{pos => pos-java16+}/java-records/R1.java (100%) create mode 100644 tests/pos-java16+/java-records/R2.java create mode 100644 tests/pos-java16+/java-records/R3.java delete mode 100644 tests/pos/java-records/FromScala.scala diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index c782c78f8eb7..8107e1579525 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -29,7 +29,7 @@ class CompilationTests { @Test def pos: Unit = { implicit val testGroup: TestGroup = TestGroup("compilePos") - aggregateTests( + var tests = List( compileFile("tests/pos/nullarify.scala", defaultOptions.and("-Ycheck:nullarify")), compileFile("tests/pos-special/utf8encoded.scala", explicitUTF8), compileFile("tests/pos-special/utf16encoded.scala", explicitUTF16), @@ -65,8 +65,13 @@ class CompilationTests { compileFile("tests/pos-special/extend-java-enum.scala", defaultOptions.and("-source", "3.0-migration")), compileFile("tests/pos-custom-args/help.scala", defaultOptions.and("-help", "-V", "-W", "-X", "-Y")), compileFile("tests/pos-custom-args/i13044.scala", defaultOptions.and("-Xmax-inlines:33")), - compileFile("tests/pos-custom-args/jdk-8-app.scala", defaultOptions.and("-release:8")), - ).checkCompile() + compileFile("tests/pos-custom-args/jdk-8-app.scala", defaultOptions.and("-release:8")) + ) + + if scala.util.Properties.isJavaAtLeast("16") then + tests ::= compileFilesInDir("tests/pos-java16+", defaultOptions.and("-Ysafe-init")) + + aggregateTests(tests*).checkCompile() } @Test def rewrites: Unit = { diff --git a/tests/pos-java16+/java-records/FromScala.scala b/tests/pos-java16+/java-records/FromScala.scala new file mode 100644 index 000000000000..67747e658432 --- /dev/null +++ b/tests/pos-java16+/java-records/FromScala.scala @@ -0,0 +1,43 @@ +object C: + def useR1: Unit = + // constructor signature + val r = R1(123, "hello") + + // accessors + val i: Int = r.i + val s: String = r.s + + // methods + val iRes: Int = r.getInt() + val sRes: String = r.getString() + + // supertype + val record: java.lang.Record = r + + def useR2: Unit = + // constructor signature + val r2 = R2.R(123, "hello") + + // accessors signature + val i: Int = r2.i + val s: String = r2.s + + // method + val i2: Int = r2.getInt + + // supertype + val isIntLike: IntLike = r2 + val isRecord: java.lang.Record = r2 + + def useR3 = + // constructor signature + val r3 = R3(123, 42L, "hi") + new R3("hi", 123) + // accessors signature + val i: Int = r3.i + val l: Long = r3.l + val s: String = r3.s + // method + val l2: Long = r3.l(43L, 44L) + // supertype + val isRecord: java.lang.Record = r3 diff --git a/tests/pos-java16+/java-records/IntLike.scala b/tests/pos-java16+/java-records/IntLike.scala new file mode 100644 index 000000000000..1f760018a975 --- /dev/null +++ b/tests/pos-java16+/java-records/IntLike.scala @@ -0,0 +1,2 @@ +trait IntLike: + def getInt: Int diff --git a/tests/pos/java-records/R1.java b/tests/pos-java16+/java-records/R1.java similarity index 100% rename from tests/pos/java-records/R1.java rename to tests/pos-java16+/java-records/R1.java diff --git a/tests/pos-java16+/java-records/R2.java b/tests/pos-java16+/java-records/R2.java new file mode 100644 index 000000000000..01da13d83b65 --- /dev/null +++ b/tests/pos-java16+/java-records/R2.java @@ -0,0 +1,13 @@ +public class R2 { + final record R(int i, String s) implements IntLike { + public int getInt() { + return i; + } + + // Canonical constructor + // public R(int i, java.lang.String s) { + // this.i = i; + // this.s = s.intern(); + // } + } +} diff --git a/tests/pos-java16+/java-records/R3.java b/tests/pos-java16+/java-records/R3.java new file mode 100644 index 000000000000..616481a0ae1f --- /dev/null +++ b/tests/pos-java16+/java-records/R3.java @@ -0,0 +1,22 @@ +public record R3(int i, long l, String s) { + + // User-specified accessor + public int i() { + return i + 1; // evil >:) + } + + // Not an accessor - too many parameters + public long l(long a1, long a2) { + return a1 + a2; + } + + // Secondary constructor + public R3(String s, int i) { + this(i, 42L, s); + } + + // Compact constructor + public R3 { + s = s.intern(); + } +} \ No newline at end of file diff --git a/tests/pos/java-records/FromScala.scala b/tests/pos/java-records/FromScala.scala deleted file mode 100644 index d9b9f4d2e129..000000000000 --- a/tests/pos/java-records/FromScala.scala +++ /dev/null @@ -1,15 +0,0 @@ -object C: - def useR1 = - // constructor signature - val r = R1(123, "hello") - - // accessors - val i: Int = r.i - val s: String = r.s - - // methods - val iRes: Int = r.getInt() - val sRes: String = r.getString() - - // supertype - val record: java.lang.Record = r From 2f91c59fcb54b373a783bd545a72700ee12719b6 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 10 May 2023 10:35:58 +0200 Subject: [PATCH 549/657] Only transform the body of the quote with QuoteTransformer --- compiler/src/dotty/tools/dotc/transform/Splicing.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 71bcaa4fcec0..b482e9311c74 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -86,8 +86,9 @@ class Splicing extends MacroTransform: override def transform(tree: tpd.Tree)(using Context): tpd.Tree = assert(level == 0) tree match - case Apply(Select(_: Quote, nme.apply), _) => - QuoteTransformer().transform(tree) + case tree: Quote => + val body1 = QuoteTransformer().transform(tree.body)(using quoteContext) + cpy.Quote(tree)(body = body1) case tree: DefDef if tree.symbol.is(Inline) => // Quotes in inlined methods are only pickled after they are inlined. tree @@ -95,7 +96,6 @@ class Splicing extends MacroTransform: super.transform(tree) end Level0QuoteTransformer - /** Transforms all direct splices in the current quote and replace them with holes. */ private class QuoteTransformer() extends Transformer: /** Set of definitions in the current quote */ @@ -108,6 +108,7 @@ class Splicing extends MacroTransform: private val typeHoles = mutable.Map.empty[TermRef, Hole] override def transform(tree: tpd.Tree)(using Context): tpd.Tree = + assert(level > 0) tree match case tree: Splice if level == 1 => val holeIdx = numHoles From b65ef59d9bdd6a524eaef2bc12a4e33ca1e0375b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pa=C5=82ka?= Date: Wed, 10 May 2023 13:41:35 +0200 Subject: [PATCH 550/657] Raise a warning instead of an error for a type ascription on a pattern other than a variable or a number literal. This partially reverts the changes from https://github.com/lampepfl/dotty/pull/16150. This change is motivated by not breaking source compatibility for a number of projects in the Open Community Build. --- .../dotty/tools/dotc/parsing/Parsers.scala | 10 +-- docs/_docs/internals/syntax.md | 5 +- docs/_docs/reference/syntax.md | 5 +- .../fatal-warnings}/i10994.scala | 0 .../fatal-warnings}/i15893.scala | 6 +- tests/neg/t5702-neg-bad-and-wild.check | 10 ++- tests/pending/run/i15893.scala | 6 +- tests/pos/i10994.scala | 2 + tests/pos/i15893.scala | 61 +++++++++++++++++++ 9 files changed, 83 insertions(+), 22 deletions(-) rename tests/{neg => neg-custom-args/fatal-warnings}/i10994.scala (100%) rename tests/{neg => neg-custom-args/fatal-warnings}/i15893.scala (89%) create mode 100644 tests/pos/i10994.scala create mode 100644 tests/pos/i15893.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 7fecdd2c5eae..170332f7abe8 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2871,14 +2871,14 @@ object Parsers { if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) } else Nil - /** Pattern1 ::= PatVar Ascription - * | [‘-’] integerLiteral Ascription - * | [‘-’] floatingPointLiteral Ascription - * | Pattern2 + /** Pattern1 ::= Pattern2 [Ascription] */ def pattern1(location: Location = Location.InPattern): Tree = val p = pattern2() - if (isVarPattern(p) || p.isInstanceOf[Number]) && in.isColon then + if in.isColon then + val isVariableOrNumber = isVarPattern(p) || p.isInstanceOf[Number] + if !isVariableOrNumber then + warning(em"Only variable and number literal patterns can have type ascriptions") in.nextToken() ascription(p, location) else p diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index c4e71ad11612..e667a429afed 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -314,10 +314,7 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) -Pattern1 ::= PatVar ‘:’ RefinedType Bind(name, Typed(Ident(wildcard), tpe)) - | [‘-’] integerLiteral ‘:’ RefinedType Typed(pat, tpe) - | [‘-’] floatingPointLiteral ‘:’ RefinedType Typed(pat, tpe) - | Pattern2 +Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) Pattern2 ::= [id ‘@’] InfixPattern [‘*’] Bind(name, pat) InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat) SimplePattern ::= PatVar Ident(wildcard) diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index dd2e7ce118c4..28259d94afb4 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -312,10 +312,7 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } -Pattern1 ::= PatVar ‘:’ RefinedType - | [‘-’] integerLiteral ‘:’ RefinedType - | [‘-’] floatingPointLiteral ‘:’ RefinedType - | Pattern2 +Pattern1 ::= Pattern2 [‘:’ RefinedType] Pattern2 ::= [id ‘@’] InfixPattern [‘*’] InfixPattern ::= SimplePattern { id [nl] SimplePattern } SimplePattern ::= PatVar diff --git a/tests/neg/i10994.scala b/tests/neg-custom-args/fatal-warnings/i10994.scala similarity index 100% rename from tests/neg/i10994.scala rename to tests/neg-custom-args/fatal-warnings/i10994.scala diff --git a/tests/neg/i15893.scala b/tests/neg-custom-args/fatal-warnings/i15893.scala similarity index 89% rename from tests/neg/i15893.scala rename to tests/neg-custom-args/fatal-warnings/i15893.scala index 997c51179099..f23e6150106a 100644 --- a/tests/neg/i15893.scala +++ b/tests/neg-custom-args/fatal-warnings/i15893.scala @@ -22,7 +22,7 @@ transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n m case Succ(Zero()) => Succ(Zero()) case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN) -def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match // exhaustivity warning; unexpected +def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match case Zero(): Zero => Zero() // error case Succ(Zero()): Succ[Zero] => Succ(Zero()) // error case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) // error @@ -57,5 +57,5 @@ inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInline println(transparentInlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected -// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected -// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected + println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index 731195411069..36ac71b2e1e7 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -10,10 +10,10 @@ | pattern expected | | longer explanation available when compiling with `-explain` --- [E040] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:13:22 --------------------------------------------------- +-- [E040] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:13:23 --------------------------------------------------- 13 | case List(1, _*3:) => // error // error - | ^ - | ')' expected, but ':' found + | ^ + | an identifier expected, but ')' found -- [E032] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:15:18 --------------------------------------------------- 15 | case List(x*, 1) => // error: pattern expected | ^ @@ -56,6 +56,10 @@ | Recursive value $1$ needs type | | longer explanation available when compiling with `-explain` +-- Warning: tests/neg/t5702-neg-bad-and-wild.scala:13:22 --------------------------------------------------------------- +13 | case List(1, _*3:) => // error // error + | ^ + | Only variable and number literal patterns can have type ascriptions -- Warning: tests/neg/t5702-neg-bad-and-wild.scala:22:20 --------------------------------------------------------------- 22 | val K(x @ _*) = k | ^ diff --git a/tests/pending/run/i15893.scala b/tests/pending/run/i15893.scala index dedec2138f2a..d9cd2822e971 100644 --- a/tests/pending/run/i15893.scala +++ b/tests/pending/run/i15893.scala @@ -24,7 +24,7 @@ transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n m case Succ(Zero()) => Succ(Zero()) case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN) */ -def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match // exhaustivity warning; unexpected +def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match case Zero(): Zero => Zero() case Succ(Zero()): Succ[Zero] => Succ(Zero()) case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) @@ -61,5 +61,5 @@ inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInline println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected */ println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected -// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected -// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected +// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected +// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected diff --git a/tests/pos/i10994.scala b/tests/pos/i10994.scala new file mode 100644 index 000000000000..99ae647466b1 --- /dev/null +++ b/tests/pos/i10994.scala @@ -0,0 +1,2 @@ +def foo = true match + case (b: Boolean): Boolean => () diff --git a/tests/pos/i15893.scala b/tests/pos/i15893.scala new file mode 100644 index 000000000000..af6e7ae38ad2 --- /dev/null +++ b/tests/pos/i15893.scala @@ -0,0 +1,61 @@ +sealed trait NatT +case class Zero() extends NatT +case class Succ[+N <: NatT](n: N) extends NatT + +type Mod2[N <: NatT] <: NatT = N match + case Zero => Zero + case Succ[Zero] => Succ[Zero] + case Succ[Succ[predPredN]] => Mod2[predPredN] + +def mod2(n: NatT): NatT = n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => mod2(predPredN) + +inline def inlineMod2(inline n: NatT): NatT = inline n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => inlineMod2(predPredN) + +transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN) + +def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match + case Zero(): Zero => Zero() // warning + case Succ(Zero()): Succ[Zero] => Succ(Zero()) // warning + case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) // warning + +inline def inlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match + case Zero(): Zero => Zero() // warning + case Succ(Zero()): Succ[Zero] => Succ(Zero()) // warning + case Succ(Succ(predPredN)): Succ[Succ[_]] => inlineDependentlyTypedMod2(predPredN) // warning + +transparent inline def transparentInlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match + case Zero(): Zero => Zero() // warning + case Succ(Zero()): Succ[Zero] => Succ(Zero()) // warning + case Succ(Succ(predPredN)): Succ[Succ[_]] => transparentInlineDependentlyTypedMod2(predPredN) // warning + +def foo(n: NatT): NatT = mod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +inline def inlineFoo(inline n: NatT): NatT = inline inlineMod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInlineMod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +@main def main(): Unit = + println(mod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(foo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected + println(inlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(inlineFoo(Succ(Succ(Succ(Zero()))))) // prints Succ(Succ(Succ(Zero()))); unexpected + println(transparentInlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected + println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected + println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected From 5bafff7cc96f1f31f6e77620ca509dfa55d816b4 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Wed, 10 May 2023 16:39:12 +0100 Subject: [PATCH 551/657] Normalize match type usage during implicit lookup --- .../dotty/tools/dotc/core/TypeComparer.scala | 2 +- .../dotty/tools/dotc/core/TypeErrors.scala | 3 +++ .../dotty/tools/dotc/typer/Implicits.scala | 7 ++++++ tests/pos/i17395.scala | 25 +++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i17395.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 465978d329e6..ee8b8c3cdfcc 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3180,7 +3180,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { tp case Nil => val casesText = MatchTypeTrace.noMatchesText(scrut, cases) - throw TypeError(em"Match type reduction $casesText") + throw MatchTypeReductionError(em"Match type reduction $casesText") inFrozenConstraint { // Empty types break the basic assumption that if a scrutinee and a diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index 24a207da6836..f59bd08da779 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -46,6 +46,9 @@ object TypeError: def toMessage(using Context) = msg end TypeError +class MatchTypeReductionError(msg: Message)(using Context) extends TypeError: + def toMessage(using Context) = msg + class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError: def toMessage(using Context) = em"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index a9631ad45e28..b5a20b6838bf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -634,6 +634,13 @@ trait ImplicitRunInfo: case t: TypeLambda => for p <- t.paramRefs do partSeen += p traverseChildren(t) + case t: MatchType => + traverseChildren(t) + traverse(try t.normalized catch case _: MatchTypeReductionError => t) + case MatchType.InDisguise(mt) + if !t.isInstanceOf[LazyRef] // skip recursive applications (eg. Tuple.Map) + => + traverse(mt) case t => traverseChildren(t) diff --git a/tests/pos/i17395.scala b/tests/pos/i17395.scala new file mode 100644 index 000000000000..87c0a45a9ff5 --- /dev/null +++ b/tests/pos/i17395.scala @@ -0,0 +1,25 @@ +trait TC[T] + +object TC { + def optionTCForPart[T](implicit tc: TC[ExtractPart[T]]): TC[Option[ExtractPart[T]]] = new TC[Option[ExtractPart[T]]] {} +} + +type ExtractPart[T] = T match { + case PartField[t] => t +} +type PartField[T] = Any { type Part = T } + +class ValuePartHolder { + type Part = Value +} + +class Value +object Value { + implicit val tcValue: TC[Value] = new {} +} + +@main def main(): Unit = { +// import Value.tcValue // explicit import works around the issue, but shouldn't be necessary + val tc = TC.optionTCForPart[ValuePartHolder] + println(tc) +} From 8eb125da4b09cc3536423f1d0684dca153c8552b Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 4 May 2023 18:15:21 +0200 Subject: [PATCH 552/657] Do postchecking inside out. Previously we sometimes got a "non=private definition with nonempty capturing type needs explicit type ascription error" that hid a more important error such as a leaking universal capture set. --- .../dotty/tools/dotc/cc/CheckCaptures.scala | 112 +++++++++--------- 1 file changed, 59 insertions(+), 53 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 72a166dbbecc..2866f4667b37 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -986,61 +986,67 @@ class CheckCaptures extends Recheck, SymTransformer: * - Heal ill-formed capture sets of type parameters. See `healTypeParam`. */ def postCheck(unit: tpd.Tree)(using Context): Unit = - unit.foreachSubTree { - case _: InferredTypeTree => - case tree: TypeTree if !tree.span.isZeroExtent => - tree.knownType.foreachPart { tp => - checkWellformedPost(tp, tree.srcPos) - tp match - case AnnotatedType(_, annot) if annot.symbol == defn.RetainsAnnot => - warnIfRedundantCaptureSet(annot.tree) - case _ => - } - case t: ValOrDefDef - if t.tpt.isInstanceOf[InferredTypeTree] && !Synthetics.isExcluded(t.symbol) => - val sym = t.symbol - val isLocal = - sym.owner.ownersIterator.exists(_.isTerm) - || sym.accessBoundary(defn.RootClass).isContainedIn(sym.topLevelClass) - def canUseInferred = // If canUseInferred is false, all capturing types in the type of `sym` need to be given explicitly - sym.is(Private) // private symbols can always have inferred types - || sym.name.is(DefaultGetterName) // default getters are exempted since otherwise it would be - // too annoying. This is a hole since a defualt getter's result type - // might leak into a type variable. - || // non-local symbols cannot have inferred types since external capture types are not inferred - isLocal // local symbols still need explicit types if - && !sym.owner.is(Trait) // they are defined in a trait, since we do OverridingPairs checking before capture inference - def isNotPureThis(ref: CaptureRef) = ref match { - case ref: ThisType => !ref.cls.isPureClass - case _ => true - } - if !canUseInferred then - val inferred = t.tpt.knownType - def checkPure(tp: Type) = tp match - case CapturingType(_, refs) - if !refs.elems.filter(isNotPureThis).isEmpty => - val resultStr = if t.isInstanceOf[DefDef] then " result" else "" - report.error( - em"""Non-local $sym cannot have an inferred$resultStr type - |$inferred - |with non-empty capture set $refs. - |The type needs to be declared explicitly.""".withoutDisambiguation(), - t.srcPos) + val checker = new TreeTraverser: + def traverse(tree: Tree)(using Context): Unit = + traverseChildren(tree) + check(tree) + def check(tree: Tree) = tree match + case _: InferredTypeTree => + case tree: TypeTree if !tree.span.isZeroExtent => + tree.knownType.foreachPart { tp => + checkWellformedPost(tp, tree.srcPos) + tp match + case AnnotatedType(_, annot) if annot.symbol == defn.RetainsAnnot => + warnIfRedundantCaptureSet(annot.tree) + case _ => + } + case t: ValOrDefDef + if t.tpt.isInstanceOf[InferredTypeTree] && !Synthetics.isExcluded(t.symbol) => + val sym = t.symbol + val isLocal = + sym.owner.ownersIterator.exists(_.isTerm) + || sym.accessBoundary(defn.RootClass).isContainedIn(sym.topLevelClass) + def canUseInferred = // If canUseInferred is false, all capturing types in the type of `sym` need to be given explicitly + sym.is(Private) // private symbols can always have inferred types + || sym.name.is(DefaultGetterName) // default getters are exempted since otherwise it would be + // too annoying. This is a hole since a defualt getter's result type + // might leak into a type variable. + || // non-local symbols cannot have inferred types since external capture types are not inferred + isLocal // local symbols still need explicit types if + && !sym.owner.is(Trait) // they are defined in a trait, since we do OverridingPairs checking before capture inference + def isNotPureThis(ref: CaptureRef) = ref match { + case ref: ThisType => !ref.cls.isPureClass + case _ => true + } + if !canUseInferred then + val inferred = t.tpt.knownType + def checkPure(tp: Type) = tp match + case CapturingType(_, refs) + if !refs.elems.filter(isNotPureThis).isEmpty => + val resultStr = if t.isInstanceOf[DefDef] then " result" else "" + report.error( + em"""Non-local $sym cannot have an inferred$resultStr type + |$inferred + |with non-empty capture set $refs. + |The type needs to be declared explicitly.""".withoutDisambiguation(), + t.srcPos) + case _ => + inferred.foreachPart(checkPure, StopAt.Static) + case t @ TypeApply(fun, args) => + fun.knownType.widen match + case tl: PolyType => + val normArgs = args.lazyZip(tl.paramInfos).map { (arg, bounds) => + arg.withType(arg.knownType.forceBoxStatus( + bounds.hi.isBoxedCapturing | bounds.lo.isBoxedCapturing)) + } + checkBounds(normArgs, tl) case _ => - inferred.foreachPart(checkPure, StopAt.Static) - case t @ TypeApply(fun, args) => - fun.knownType.widen match - case tl: PolyType => - val normArgs = args.lazyZip(tl.paramInfos).map { (arg, bounds) => - arg.withType(arg.knownType.forceBoxStatus( - bounds.hi.isBoxedCapturing | bounds.lo.isBoxedCapturing)) - } - checkBounds(normArgs, tl) - case _ => - args.foreach(healTypeParam(_)) - case _ => - } + args.foreach(healTypeParam(_)) + case _ => + end check + end checker + checker.traverse(unit) if !ctx.reporter.errorsReported then // We dont report errors here if previous errors were reported, because other // errors often result in bad applied types, but flagging these bad types gives From 05bce382e9239c46ba6b2507f95406aed21d5e27 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 5 May 2023 12:28:47 +0200 Subject: [PATCH 553/657] Implement sealed type variables Abolish restriction that box/unbox cannot be done with `cap`. Instead, do a deep check for `cap` - in type arguments for `sealed` type variables - in the types of mutable vars - in the type of `try` under the `saferExceptions` language import The caps.unsafe.{unsafeBox, unsafeUnbox, unsafeBoxFunArg} methods are not longer needed and are deprecated. Instead there is an annotation annotation.unchecked.uncheckedCaptures that can be added to a mutable variable to turn off checking its type. These changes in behavior are introduced in 3.3. Older source versions still support the old behavior (which corresponds to the paper). So to run examples in the paper as described there, they need a `-source 3.2` or a language import. import language.`3.2`. --- .../src/dotty/tools/dotc/cc/CaptureOps.scala | 4 + .../dotty/tools/dotc/cc/CheckCaptures.scala | 42 +++++++--- compiler/src/dotty/tools/dotc/cc/Setup.scala | 20 ++++- .../tools/dotc/config/SourceVersion.scala | 2 + .../dotty/tools/dotc/core/Definitions.scala | 2 + .../src/dotty/tools/dotc/core/Types.scala | 17 +++- .../dotty/tools/dotc/parsing/Parsers.scala | 33 ++++---- .../dotty/tools/dotc/transform/Recheck.scala | 13 ++- .../src/dotty/tools/dotc/typer/Checking.scala | 7 +- .../src/dotty/tools/dotc/typer/Typer.scala | 2 +- .../unchecked/uncheckedCapabilityLeaks.scala | 12 +++ library/src/scala/caps.scala | 7 ++ .../captures/box-adapt-cases.scala | 2 +- .../neg-custom-args/captures/capt-test.scala | 6 +- tests/neg-custom-args/captures/capt1.check | 9 +-- tests/neg-custom-args/captures/capt1.scala | 2 +- tests/neg-custom-args/captures/ctest.scala | 4 +- tests/neg-custom-args/captures/filevar.scala | 4 +- .../captures/heal-tparam-cs.scala | 4 +- tests/neg-custom-args/captures/i15049.scala | 4 +- tests/neg-custom-args/captures/i15772.check | 8 +- tests/neg-custom-args/captures/i15923.scala | 8 +- .../captures/lazylists-exceptions.check | 5 +- tests/neg-custom-args/captures/real-try.check | 31 +++++--- tests/neg-custom-args/captures/real-try.scala | 6 +- .../captures/sealed-leaks.scala | 20 +++++ .../captures/stack-alloc.scala | 2 +- tests/neg-custom-args/captures/try.check | 35 ++++---- tests/neg-custom-args/captures/try.scala | 8 +- tests/neg-custom-args/captures/try3.scala | 6 +- tests/neg-custom-args/captures/unbox.scala | 1 + .../captures/usingLogFile.check | 79 +++++++++++-------- .../captures/usingLogFile.scala | 25 +++--- tests/neg-custom-args/captures/vars.check | 42 +++++----- tests/neg-custom-args/captures/vars.scala | 12 +-- tests/pos-custom-args/captures/capt-env.scala | 8 ++ .../captures/i15749.scala | 2 +- .../captures/i15749a.scala | 8 +- .../captures/i15923-cases.scala | 2 +- .../captures/i15925.scala | 6 +- .../captures/unsafe-unbox.scala | 7 +- tests/pos-custom-args/captures/vars1.scala | 23 ++++-- tests/pos-with-compiler-cc/dotc/Run.scala | 5 +- .../dotc/core/Scopes.scala | 7 +- .../dotc/core/TypeComparer.scala | 11 +-- .../dotc/core/tasty/TreeUnpickler.scala | 10 +-- .../dotc/typer/Namer.scala | 4 +- .../dotc/typer/Typer.scala | 15 ++-- 48 files changed, 371 insertions(+), 221 deletions(-) create mode 100644 library/src/scala/annotation/unchecked/uncheckedCapabilityLeaks.scala create mode 100644 tests/neg-custom-args/captures/sealed-leaks.scala create mode 100644 tests/pos-custom-args/captures/capt-env.scala rename tests/{neg-custom-args => pos-custom-args}/captures/i15749.scala (73%) rename tests/{neg-custom-args => pos-custom-args}/captures/i15749a.scala (62%) rename tests/{neg-custom-args => pos-custom-args}/captures/i15923-cases.scala (89%) rename tests/{neg-custom-args => pos-custom-args}/captures/i15925.scala (69%) diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala index decd428f5365..3ba26c92cab5 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureOps.scala @@ -6,6 +6,7 @@ import core.* import Types.*, Symbols.*, Contexts.*, Annotations.*, Flags.* import ast.{tpd, untpd} import Decorators.*, NameOps.* +import config.SourceVersion import config.Printers.capt import util.Property.Key import tpd.* @@ -19,6 +20,9 @@ private[cc] def retainedElems(tree: Tree)(using Context): List[Tree] = tree matc case Apply(_, Typed(SeqLiteral(elems, _), _) :: Nil) => elems case _ => Nil +def allowUniversalInBoxed(using Context) = + Feature.sourceVersion.isAtLeast(SourceVersion.`3.3`) + /** An exception thrown if a @retains argument is not syntactically a CaptureRef */ class IllegalCaptureRef(tpe: Type) extends Exception diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 2866f4667b37..9e0263d63b38 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -128,6 +128,20 @@ object CheckCaptures: if remaining.accountsFor(firstRef) then report.warning(em"redundant capture: $remaining already accounts for $firstRef", ann.srcPos) + def disallowRootCapabilitiesIn(tp: Type, what: String, have: String, addendum: String, pos: SrcPos)(using Context) = + val check = new TypeTraverser: + def traverse(t: Type) = + if variance >= 0 then + t.captureSet.disallowRootCapability: () => + def part = if t eq tp then "" else i"the part $t of " + report.error( + em"""$what cannot $have $tp since + |${part}that type captures the root capability `cap`. + |$addendum""", + pos) + traverseChildren(t) + check.traverse(tp) + class CheckCaptures extends Recheck, SymTransformer: thisPhase => @@ -525,6 +539,15 @@ class CheckCaptures extends Recheck, SymTransformer: case _ => super.recheckTyped(tree) + override def recheckTry(tree: Try, pt: Type)(using Context): Type = + val tp = super.recheckTry(tree, pt) + if allowUniversalInBoxed && Feature.enabled(Feature.saferExceptions) then + disallowRootCapabilitiesIn(tp, + "Result of `try`", "have type", + "This is often caused by a locally generated exception capability leaking as part of its result.", + tree.srcPos) + tp + /* Currently not needed, since capture checking takes place after ElimByName. * Keep around in case we need to get back to it def recheckByNameArg(tree: Tree, pt: Type)(using Context): Type = @@ -588,7 +611,7 @@ class CheckCaptures extends Recheck, SymTransformer: } checkNotUniversal(parent) case _ => - checkNotUniversal(typeToCheck) + if !allowUniversalInBoxed then checkNotUniversal(typeToCheck) super.recheckFinish(tpe, tree, pt) /** Massage `actual` and `expected` types using the methods below before checking conformance */ @@ -771,7 +794,7 @@ class CheckCaptures extends Recheck, SymTransformer: val criticalSet = // the set which is not allowed to have `cap` if covariant then cs1 // can't box with `cap` else expected.captureSet // can't unbox with `cap` - if criticalSet.isUniversal && expected.isValueType then + if criticalSet.isUniversal && expected.isValueType && !allowUniversalInBoxed then // We can't box/unbox the universal capability. Leave `actual` as it is // so we get an error in checkConforms. This tends to give better error // messages than disallowing the root capability in `criticalSet`. @@ -779,13 +802,14 @@ class CheckCaptures extends Recheck, SymTransformer: println(i"cannot box/unbox $actual vs $expected") actual else - // Disallow future addition of `cap` to `criticalSet`. - criticalSet.disallowRootCapability { () => - report.error( - em"""$actual cannot be box-converted to $expected - |since one of their capture sets contains the root capability `cap`""", - pos) - } + if !allowUniversalInBoxed then + // Disallow future addition of `cap` to `criticalSet`. + criticalSet.disallowRootCapability { () => + report.error( + em"""$actual cannot be box-converted to $expected + |since one of their capture sets contains the root capability `cap`""", + pos) + } if !insertBox then // unboxing markFree(criticalSet, pos) adaptedType(!boxed) diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index fc16422e1373..09f9695abfe1 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -411,11 +411,28 @@ extends tpd.TreeTraverser: boxed = tree.symbol.is(Mutable), // types of mutable variables are boxed exact = tree.symbol.allOverriddenSymbols.hasNext // types of symbols that override a parent don't get a capture set ) + if allowUniversalInBoxed && tree.symbol.is(Mutable) + && !tree.symbol.hasAnnotation(defn.UncheckedCapturesAnnot) + then + CheckCaptures.disallowRootCapabilitiesIn(tpt.knownType, + i"Mutable variable ${tree.symbol.name}", "have type", + "This restriction serves to prevent local capabilities from escaping the scope where they are defined.", + tree.srcPos) traverse(tree.rhs) case tree @ TypeApply(fn, args) => traverse(fn) for case arg: TypeTree <- args do transformTT(arg, boxed = true, exact = false) // type arguments in type applications are boxed + + if allowUniversalInBoxed then + val polyType = fn.tpe.widen.asInstanceOf[TypeLambda] + for case (arg: TypeTree, pinfo, pname) <- args.lazyZip(polyType.paramInfos).lazyZip((polyType.paramNames)) do + if pinfo.bounds.hi.hasAnnotation(defn.Caps_SealedAnnot) then + def where = if fn.symbol.exists then i" in the body of ${fn.symbol}" else "" + CheckCaptures.disallowRootCapabilitiesIn(arg.knownType, + i"Sealed type variable $pname", " be instantiated to", + i"This is often caused by a local capability$where\nleaking as part of its result.", + tree.srcPos) case _ => traverseChildren(tree) tree match @@ -494,11 +511,10 @@ extends tpd.TreeTraverser: def apply(tree: Tree)(using Context): Unit = traverse(tree)(using ctx.withProperty(Setup.IsDuringSetupKey, Some(()))) -end Setup object Setup: val IsDuringSetupKey = new Property.Key[Unit] def isDuringSetup(using Context): Boolean = ctx.property(IsDuringSetupKey).isDefined - +end Setup \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala index 4b9b1b247856..b8fa7994ce0c 100644 --- a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala +++ b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala @@ -18,6 +18,8 @@ enum SourceVersion: def isAtLeast(v: SourceVersion) = stable.ordinal >= v.ordinal + def isAtMost(v: SourceVersion) = stable.ordinal <= v.ordinal + object SourceVersion extends Property.Key[SourceVersion]: def defaultSourceVersion = `3.3` diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 82b1a6354321..da2ec78b5179 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -973,6 +973,7 @@ class Definitions { @tu lazy val Caps_unsafeBox: Symbol = CapsUnsafeModule.requiredMethod("unsafeBox") @tu lazy val Caps_unsafeUnbox: Symbol = CapsUnsafeModule.requiredMethod("unsafeUnbox") @tu lazy val Caps_unsafeBoxFunArg: Symbol = CapsUnsafeModule.requiredMethod("unsafeBoxFunArg") + @tu lazy val Caps_SealedAnnot: ClassSymbol = requiredClass("scala.caps.Sealed") // Annotation base classes @tu lazy val AnnotationClass: ClassSymbol = requiredClass("scala.annotation.Annotation") @@ -1021,6 +1022,7 @@ class Definitions { @tu lazy val UncheckedAnnot: ClassSymbol = requiredClass("scala.unchecked") @tu lazy val UncheckedStableAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedStable") @tu lazy val UncheckedVarianceAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedVariance") + @tu lazy val UncheckedCapturesAnnot: ClassSymbol = requiredClass("scala.annotation.unchecked.uncheckedCaptures") @tu lazy val VolatileAnnot: ClassSymbol = requiredClass("scala.volatile") @tu lazy val WithPureFunsAnnot: ClassSymbol = requiredClass("scala.annotation.internal.WithPureFuns") @tu lazy val BeanGetterMetaAnnot: ClassSymbol = requiredClass("scala.annotation.meta.beanGetter") diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 0942cc5ca981..2e3ab1a5ded8 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3960,10 +3960,15 @@ object Types { protected def toPInfo(tp: Type)(using Context): PInfo + /** If `tparam` is a sealed type parameter symbol of a polymorphic method, add + * a @caps.Sealed annotation to the upperbound in `tp`. + */ + protected def addSealed(tparam: ParamInfo, tp: Type)(using Context): Type = tp + def fromParams[PI <: ParamInfo.Of[N]](params: List[PI], resultType: Type)(using Context): Type = if (params.isEmpty) resultType else apply(params.map(_.paramName))( - tl => params.map(param => toPInfo(tl.integrate(params, param.paramInfo))), + tl => params.map(param => toPInfo(addSealed(param, tl.integrate(params, param.paramInfo)))), tl => tl.integrate(params, resultType)) } @@ -4285,6 +4290,16 @@ object Types { resultTypeExp: PolyType => Type)(using Context): PolyType = unique(new PolyType(paramNames)(paramInfosExp, resultTypeExp)) + override protected def addSealed(tparam: ParamInfo, tp: Type)(using Context): Type = + tparam match + case tparam: Symbol if tparam.is(Sealed) => + tp match + case tp @ TypeBounds(lo, hi) => + tp.derivedTypeBounds(lo, + AnnotatedType(hi, Annotation(defn.Caps_SealedAnnot, tparam.span))) + case _ => tp + case _ => tp + def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] = Some((tl.typeParams, tl.resType)) } diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 2f51af8549ae..8d13bdeed68f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3152,7 +3152,9 @@ object Parsers { * id [HkTypeParamClause] TypeParamBounds * * DefTypeParamClause::= ‘[’ DefTypeParam {‘,’ DefTypeParam} ‘]’ - * DefTypeParam ::= {Annotation} id [HkTypeParamClause] TypeParamBounds + * DefTypeParam ::= {Annotation} + * [`sealed`] -- under captureChecking + * id [HkTypeParamClause] TypeParamBounds * * TypTypeParamClause::= ‘[’ TypTypeParam {‘,’ TypTypeParam} ‘]’ * TypTypeParam ::= {Annotation} id [HkTypePamClause] TypeBounds @@ -3162,24 +3164,25 @@ object Parsers { */ def typeParamClause(ownerKind: ParamOwner): List[TypeDef] = inBrackets { - def variance(vflag: FlagSet): FlagSet = - if ownerKind == ParamOwner.Def || ownerKind == ParamOwner.TypeParam then - syntaxError(em"no `+/-` variance annotation allowed here") - in.nextToken() - EmptyFlags - else - in.nextToken() - vflag + def checkVarianceOK(): Boolean = + val ok = ownerKind != ParamOwner.Def && ownerKind != ParamOwner.TypeParam + if !ok then syntaxError(em"no `+/-` variance annotation allowed here") + in.nextToken() + ok def typeParam(): TypeDef = { val isAbstractOwner = ownerKind == ParamOwner.Type || ownerKind == ParamOwner.TypeParam val start = in.offset - val mods = - annotsAsMods() - | (if (ownerKind == ParamOwner.Class) Param | PrivateLocal else Param) - | (if isIdent(nme.raw.PLUS) then variance(Covariant) - else if isIdent(nme.raw.MINUS) then variance(Contravariant) - else EmptyFlags) + var mods = annotsAsMods() | Param + if ownerKind == ParamOwner.Class then mods |= PrivateLocal + if Feature.ccEnabled && in.token == SEALED then + if ownerKind == ParamOwner.Def then mods |= Sealed + else syntaxError(em"`sealed` modifier only allowed for method type parameters") + in.nextToken() + if isIdent(nme.raw.PLUS) && checkVarianceOK() then + mods |= Covariant + else if isIdent(nme.raw.MINUS) && checkVarianceOK() then + mods |= Contravariant atSpan(start, nameStart) { val name = if (isAbstractOwner && in.token == USCORE) { diff --git a/compiler/src/dotty/tools/dotc/transform/Recheck.scala b/compiler/src/dotty/tools/dotc/transform/Recheck.scala index c524bbb7702f..527c73d02250 100644 --- a/compiler/src/dotty/tools/dotc/transform/Recheck.scala +++ b/compiler/src/dotty/tools/dotc/transform/Recheck.scala @@ -4,7 +4,7 @@ package transform import core.* import Symbols.*, Contexts.*, Types.*, ContextOps.*, Decorators.*, SymDenotations.* -import Flags.*, SymUtils.*, NameKinds.*, Denotations.Denotation +import Flags.*, SymUtils.*, NameKinds.*, Denotations.{Denotation, SingleDenotation} import ast.* import Names.Name import Phases.Phase @@ -22,7 +22,7 @@ import StdNames.nme import reporting.trace import annotation.constructorOnly import cc.CaptureSet.IdempotentCaptRefMap -import dotty.tools.dotc.core.Denotations.SingleDenotation +import annotation.tailrec object Recheck: import tpd.* @@ -406,7 +406,14 @@ abstract class Recheck extends Phase, SymTransformer: NoType def recheckStats(stats: List[Tree])(using Context): Unit = - stats.foreach(recheck(_)) + @tailrec def traverse(stats: List[Tree])(using Context): Unit = stats match + case (imp: Import) :: rest => + traverse(rest)(using ctx.importContext(imp, imp.symbol)) + case stat :: rest => + recheck(stat) + traverse(rest) + case _ => + traverse(stats) def recheckDef(tree: ValOrDefDef, sym: Symbol)(using Context): Unit = inContext(ctx.localContext(tree, sym)) { diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 436721a6774b..b2ab5332c3b2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -506,7 +506,12 @@ object Checking { // note: this is not covered by the next test since terms can be abstract (which is a dual-mode flag) // but they can never be one of ClassOnlyFlags if !sym.isClass && sym.isOneOf(ClassOnlyFlags) then - fail(em"only classes can be ${(sym.flags & ClassOnlyFlags).flagsString}") + val illegal = sym.flags & ClassOnlyFlags + if sym.is(TypeParam) && illegal == Sealed && Feature.ccEnabled && cc.allowUniversalInBoxed then + if !sym.owner.is(Method) then + fail(em"only method type parameters can be sealed") + else + fail(em"only classes can be ${illegal.flagsString}") if (sym.is(AbsOverride) && !sym.owner.is(Trait)) fail(AbstractOverrideOnlyInTraits(sym)) if sym.is(Trait) then diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9f13071ae4b7..cfc9e968ef41 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -608,7 +608,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer ownType match case ownType: TermRef if ownType.symbol.is(ConstructorProxy) => findRef(name, pt, EmptyFlags, ConstructorProxy, tree.srcPos) match - case shadowed: TermRef => + case shadowed: TermRef if !shadowed.symbol.maybeOwner.isEmptyPackage => pt match case pt: FunOrPolyProto => def err(shadowedIsApply: Boolean) = diff --git a/library/src/scala/annotation/unchecked/uncheckedCapabilityLeaks.scala b/library/src/scala/annotation/unchecked/uncheckedCapabilityLeaks.scala new file mode 100644 index 000000000000..477ac6d742f7 --- /dev/null +++ b/library/src/scala/annotation/unchecked/uncheckedCapabilityLeaks.scala @@ -0,0 +1,12 @@ +package scala.annotation +package unchecked + +/** An annotation for mutable variables that are allowed to capture + * the root capability `cap`. Allowing this is not capture safe since + * it can cause leakage of capabilities from local scopes by assigning + * values retaining such capabilties to the annotated variable in + * an outer scope. + */ +class uncheckedCaptures extends StaticAnnotation + + diff --git a/library/src/scala/caps.scala b/library/src/scala/caps.scala index 6b7b085bbc06..4981353d61be 100644 --- a/library/src/scala/caps.scala +++ b/library/src/scala/caps.scala @@ -27,8 +27,15 @@ import annotation.experimental * avoids the error that would be raised when unboxing `*`. */ extension [T, U](f: T => U) def unsafeBoxFunArg: T => U = f + end unsafe + /** An annotation that expresses the sealed modifier on a type parameter + * Should not be directly referred to in source + */ + @deprecated("The Sealed annotation should not be directly used in source code.\nUse the `sealed` modifier on type parameters instead.") + class Sealed extends annotation.Annotation + /** Mixing in this trait forces a trait or class to be pure, i.e. * have no capabilities retained in its self type. */ diff --git a/tests/neg-custom-args/captures/box-adapt-cases.scala b/tests/neg-custom-args/captures/box-adapt-cases.scala index 351fab2bd8a5..7010444eecb5 100644 --- a/tests/neg-custom-args/captures/box-adapt-cases.scala +++ b/tests/neg-custom-args/captures/box-adapt-cases.scala @@ -4,7 +4,7 @@ def test1(): Unit = { type Id[X] = [T] -> (op: X => T) -> T val x: Id[Cap^] = ??? - x(cap => cap.use()) // error + x(cap => cap.use()) // was error, now OK } def test2(io: Cap^{cap}): Unit = { diff --git a/tests/neg-custom-args/captures/capt-test.scala b/tests/neg-custom-args/captures/capt-test.scala index 13368aeea190..f14951f410c4 100644 --- a/tests/neg-custom-args/captures/capt-test.scala +++ b/tests/neg-custom-args/captures/capt-test.scala @@ -14,14 +14,14 @@ def raise[E <: Exception](e: E): Nothing throws E = throw e def foo(x: Boolean): Int throws Fail = if x then 1 else raise(Fail()) -def handle[E <: Exception, R <: Top](op: (CanThrow[E]) => R)(handler: E => R): R = +def handle[E <: Exception, sealed R <: Top](op: (CanThrow[E]) => R)(handler: E => R): R = val x: CanThrow[E] = ??? try op(x) catch case ex: E => handler(ex) def test: Unit = - val b = handle[Exception, () => Nothing] { + val b = handle[Exception, () => Nothing] { // error (x: CanThrow[Exception]) => () => raise(new Exception)(using x) - } { // error + } { (ex: Exception) => ??? } diff --git a/tests/neg-custom-args/captures/capt1.check b/tests/neg-custom-args/captures/capt1.check index f51d4631782d..85d3b2a7ddcb 100644 --- a/tests/neg-custom-args/captures/capt1.check +++ b/tests/neg-custom-args/captures/capt1.check @@ -40,14 +40,7 @@ -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:32:24 ---------------------------------------- 32 | val z2 = h[() -> Cap](() => x) // error | ^^^^^^^ - | Found: () ->{x} Cap + | Found: () ->{x} box C^ | Required: () -> box C^ | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/capt1.scala:33:5 ----------------------------------------- -33 | (() => C()) // error - | ^^^^^^^^^ - | Found: () ->? Cap - | Required: () -> box C^ - | - | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/capt1.scala b/tests/neg-custom-args/captures/capt1.scala index 69b4c9984494..651184e8d2c9 100644 --- a/tests/neg-custom-args/captures/capt1.scala +++ b/tests/neg-custom-args/captures/capt1.scala @@ -30,7 +30,7 @@ def foo() = val x: C @retains(caps.cap) = ??? def h[X](a: X)(b: X) = a val z2 = h[() -> Cap](() => x) // error - (() => C()) // error + (() => C()) val z3 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // ok val z4 = h[(() -> Cap) @retains(x)](() => x)(() => C()) // what was inferred for z3 diff --git a/tests/neg-custom-args/captures/ctest.scala b/tests/neg-custom-args/captures/ctest.scala index 37d6ad034936..ad10b43a7773 100644 --- a/tests/neg-custom-args/captures/ctest.scala +++ b/tests/neg-custom-args/captures/ctest.scala @@ -2,5 +2,5 @@ class CC type Cap = CC^ def test(cap1: Cap, cap2: Cap) = - var b: List[String => String] = Nil // was error, now OK - val bc = b.head // error + var b: List[String => String] = Nil // error + val bc = b.head // was error, now OK diff --git a/tests/neg-custom-args/captures/filevar.scala b/tests/neg-custom-args/captures/filevar.scala index 474ba36b0e79..830563f51de3 100644 --- a/tests/neg-custom-args/captures/filevar.scala +++ b/tests/neg-custom-args/captures/filevar.scala @@ -5,8 +5,8 @@ class File: def write(x: String): Unit = ??? class Service: - var file: File^ = uninitialized - def log = file.write("log") // error + var file: File^ = uninitialized // error + def log = file.write("log") def withFile[T](op: (f: File^) => T): T = op(new File) diff --git a/tests/neg-custom-args/captures/heal-tparam-cs.scala b/tests/neg-custom-args/captures/heal-tparam-cs.scala index 173de2b1cbb5..58d12f8b6ce5 100644 --- a/tests/neg-custom-args/captures/heal-tparam-cs.scala +++ b/tests/neg-custom-args/captures/heal-tparam-cs.scala @@ -2,7 +2,7 @@ import language.experimental.captureChecking trait Cap { def use(): Unit } -def localCap[T](op: (cap: Cap^{cap}) => T): T = ??? +def localCap[sealed T](op: (cap: Cap^{cap}) => T): T = ??? def main(io: Cap^{cap}, net: Cap^{cap}): Unit = { val test1 = localCap { cap => // error @@ -24,7 +24,7 @@ def main(io: Cap^{cap}, net: Cap^{cap}): Unit = { (cap1: Cap^{io}) => () => { cap1.use() } } - def localCap2[T](op: (cap: Cap^{io}) => T): T = ??? + def localCap2[sealed T](op: (cap: Cap^{io}) => T): T = ??? val test5: () ->{io} Unit = localCap2 { cap => // ok diff --git a/tests/neg-custom-args/captures/i15049.scala b/tests/neg-custom-args/captures/i15049.scala index bc65db2147f8..d978e0e1ad0f 100644 --- a/tests/neg-custom-args/captures/i15049.scala +++ b/tests/neg-custom-args/captures/i15049.scala @@ -2,9 +2,9 @@ class Session: def request = "Response" class Foo: private val session: Session^{cap} = new Session - def withSession[T](f: (Session^{cap}) => T): T = f(session) + def withSession[sealed T](f: (Session^{cap}) => T): T = f(session) -def Test = +def Test: Unit = val f = new Foo f.withSession(s => s).request // error f.withSession[Session^](t => t) // error diff --git a/tests/neg-custom-args/captures/i15772.check b/tests/neg-custom-args/captures/i15772.check index e77d4022451d..949f7ca48588 100644 --- a/tests/neg-custom-args/captures/i15772.check +++ b/tests/neg-custom-args/captures/i15772.check @@ -12,11 +12,11 @@ | Required: Observe[C^] | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:33:34 --------------------------------------- +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:33:33 --------------------------------------- 33 | val boxed2 : Observe[C]^ = box2(c) // error - | ^ - | Found: C^ - | Required: box C{val arg: C^?}^ + | ^^^^^^^ + | Found: (C{val arg: C^}^ => Unit) ->? Unit + | Required: (C => Unit) => Unit | | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i15772.scala:44:2 ---------------------------------------- diff --git a/tests/neg-custom-args/captures/i15923.scala b/tests/neg-custom-args/captures/i15923.scala index 3ce9d6121396..3994b34f5928 100644 --- a/tests/neg-custom-args/captures/i15923.scala +++ b/tests/neg-custom-args/captures/i15923.scala @@ -3,12 +3,12 @@ type Id[X] = [T] -> (op: X => T) -> T def mkId[X](x: X): Id[X] = [T] => (op: X => T) => op(x) def bar() = { - def withCap[X](op: (Cap^) => X): X = { + def withCap[sealed X](op: (Cap^) => X): X = { val cap: Cap^ = new Cap { def use() = { println("cap is used"); 0 } } val result = op(cap) result } - val leak = withCap(cap => mkId(cap)) - leak { cap => cap.use() } // error -} + val leak = withCap(cap => mkId(cap)) // error + leak { cap => cap.use() } +} \ No newline at end of file diff --git a/tests/neg-custom-args/captures/lazylists-exceptions.check b/tests/neg-custom-args/captures/lazylists-exceptions.check index 03278f25e8dc..f58ed265d3be 100644 --- a/tests/neg-custom-args/captures/lazylists-exceptions.check +++ b/tests/neg-custom-args/captures/lazylists-exceptions.check @@ -1,8 +1,9 @@ -- Error: tests/neg-custom-args/captures/lazylists-exceptions.scala:36:2 ----------------------------------------------- 36 | try // error | ^ - | The expression's type LazyList[Int]^ is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. + | Result of `try` cannot have type LazyList[Int]^ since + | that type captures the root capability `cap`. + | This is often caused by a locally generated exception capability leaking as part of its result. 37 | tabulate(10) { i => 38 | if i > 9 then throw Ex1() 39 | i * i diff --git a/tests/neg-custom-args/captures/real-try.check b/tests/neg-custom-args/captures/real-try.check index 0b4da5af27b5..c8df3777bcfa 100644 --- a/tests/neg-custom-args/captures/real-try.check +++ b/tests/neg-custom-args/captures/real-try.check @@ -1,8 +1,15 @@ +-- [E129] Potential Issue Warning: tests/neg-custom-args/captures/real-try.scala:30:4 ---------------------------------- +30 | b.x + | ^^^ + | A pure expression does nothing in statement position; you may be omitting necessary parentheses + | + | longer explanation available when compiling with `-explain` -- Error: tests/neg-custom-args/captures/real-try.scala:12:2 ----------------------------------------------------------- 12 | try // error | ^ - | The expression's type () => Unit is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. + | Result of `try` cannot have type () => Unit since + | that type captures the root capability `cap`. + | This is often caused by a locally generated exception capability leaking as part of its result. 13 | () => foo(1) 14 | catch 15 | case _: Ex1 => ??? @@ -10,14 +17,20 @@ -- Error: tests/neg-custom-args/captures/real-try.scala:18:2 ----------------------------------------------------------- 18 | try // error | ^ - | The expression's type () => Cell[Unit]^? is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. + | Result of `try` cannot have type () => Cell[Unit]^? since + | that type captures the root capability `cap`. + | This is often caused by a locally generated exception capability leaking as part of its result. 19 | () => Cell(foo(1)) 20 | catch 21 | case _: Ex1 => ??? 22 | case _: Ex2 => ??? --- Error: tests/neg-custom-args/captures/real-try.scala:30:4 ----------------------------------------------------------- -30 | b.x // error - | ^^^ - | The expression's type box () => Unit is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. +-- Error: tests/neg-custom-args/captures/real-try.scala:24:10 ---------------------------------------------------------- +24 | val b = try // error + | ^ + | Result of `try` cannot have type Cell[box () => Unit]^? since + | the part box () => Unit of that type captures the root capability `cap`. + | This is often caused by a locally generated exception capability leaking as part of its result. +25 | Cell(() => foo(1))//: Cell[box {ev} () => Unit] <: Cell[box {cap} () => Unit] +26 | catch +27 | case _: Ex1 => ??? +28 | case _: Ex2 => ??? diff --git a/tests/neg-custom-args/captures/real-try.scala b/tests/neg-custom-args/captures/real-try.scala index 03e168032122..a826fdaa4af7 100644 --- a/tests/neg-custom-args/captures/real-try.scala +++ b/tests/neg-custom-args/captures/real-try.scala @@ -8,7 +8,7 @@ def foo(i: Int): (CanThrow[Ex1], CanThrow[Ex2]) ?-> Unit = class Cell[+T](val x: T) -def test() = +def test(): Unit = try // error () => foo(1) catch @@ -21,10 +21,10 @@ def test() = case _: Ex1 => ??? case _: Ex2 => ??? - val b = try // ok here, but error on use + val b = try // error Cell(() => foo(1))//: Cell[box {ev} () => Unit] <: Cell[box {cap} () => Unit] catch case _: Ex1 => ??? case _: Ex2 => ??? - b.x // error + b.x diff --git a/tests/neg-custom-args/captures/sealed-leaks.scala b/tests/neg-custom-args/captures/sealed-leaks.scala new file mode 100644 index 000000000000..bf46b52194c1 --- /dev/null +++ b/tests/neg-custom-args/captures/sealed-leaks.scala @@ -0,0 +1,20 @@ + +import java.io.* +def Test2 = + + def usingLogFile[sealed T](op: FileOutputStream^ => T): T = + val logFile = FileOutputStream("log") + val result = op(logFile) + logFile.close() + result + + val later = usingLogFile { f => () => f.write(0) } // error + val later2 = usingLogFile[(() => Unit) | Null] { f => () => f.write(0) } // error + + var x: (FileOutputStream^) | Null = null // error + def foo(f: FileOutputStream^, g: FileOutputStream^) = + var y = if ??? then f else g // error + + usingLogFile { f => x = f } + + later() \ No newline at end of file diff --git a/tests/neg-custom-args/captures/stack-alloc.scala b/tests/neg-custom-args/captures/stack-alloc.scala index 027f2b3e005c..71b544dbe88d 100644 --- a/tests/neg-custom-args/captures/stack-alloc.scala +++ b/tests/neg-custom-args/captures/stack-alloc.scala @@ -5,7 +5,7 @@ class Pooled val stack = mutable.ArrayBuffer[Pooled]() var nextFree = 0 -def withFreshPooled[T](op: Pooled^ => T): T = +def withFreshPooled[sealed T](op: Pooled^ => T): T = if nextFree >= stack.size then stack.append(new Pooled) val pooled = stack(nextFree) nextFree = nextFree + 1 diff --git a/tests/neg-custom-args/captures/try.check b/tests/neg-custom-args/captures/try.check index f85ca468e6e5..4af370bfba1a 100644 --- a/tests/neg-custom-args/captures/try.check +++ b/tests/neg-custom-args/captures/try.check @@ -1,12 +1,10 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:23:49 ------------------------------------------ +-- Error: tests/neg-custom-args/captures/try.scala:23:16 --------------------------------------------------------------- 23 | val a = handle[Exception, CanThrow[Exception]] { // error - | ^ - | Found: CT[Exception]^ ->? CanThrow[Exception] - | Required: CanThrow[Exception] => box CT[Exception]^ -24 | (x: CanThrow[Exception]) => x -25 | }{ - | - | longer explanation available when compiling with `-explain` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Sealed type variable R cannot be instantiated to box CT[Exception]^ since + | that type captures the root capability `cap`. + | This is often caused by a local capability in the body of method handle + | leaking as part of its result. -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/try.scala:29:43 ------------------------------------------ 29 | val b = handle[Exception, () -> Nothing] { // error | ^ @@ -22,7 +20,7 @@ 49 | () => 50 | raise(new Exception)(using x) 51 | 22 -52 |} { // error +52 |} { // error | ^ | Found: () ->{x$0} Int | Required: () -> Int @@ -30,15 +28,10 @@ 54 |} | | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/try.scala:40:4 ---------------------------------------------------------------- -35 | val xx = handle { -36 | (x: CanThrow[Exception]) => -37 | () => -38 | raise(new Exception)(using x) -39 | 22 -40 | } { // error - | ^ - | The expression's type box () => Int is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. -41 | (ex: Exception) => () => 22 -42 | } +-- Error: tests/neg-custom-args/captures/try.scala:35:11 --------------------------------------------------------------- +35 | val xx = handle { // error + | ^^^^^^ + | Sealed type variable R cannot be instantiated to box () => Int since + | that type captures the root capability `cap`. + | This is often caused by a local capability in the body of method handle + | leaking as part of its result. diff --git a/tests/neg-custom-args/captures/try.scala b/tests/neg-custom-args/captures/try.scala index ef375c62b817..3c6f0605d8b9 100644 --- a/tests/neg-custom-args/captures/try.scala +++ b/tests/neg-custom-args/captures/try.scala @@ -14,7 +14,7 @@ def raise[E <: Exception](e: E): Nothing throws E = throw e def foo(x: Boolean): Int throws Fail = if x then 1 else raise(Fail()) -def handle[E <: Exception, R <: Top](op: CanThrow[E] => R)(handler: E => R): R = +def handle[E <: Exception, sealed R <: Top](op: CanThrow[E] => R)(handler: E => R): R = val x: CanThrow[E] = ??? try op(x) catch case ex: E => handler(ex) @@ -32,12 +32,12 @@ def test = (ex: Exception) => ??? } - val xx = handle { + val xx = handle { // error (x: CanThrow[Exception]) => () => raise(new Exception)(using x) 22 - } { // error + } { (ex: Exception) => () => 22 } val yy = xx :: Nil @@ -49,6 +49,6 @@ val global: () -> Int = handle { () => raise(new Exception)(using x) 22 -} { // error +} { // error (ex: Exception) => () => 22 } diff --git a/tests/neg-custom-args/captures/try3.scala b/tests/neg-custom-args/captures/try3.scala index 5a569aa5fd8d..4c6835353c3f 100644 --- a/tests/neg-custom-args/captures/try3.scala +++ b/tests/neg-custom-args/captures/try3.scala @@ -4,7 +4,7 @@ class CT[E] type CanThrow[E] = CT[E]^ type Top = Any^ -def handle[E <: Exception, T <: Top](op: CanThrow[E] ?=> T)(handler: E => T): T = +def handle[E <: Exception, sealed T <: Top](op: CanThrow[E] ?=> T)(handler: E => T): T = val x: CanThrow[E] = ??? try op(using x) catch case ex: E => handler(ex) @@ -14,12 +14,12 @@ def raise[E <: Exception](ex: E)(using CanThrow[E]): Nothing = @main def Test: Int = def f(a: Boolean) = - handle { + handle { // error if !a then raise(IOException()) (b: Boolean) => if !b then raise(IOException()) 0 - } { // error + } { ex => (b: Boolean) => -1 } val g = f(true) diff --git a/tests/neg-custom-args/captures/unbox.scala b/tests/neg-custom-args/captures/unbox.scala index 18a86a1237ff..33702a954068 100644 --- a/tests/neg-custom-args/captures/unbox.scala +++ b/tests/neg-custom-args/captures/unbox.scala @@ -1,3 +1,4 @@ +import language.`3.2` type Proc = () => Unit val xs: List[Proc] = ??? diff --git a/tests/neg-custom-args/captures/usingLogFile.check b/tests/neg-custom-args/captures/usingLogFile.check index 17b9146ce71c..f9230e37a1cf 100644 --- a/tests/neg-custom-args/captures/usingLogFile.check +++ b/tests/neg-custom-args/captures/usingLogFile.check @@ -1,39 +1,54 @@ --- Error: tests/neg-custom-args/captures/usingLogFile.scala:33:2 ------------------------------------------------------- -33 | later3() // error - | ^^^^^^ - | box () => Unit cannot be box-converted to a type that can be selected or applied - | since one of their capture sets contains the root capability `cap` --- Error: tests/neg-custom-args/captures/usingLogFile.scala:37:9 ------------------------------------------------------- -37 | later4.x() // error - | ^^^^^^^^ - | The expression's type box () => Unit is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. --- Error: tests/neg-custom-args/captures/usingLogFile.scala:23:6 ------------------------------------------------------- +-- Error: tests/neg-custom-args/captures/usingLogFile.scala:31:6 ------------------------------------------------------- +31 | var later3: () => Unit = () => () // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Mutable variable later3 cannot have type box () => Unit since + | that type captures the root capability `cap`. + | This restriction serves to prevent local capabilities from escaping the scope where they are defined. +-- Error: tests/neg-custom-args/captures/usingLogFile.scala:35:6 ------------------------------------------------------- +35 | var later4: Cell[() => Unit] = Cell(() => ()) // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Mutable variable later4 cannot have type Test2.Cell[() => Unit] since + | the part () => Unit of that type captures the root capability `cap`. + | This restriction serves to prevent local capabilities from escaping the scope where they are defined. +-- Error: tests/neg-custom-args/captures/usingLogFile.scala:23:14 ------------------------------------------------------ 23 | val later = usingLogFile { f => () => f.write(0) } // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Non-local value later cannot have an inferred type - | () ->{x$0} Unit - | with non-empty capture set {x$0}. - | The type needs to be declared explicitly. --- Error: tests/neg-custom-args/captures/usingLogFile.scala:29:9 ------------------------------------------------------- -29 | later2.x() // error - | ^^^^^^^^ - | The expression's type box () => Unit is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. + | ^^^^^^^^^^^^ + | Sealed type variable T cannot be instantiated to box () => Unit since + | that type captures the root capability `cap`. + | This is often caused by a local capability in the body of method usingLogFile + | leaking as part of its result. +-- Error: tests/neg-custom-args/captures/usingLogFile.scala:28:23 ------------------------------------------------------ +28 | private val later2 = usingLogFile { f => Cell(() => f.write(0)) } // error + | ^^^^^^^^^^^^ + | Sealed type variable T cannot be instantiated to box Test2.Cell[() => Unit]^? since + | the part () => Unit of that type captures the root capability `cap`. + | This is often caused by a local capability in the body of method usingLogFile + | leaking as part of its result. -- Error: tests/neg-custom-args/captures/usingLogFile.scala:47:6 ------------------------------------------------------- 47 | val later = usingLogFile { f => () => f.write(0) } // error | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | Non-local value later cannot have an inferred type - | () ->{x$0} Unit - | with non-empty capture set {x$0}. + | () => Unit + | with non-empty capture set {x$0, cap}. | The type needs to be declared explicitly. --- Error: tests/neg-custom-args/captures/usingLogFile.scala:62:25 ------------------------------------------------------ +-- Error: tests/neg-custom-args/captures/usingLogFile.scala:62:16 ------------------------------------------------------ 62 | val later = usingFile("out", f => (y: Int) => xs.foreach(x => f.write(x + y))) // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | The expression's type box (x$0: Int) => Unit is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. --- Error: tests/neg-custom-args/captures/usingLogFile.scala:71:25 ------------------------------------------------------ -71 | val later = usingFile("logfile", usingLogger(_, l => () => l.log("test"))) // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | The expression's type box () => Unit is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. + | ^^^^^^^^^ + | Sealed type variable T cannot be instantiated to box (x$0: Int) => Unit since + | that type captures the root capability `cap`. + | This is often caused by a local capability in the body of method usingFile + | leaking as part of its result. +-- Error: tests/neg-custom-args/captures/usingLogFile.scala:71:16 ------------------------------------------------------ +71 | val later = usingFile("logfile", // error + | ^^^^^^^^^ + | Sealed type variable T cannot be instantiated to box () => Unit since + | that type captures the root capability `cap`. + | This is often caused by a local capability in the body of method usingFile + | leaking as part of its result. +-- Error: tests/neg-custom-args/captures/usingLogFile.scala:72:6 ------------------------------------------------------- +72 | usingLogger(_, l => () => l.log("test"))) // error + | ^^^^^^^^^^^ + | Sealed type variable T cannot be instantiated to box () => Unit since + | that type captures the root capability `cap`. + | This is often caused by a local capability in the body of method usingLogger + | leaking as part of its result. diff --git a/tests/neg-custom-args/captures/usingLogFile.scala b/tests/neg-custom-args/captures/usingLogFile.scala index 1e281f130b8a..40c90b934464 100644 --- a/tests/neg-custom-args/captures/usingLogFile.scala +++ b/tests/neg-custom-args/captures/usingLogFile.scala @@ -3,7 +3,7 @@ import annotation.capability object Test1: - def usingLogFile[T](op: FileOutputStream => T): T = + def usingLogFile[sealed T](op: FileOutputStream => T): T = val logFile = FileOutputStream("log") val result = op(logFile) logFile.close() @@ -14,7 +14,7 @@ object Test1: object Test2: - def usingLogFile[T](op: FileOutputStream^ => T): T = + def usingLogFile[sealed T](op: FileOutputStream^ => T): T = val logFile = FileOutputStream("log") val result = op(logFile) logFile.close() @@ -25,20 +25,20 @@ object Test2: class Cell[+T](val x: T) - private val later2 = usingLogFile { f => Cell(() => f.write(0)) } - later2.x() // error + private val later2 = usingLogFile { f => Cell(() => f.write(0)) } // error + later2.x() - var later3: () => Unit = () => () + var later3: () => Unit = () => () // error usingLogFile { f => later3 = () => f.write(0) } - later3() // error + later3() - var later4: Cell[() => Unit] = Cell(() => ()) + var later4: Cell[() => Unit] = Cell(() => ()) // error usingLogFile { f => later4 = Cell(() => f.write(0)) } - later4.x() // error + later4.x() object Test3: - def usingLogFile[T](op: FileOutputStream^ => T) = + def usingLogFile[sealed T](op: FileOutputStream^ => T) = val logFile = FileOutputStream("log") val result = op(logFile) logFile.close() @@ -50,7 +50,7 @@ object Test4: class Logger(f: OutputStream^): def log(msg: String): Unit = ??? - def usingFile[T](name: String, op: OutputStream^ => T): T = + def usingFile[sealed T](name: String, op: OutputStream^ => T): T = val f = new FileOutputStream(name) val result = op(f) f.close() @@ -63,10 +63,11 @@ object Test4: later(1) - def usingLogger[T](f: OutputStream^, op: Logger^{f} => T): T = + def usingLogger[sealed T](f: OutputStream^, op: Logger^{f} => T): T = val logger = Logger(f) op(logger) def test = - val later = usingFile("logfile", usingLogger(_, l => () => l.log("test"))) // error + val later = usingFile("logfile", // error + usingLogger(_, l => () => l.log("test"))) // error later() diff --git a/tests/neg-custom-args/captures/vars.check b/tests/neg-custom-args/captures/vars.check index 565101359673..e7055c810bb0 100644 --- a/tests/neg-custom-args/captures/vars.check +++ b/tests/neg-custom-args/captures/vars.check @@ -1,3 +1,15 @@ +-- Error: tests/neg-custom-args/captures/vars.scala:13:6 --------------------------------------------------------------- +13 | var a: String => String = f // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Mutable variable a cannot have type box String => String since + | that type captures the root capability `cap`. + | This restriction serves to prevent local capabilities from escaping the scope where they are defined. +-- Error: tests/neg-custom-args/captures/vars.scala:14:6 --------------------------------------------------------------- +14 | var b: List[String => String] = Nil // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Mutable variable b cannot have type List[String => String] since + | the part String => String of that type captures the root capability `cap`. + | This restriction serves to prevent local capabilities from escaping the scope where they are defined. -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars.scala:11:24 ----------------------------------------- 11 | val z2c: () -> Unit = z2 // error | ^^ @@ -5,28 +17,10 @@ | Required: () -> Unit | | longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/vars.scala:15:10 ----------------------------------------- -15 | val u = a // error - | ^ - | Found: (a : box String => String) - | Required: (x$0: String) => String - | - | longer explanation available when compiling with `-explain` --- Error: tests/neg-custom-args/captures/vars.scala:16:2 --------------------------------------------------------------- -16 | a("") // error - | ^ - | box String => String cannot be box-converted to a type that can be selected or applied - | since one of their capture sets contains the root capability `cap` --- Error: tests/neg-custom-args/captures/vars.scala:17:4 --------------------------------------------------------------- -17 | b.head // error - | ^^^^^^ - | The expression's type box String => String is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. --- Error: tests/neg-custom-args/captures/vars.scala:32:8 --------------------------------------------------------------- +-- Error: tests/neg-custom-args/captures/vars.scala:32:2 --------------------------------------------------------------- 32 | local { cap3 => // error - | ^ - | The expression's type box (x$0: String) => String is not allowed to capture the root capability `cap`. - | This usually means that a capability persists longer than its allowed lifetime. -33 | def g(x: String): String = if cap3 == cap3 then "" else "a" -34 | g -35 | } + | ^^^^^ + | Sealed type variable T cannot be instantiated to box (x$0: String) => String since + | that type captures the root capability `cap`. + | This is often caused by a local capability in the body of method local + | leaking as part of its result. diff --git a/tests/neg-custom-args/captures/vars.scala b/tests/neg-custom-args/captures/vars.scala index e437d087d5a2..b7761952167e 100644 --- a/tests/neg-custom-args/captures/vars.scala +++ b/tests/neg-custom-args/captures/vars.scala @@ -10,11 +10,11 @@ def test(cap1: Cap, cap2: Cap) = val z2 = () => { x = identity } val z2c: () -> Unit = z2 // error - var a: String => String = f // was error, now OK - var b: List[String => String] = Nil // was error, now OK - val u = a // error - a("") // error - b.head // error + var a: String => String = f // error + var b: List[String => String] = Nil // error + val u = a // was error, now ok + a("") // was error, now ok + b.head // was error, now ok def scope = val cap3: Cap = CC() @@ -27,7 +27,7 @@ def test(cap1: Cap, cap2: Cap) = val s = scope val sc: String => String = scope - def local[T](op: Cap -> T): T = op(CC()) + def local[sealed T](op: Cap -> T): T = op(CC()) local { cap3 => // error def g(x: String): String = if cap3 == cap3 then "" else "a" diff --git a/tests/pos-custom-args/captures/capt-env.scala b/tests/pos-custom-args/captures/capt-env.scala new file mode 100644 index 000000000000..be24ed618606 --- /dev/null +++ b/tests/pos-custom-args/captures/capt-env.scala @@ -0,0 +1,8 @@ +class C +type Cap = C^ + +def test(c: Cap) = + def x = () => () => c; () + def y = () => x() + def z = () => x()() + diff --git a/tests/neg-custom-args/captures/i15749.scala b/tests/pos-custom-args/captures/i15749.scala similarity index 73% rename from tests/neg-custom-args/captures/i15749.scala rename to tests/pos-custom-args/captures/i15749.scala index 32947c146021..4959c003a918 100644 --- a/tests/neg-custom-args/captures/i15749.scala +++ b/tests/pos-custom-args/captures/i15749.scala @@ -12,4 +12,4 @@ type BoxedLazyVal[T] = Foo[LazyVal[T]] def force[A](v: BoxedLazyVal[A]): A = // Γ ⊢ v.x : □ {cap} Unit -> A - v.x(unit) // error: (unbox v.x)(unit), where (unbox v.x) should be untypable \ No newline at end of file + v.x(unit) // was error: (unbox v.x)(unit), where (unbox v.x) should be untypable, now ok \ No newline at end of file diff --git a/tests/neg-custom-args/captures/i15749a.scala b/tests/pos-custom-args/captures/i15749a.scala similarity index 62% rename from tests/neg-custom-args/captures/i15749a.scala rename to tests/pos-custom-args/captures/i15749a.scala index e4d70efa5206..fe5f4d75dae1 100644 --- a/tests/neg-custom-args/captures/i15749a.scala +++ b/tests/pos-custom-args/captures/i15749a.scala @@ -1,5 +1,5 @@ class Unit -object unit extends Unit +object u extends Unit type Top = Any^ @@ -10,12 +10,12 @@ def test = def wrapper[T](x: T): Wrapper[T] = [X] => (op: T ->{cap} X) => op(x) - def strictMap[A <: Top, B <: Top](mx: Wrapper[A])(f: A ->{cap} B): Wrapper[B] = + def strictMap[A <: Top, sealed B <: Top](mx: Wrapper[A])(f: A ->{cap} B): Wrapper[B] = mx((x: A) => wrapper(f(x))) - def force[A](thunk: Unit ->{cap} A): A = thunk(unit) + def force[A](thunk: Unit ->{cap} A): A = thunk(u) - def forceWrapper[A](mx: Wrapper[Unit ->{cap} A]): Wrapper[A] = + def forceWrapper[sealed A](mx: Wrapper[Unit ->{cap} A]): Wrapper[A] = // Γ ⊢ mx: Wrapper[□ {cap} Unit => A] // `force` should be typed as ∀(□ {cap} Unit -> A) A, but it can not strictMap[Unit ->{cap} A, A](mx)(t => force[A](t)) // error diff --git a/tests/neg-custom-args/captures/i15923-cases.scala b/tests/pos-custom-args/captures/i15923-cases.scala similarity index 89% rename from tests/neg-custom-args/captures/i15923-cases.scala rename to tests/pos-custom-args/captures/i15923-cases.scala index 9a103c976a8e..136b8950eb26 100644 --- a/tests/neg-custom-args/captures/i15923-cases.scala +++ b/tests/pos-custom-args/captures/i15923-cases.scala @@ -3,7 +3,7 @@ type Id[X] = [T] -> (op: X => T) -> T def mkId[X](x: X): Id[X] = [T] => (op: X => T) => op(x) def foo(x: Id[Cap^{cap}]) = { - x(_.use()) // error + x(_.use()) // was error, now OK } def bar(io: Cap^{cap}, x: Id[Cap^{io}]) = { diff --git a/tests/neg-custom-args/captures/i15925.scala b/tests/pos-custom-args/captures/i15925.scala similarity index 69% rename from tests/neg-custom-args/captures/i15925.scala rename to tests/pos-custom-args/captures/i15925.scala index 433d27a98414..63b6962ff9f8 100644 --- a/tests/neg-custom-args/captures/i15925.scala +++ b/tests/pos-custom-args/captures/i15925.scala @@ -1,13 +1,13 @@ import language.experimental.captureChecking class Unit -object unit extends Unit +object u extends Unit type Foo[X] = [T] -> (op: X => T) -> T type Lazy[X] = Unit => X def force[X](fx: Foo[Lazy[X]]): X = - fx[X](f => f(unit)) // error + fx[X](f => f(u)) def force2[X](fx: Foo[Unit => X]): X = - fx[X](f => f(unit)) // error + fx[X](f => f(u)) diff --git a/tests/pos-custom-args/captures/unsafe-unbox.scala b/tests/pos-custom-args/captures/unsafe-unbox.scala index b228d8c07925..63a32d58f640 100644 --- a/tests/pos-custom-args/captures/unsafe-unbox.scala +++ b/tests/pos-custom-args/captures/unsafe-unbox.scala @@ -1,4 +1,7 @@ -import caps.unsafe.* +import annotation.unchecked.uncheckedCaptures def test = + @uncheckedCaptures var finalizeActions = collection.mutable.ListBuffer[() => Unit]() - val action = finalizeActions.remove(0).unsafeUnbox + val action = finalizeActions.remove(0) + + diff --git a/tests/pos-custom-args/captures/vars1.scala b/tests/pos-custom-args/captures/vars1.scala index c008bac2e72f..56548e5a9c30 100644 --- a/tests/pos-custom-args/captures/vars1.scala +++ b/tests/pos-custom-args/captures/vars1.scala @@ -1,9 +1,12 @@ import caps.unsafe.* +import annotation.unchecked.uncheckedCaptures object Test: type ErrorHandler = (Int, String) => Unit + @uncheckedCaptures var defaultIncompleteHandler: ErrorHandler = ??? + @uncheckedCaptures var incompleteHandler: ErrorHandler = defaultIncompleteHandler val x = incompleteHandler.unsafeUnbox val _ : ErrorHandler = x @@ -11,11 +14,17 @@ object Test: def defaultIncompleteHandler1(): ErrorHandler = ??? val defaultIncompleteHandler2: ErrorHandler = ??? - var incompleteHandler1: ErrorHandler = defaultIncompleteHandler1().unsafeBox - var incompleteHandler2: ErrorHandler = defaultIncompleteHandler2.unsafeBox - private var incompleteHandler7 = defaultIncompleteHandler1().unsafeBox - private var incompleteHandler8 = defaultIncompleteHandler2.unsafeBox + @uncheckedCaptures + var incompleteHandler1: ErrorHandler = defaultIncompleteHandler1() + @uncheckedCaptures + var incompleteHandler2: ErrorHandler = defaultIncompleteHandler2 + @uncheckedCaptures + private var incompleteHandler7 = defaultIncompleteHandler1() + @uncheckedCaptures + private var incompleteHandler8 = defaultIncompleteHandler2 + + incompleteHandler1 = defaultIncompleteHandler2 + incompleteHandler1 = defaultIncompleteHandler2 + val saved = incompleteHandler1 + - incompleteHandler1 = defaultIncompleteHandler2.unsafeBox - incompleteHandler1 = defaultIncompleteHandler2.unsafeBox - val saved = incompleteHandler1.unsafeUnbox diff --git a/tests/pos-with-compiler-cc/dotc/Run.scala b/tests/pos-with-compiler-cc/dotc/Run.scala index 16a955afca1a..96f8c6a7b06f 100644 --- a/tests/pos-with-compiler-cc/dotc/Run.scala +++ b/tests/pos-with-compiler-cc/dotc/Run.scala @@ -32,7 +32,7 @@ import scala.collection.mutable import scala.util.control.NonFatal import scala.io.Codec import annotation.constructorOnly -import caps.unsafe.unsafeUnbox +import annotation.unchecked.uncheckedCaptures /** A compiler run. Exports various methods to compile source files */ class Run(comp: Compiler, @constructorOnly ictx0: Context) extends ImplicitRunInfo with ConstraintRunInfo { @@ -165,6 +165,7 @@ class Run(comp: Compiler, @constructorOnly ictx0: Context) extends ImplicitRunIn val staticRefs = util.EqHashMap[Name, Denotation](initialCapacity = 1024) /** Actions that need to be performed at the end of the current compilation run */ + @uncheckedCaptures private var finalizeActions = mutable.ListBuffer[() => Unit]() /** Will be set to true if any of the compiled compilation units contains @@ -275,7 +276,7 @@ class Run(comp: Compiler, @constructorOnly ictx0: Context) extends ImplicitRunIn Rewrites.writeBack() suppressions.runFinished(hasErrors = ctx.reporter.hasErrors) while (finalizeActions.nonEmpty) { - val action = finalizeActions.remove(0).unsafeUnbox + val action = finalizeActions.remove(0) action() } compiling = false diff --git a/tests/pos-with-compiler-cc/dotc/core/Scopes.scala b/tests/pos-with-compiler-cc/dotc/core/Scopes.scala index 3b4cf9a98c54..7ab68ddf78a2 100644 --- a/tests/pos-with-compiler-cc/dotc/core/Scopes.scala +++ b/tests/pos-with-compiler-cc/dotc/core/Scopes.scala @@ -1,8 +1,3 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2012 LAMP/EPFL - * @author Martin Odersky - */ - package dotty.tools package dotc package core @@ -17,6 +12,7 @@ import Denotations._ import printing.Texts._ import printing.Printer import SymDenotations.NoDenotation +import annotation.unchecked.uncheckedCaptures import collection.mutable @@ -220,6 +216,7 @@ object Scopes { private var elemsCache: List[Symbol] | Null = null /** The synthesizer to be used, or `null` if no synthesis is done on this scope */ + @uncheckedCaptures private var synthesize: SymbolSynthesizer | Null = null /** Use specified synthesize for this scope */ diff --git a/tests/pos-with-compiler-cc/dotc/core/TypeComparer.scala b/tests/pos-with-compiler-cc/dotc/core/TypeComparer.scala index 67b9f063e9d0..0e1fc277865a 100644 --- a/tests/pos-with-compiler-cc/dotc/core/TypeComparer.scala +++ b/tests/pos-with-compiler-cc/dotc/core/TypeComparer.scala @@ -25,7 +25,7 @@ import reporting.trace import annotation.constructorOnly import cc.{CapturingType, derivedCapturingType, CaptureSet, stripCapturing, isBoxedCapturing, boxed, boxedUnlessFun, boxedIfTypeParam, isAlwaysPure} import language.experimental.pureFunctions -import caps.unsafe.* +import annotation.unchecked.uncheckedCaptures /** Provides methods to compare types. */ @@ -33,17 +33,18 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling import TypeComparer._ Stats.record("TypeComparer") - private var myContext: Context = initctx.unsafeBox - def comparerContext: Context = myContext.unsafeUnbox + @uncheckedCaptures + private var myContext: Context = initctx + def comparerContext: Context = myContext - protected given [DummySoItsADef]: Context = myContext.unsafeUnbox + protected given [DummySoItsADef]: Context = myContext protected var state: TyperState = compiletime.uninitialized def constraint: Constraint = state.constraint def constraint_=(c: Constraint): Unit = state.constraint = c def init(c: Context): Unit = - myContext = c.unsafeBox + myContext = c state = c.typerState monitored = false GADTused = false diff --git a/tests/pos-with-compiler-cc/dotc/core/tasty/TreeUnpickler.scala b/tests/pos-with-compiler-cc/dotc/core/tasty/TreeUnpickler.scala index b87cde4a6ad1..fcc449af3632 100644 --- a/tests/pos-with-compiler-cc/dotc/core/tasty/TreeUnpickler.scala +++ b/tests/pos-with-compiler-cc/dotc/core/tasty/TreeUnpickler.scala @@ -47,7 +47,7 @@ import dotty.tools.tasty.TastyFormat._ import scala.annotation.constructorOnly import scala.annotation.internal.sharable import language.experimental.pureFunctions -import caps.unsafe.{unsafeUnbox, unsafeBox} +import annotation.unchecked.uncheckedCaptures /** Unpickler for typed trees * @param reader the reader from which to unpickle @@ -1086,15 +1086,15 @@ class TreeUnpickler(reader: TastyReader, def readIndexedStats[T](exprOwner: Symbol, end: Addr, k: (List[Tree], Context) => T = sameTrees)(using Context): T = val buf = new mutable.ListBuffer[Tree] - var curCtx = ctx.unsafeBox + @uncheckedCaptures var curCtx = ctx while currentAddr.index < end.index do - val stat = readIndexedStat(exprOwner)(using curCtx.unsafeUnbox) + val stat = readIndexedStat(exprOwner)(using curCtx) buf += stat stat match - case stat: Import => curCtx = curCtx.unsafeUnbox.importContext(stat, stat.symbol).unsafeBox + case stat: Import => curCtx = curCtx.importContext(stat, stat.symbol) case _ => assert(currentAddr.index == end.index) - k(buf.toList, curCtx.unsafeUnbox) + k(buf.toList, curCtx) def readStats[T](exprOwner: Symbol, end: Addr, k: (List[Tree], Context) => T = sameTrees)(using Context): T = { fork.indexStats(end) diff --git a/tests/pos-with-compiler-cc/dotc/typer/Namer.scala b/tests/pos-with-compiler-cc/dotc/typer/Namer.scala index 548f645a23d9..8487192b9d8a 100644 --- a/tests/pos-with-compiler-cc/dotc/typer/Namer.scala +++ b/tests/pos-with-compiler-cc/dotc/typer/Namer.scala @@ -29,7 +29,7 @@ import TypeErasure.erasure import reporting._ import config.Feature.sourceVersion import config.SourceVersion._ - +import annotation.unchecked.uncheckedCaptures /** This class creates symbols from definitions and imports and gives them * lazy types. @@ -930,6 +930,8 @@ class Namer { typer: Typer => class TypeDefCompleter(original: TypeDef)(ictx: DetachedContext) extends Completer(original)(ictx) with TypeParamsCompleter { private var myTypeParams: List[TypeSymbol] | Null = null + + @uncheckedCaptures private var nestedCtx: Context | Null = null assert(!original.isClassDef) diff --git a/tests/pos-with-compiler-cc/dotc/typer/Typer.scala b/tests/pos-with-compiler-cc/dotc/typer/Typer.scala index aa286446a334..0baae1730f4a 100644 --- a/tests/pos-with-compiler-cc/dotc/typer/Typer.scala +++ b/tests/pos-with-compiler-cc/dotc/typer/Typer.scala @@ -54,7 +54,8 @@ import config.Config import language.experimental.pureFunctions import scala.annotation.constructorOnly -import caps.unsafe.{unsafeBox, unsafeUnbox} +import annotation.unchecked.uncheckedCaptures + object Typer { @@ -1673,11 +1674,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer * and the patterns of the Match tree and the MatchType correspond. */ def typedDependentMatchFinish(tree: untpd.Match, sel: Tree, wideSelType: Type, cases: List[untpd.CaseDef], pt: MatchType)(using Context): Tree = { - var caseCtx = ctx.unsafeBox + @uncheckedCaptures var caseCtx = ctx val cases1 = tree.cases.zip(pt.cases) .map { case (cas, tpe) => - val case1 = typedCase(cas, sel, wideSelType, tpe)(using caseCtx.unsafeUnbox) - caseCtx = Nullables.afterPatternContext(sel, case1.pat).unsafeBox + val case1 = typedCase(cas, sel, wideSelType, tpe)(using caseCtx) + caseCtx = Nullables.afterPatternContext(sel, case1.pat) case1 } .asInstanceOf[List[CaseDef]] @@ -1692,10 +1693,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } def typedCases(cases: List[untpd.CaseDef], sel: Tree, wideSelType: Type, pt: Type)(using Context): List[CaseDef] = - var caseCtx = ctx.unsafeBox + @uncheckedCaptures var caseCtx = ctx cases.mapconserve { cas => - val case1 = typedCase(cas, sel, wideSelType, pt)(using caseCtx.unsafeUnbox) - caseCtx = Nullables.afterPatternContext(sel, case1.pat).unsafeBox + val case1 = typedCase(cas, sel, wideSelType, pt)(using caseCtx) + caseCtx = Nullables.afterPatternContext(sel, case1.pat) case1 } From d96b65c16b4671ba24b9656ae57aa082623665f8 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 5 May 2023 12:46:25 +0200 Subject: [PATCH 554/657] Deprecate unsafe{Box,Unbox} methods --- library/src/scala/caps.scala | 33 +++++++++++-------- .../no-experimental/dotty-experimental.scala | 2 +- .../dotc/transform/init/Semantic.scala | 3 +- .../dotc/typer/TypeAssigner.scala | 3 +- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/library/src/scala/caps.scala b/library/src/scala/caps.scala index 4981353d61be..866e5dbd18cd 100644 --- a/library/src/scala/caps.scala +++ b/library/src/scala/caps.scala @@ -13,20 +13,25 @@ import annotation.experimental object unsafe: - /** If argument is of type `cs T`, converts to type `box cs T`. This - * avoids the error that would be raised when boxing `*`. - */ - extension [T](x: T) def unsafeBox: T = x - - /** If argument is of type `box cs T`, converts to type `cs T`. This - * avoids the error that would be raised when unboxing `*`. - */ - extension [T](x: T) def unsafeUnbox: T = x - - /** If argument is of type `box cs T`, converts to type `cs T`. This - * avoids the error that would be raised when unboxing `*`. - */ - extension [T, U](f: T => U) def unsafeBoxFunArg: T => U = f + extension [T](x: T) + /** If argument is of type `cs T`, converts to type `box cs T`. This + * avoids the error that would be raised when boxing `*`. + */ + @deprecated(since = "3.3") + def unsafeBox: T = x + + /** If argument is of type `box cs T`, converts to type `cs T`. This + * avoids the error that would be raised when unboxing `*`. + */ + @deprecated(since = "3.3") + def unsafeUnbox: T = x + + extension [T, U](f: T => U) + /** If argument is of type `box cs T`, converts to type `cs T`. This + * avoids the error that would be raised when unboxing `*`. + */ + @deprecated(since = "3.3") + def unsafeBoxFunArg: T => U = f end unsafe diff --git a/tests/pos-custom-args/no-experimental/dotty-experimental.scala b/tests/pos-custom-args/no-experimental/dotty-experimental.scala index 320c68dbea50..72d16ddd9b15 100644 --- a/tests/pos-custom-args/no-experimental/dotty-experimental.scala +++ b/tests/pos-custom-args/no-experimental/dotty-experimental.scala @@ -1,6 +1,6 @@ package dotty.tools object test { - val x = caps.unsafe.unsafeBox + val x = caps.cap } diff --git a/tests/pos-with-compiler-cc/dotc/transform/init/Semantic.scala b/tests/pos-with-compiler-cc/dotc/transform/init/Semantic.scala index 14873938bf78..6ddc0e18d995 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/init/Semantic.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/init/Semantic.scala @@ -18,7 +18,6 @@ import Errors.* import scala.collection.mutable import scala.annotation.tailrec -import caps.unsafe.unsafeBoxFunArg object Semantic: @@ -1670,7 +1669,7 @@ object Semantic: } // initialize super classes after outers are set - tasks.foreach(((task: () => Unit) => task()).unsafeBoxFunArg) + tasks.foreach((task: () => Unit) => task()) // !cc! .asInstanceOf needed to convert from `(() => Unit) -> Unit` to `(box () => Unit) -> Unit`. end if diff --git a/tests/pos-with-compiler-cc/dotc/typer/TypeAssigner.scala b/tests/pos-with-compiler-cc/dotc/typer/TypeAssigner.scala index 2edcc37b613d..cb7bf2f2505d 100644 --- a/tests/pos-with-compiler-cc/dotc/typer/TypeAssigner.scala +++ b/tests/pos-with-compiler-cc/dotc/typer/TypeAssigner.scala @@ -12,7 +12,6 @@ import collection.mutable import reporting._ import Checking.{checkNoPrivateLeaks, checkNoWildcard} import cc.CaptureSet -import caps.unsafe.unsafeBoxFunArg trait TypeAssigner { import tpd.* @@ -27,7 +26,7 @@ trait TypeAssigner { qual.isEmpty || sym.name == qual || sym.is(Module) && sym.name.stripModuleClassSuffix == qual) - ctx.outersIterator.map(((ctx: Context) => ctx.owner).unsafeBoxFunArg).find(qualifies) match { + ctx.outersIterator.map((ctx: Context) => ctx.owner).find(qualifies) match { case Some(c) if packageOK || !c.is(Package) => c case _ => From 60c541e56484588d9e855e9512d9dfdbe30c8eb2 Mon Sep 17 00:00:00 2001 From: odersky Date: Fri, 5 May 2023 15:02:06 +0200 Subject: [PATCH 555/657] Update MiMaFilters --- project/MiMaFilters.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index 5009d68a7247..c5b00510bece 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -6,9 +6,11 @@ object MiMaFilters { ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.unsafeBox"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.unsafeUnbox"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.cap"), + ProblemFilters.exclude[MissingClassProblem]("scala.caps$Sealed"), ProblemFilters.exclude[DirectMissingMethodProblem]("scala.CanEqual.canEqualMap"), ProblemFilters.exclude[MissingClassProblem]("scala.caps$Pure"), ProblemFilters.exclude[MissingClassProblem]("scala.caps$unsafe$"), + ProblemFilters.exclude[MissingClassProblem]("scala.annotation.unchecked.uncheckedCaptures"), ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.3.3-migration"), ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.3.3"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$3$u002E3$"), From a8f583d17b1dcdbbecf87dbb43d1026aeebec06f Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 10 May 2023 18:32:42 +0200 Subject: [PATCH 556/657] Change * to cap in compiler comments and one output string --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 4 ++-- compiler/src/dotty/tools/dotc/cc/CaptureSet.scala | 4 ++-- compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala | 6 +++--- compiler/src/dotty/tools/dotc/cc/Setup.scala | 4 ++-- compiler/src/dotty/tools/dotc/cc/Synthetics.scala | 6 +++--- compiler/src/dotty/tools/dotc/core/Definitions.scala | 2 +- compiler/src/dotty/tools/dotc/core/Flags.scala | 2 +- compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index a2e3277c9f81..2d335d1ed380 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -464,8 +464,8 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] } } - /** Under pureFunctions: A builder and extractor for `=> T`, which is an alias for `{*}-> T`. - * Only trees of the form `=> T` are matched; trees written directly as `{*}-> T` + /** Under pureFunctions: A builder and extractor for `=> T`, which is an alias for `->{cap} T`. + * Only trees of the form `=> T` are matched; trees written directly as `->{cap} T` * are ignored by the extractor. */ object ImpureByNameTypeTree: diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index 509fecd4d3b4..ce903acab411 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -319,7 +319,7 @@ object CaptureSet: /** The empty capture set `{}` */ val empty: CaptureSet.Const = Const(emptySet) - /** The universal capture set `{*}` */ + /** The universal capture set `{cap}` */ def universal(using Context): CaptureSet = defn.captureRoot.termRef.singletonCaptureSet @@ -429,7 +429,7 @@ object CaptureSet: /** Roughly: the intersection of all constant known supersets of this set. * The aim is to find an as-good-as-possible constant set that is a superset - * of this set. The universal set {*} is a sound fallback. + * of this set. The universal set {cap} is a sound fallback. */ final def upperApprox(origin: CaptureSet)(using Context): CaptureSet = if computingApprox then universal diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 9e0263d63b38..6ec360eca938 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -884,7 +884,7 @@ class CheckCaptures extends Recheck, SymTransformer: /** Check that self types of subclasses conform to self types of super classes. * (See comment below how this is achieved). The check assumes that classes - * without an explicit self type have the universal capture set `{*}` on the + * without an explicit self type have the universal capture set `{cap}` on the * self type. If a class without explicit self type is not `effectivelyFinal` * it is checked that the inferred self type is universal, in order to assure * that joint and separate compilation give the same result. @@ -941,9 +941,9 @@ class CheckCaptures extends Recheck, SymTransformer: * that this type parameter can't see. * For example, when capture checking the following expression: * - * def usingLogFile[T](op: (f: {*} File) => T): T = ... + * def usingLogFile[T](op: (f: {cap} File) => T): T = ... * - * usingLogFile[box ?1 () -> Unit] { (f: {*} File) => () => { f.write(0) } } + * usingLogFile[box ?1 () -> Unit] { (f: {cap} File) => () => { f.write(0) } } * * We may propagate `f` into ?1, making ?1 ill-formed. * This also causes soundness issues, since `f` in ?1 should be widened to `cap`, diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index 09f9695abfe1..bbe54f14b86c 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -122,7 +122,7 @@ extends tpd.TreeTraverser: val sym = tp.typeSymbol if sym.isClass then sym == defn.AnyClass - // we assume Any is a shorthand of {*} Any, so if Any is an upper + // we assume Any is a shorthand of {cap} Any, so if Any is an upper // bound, the type is taken to be impure. else superTypeIsImpure(tp.superType) case tp: (RefinedOrRecType | MatchType) => @@ -155,7 +155,7 @@ extends tpd.TreeTraverser: case CapturingType(parent, refs) => needsVariable(parent) && refs.isConst // if refs is a variable, no need to add another - && !refs.isUniversal // if refs is {*}, an added variable would not change anything + && !refs.isUniversal // if refs is {cap}, an added variable would not change anything case _ => false }.showing(i"can have inferred capture $tp = $result", capt) diff --git a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala index dacbd27e0f35..5fe68dd6a7ac 100644 --- a/compiler/src/dotty/tools/dotc/cc/Synthetics.scala +++ b/compiler/src/dotty/tools/dotc/cc/Synthetics.scala @@ -54,9 +54,9 @@ object Synthetics: /** Add capture dependencies to the type of the `apply` or `copy` method of a case class. * An apply method in a case class like this: - * case class CC(a: {d} A, b: B, {*} c: C) + * case class CC(a: {d} A, b: B, {cap} c: C) * would get type - * def apply(a': {d} A, b: B, {*} c': C): {a', c'} CC { val a = {a'} A, val c = {c'} C } + * def apply(a': {d} A, b: B, {cap} c': C): {a', c'} CC { val a = {a'} A, val c = {c'} C } * where `'` is used to indicate the difference between parameter symbol and refinement name. * Analogous for the copy method. */ @@ -123,7 +123,7 @@ object Synthetics: case _ => info - /** Augment an unapply of type `(x: C): D` to `(x: {*} C): {x} D` */ + /** Augment an unapply of type `(x: C): D` to `(x: {cap} C): {x} D` */ private def addUnapplyCaptures(info: Type)(using Context): Type = info match case info: MethodType => val paramInfo :: Nil = info.paramInfos: @unchecked diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index da2ec78b5179..eed22ba3808c 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -106,7 +106,7 @@ class Definitions { * } * ImpureXYZFunctionN follow this template: * - * type ImpureXYZFunctionN[-T0,...,-T{N-1}, +R] = {*} XYZFunctionN[T0,...,T{N-1}, R] + * type ImpureXYZFunctionN[-T0,...,-T{N-1}, +R] = {cap} XYZFunctionN[T0,...,T{N-1}, R] */ private def newFunctionNType(name: TypeName): Symbol = { val impure = name.startsWith("Impure") diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 47e7a85952b2..8100bea374eb 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -315,7 +315,7 @@ object Flags { val (SuperParamAliasOrScala2x @ _, SuperParamAlias @ _, Scala2x @ _) = newFlags(26, "", "") /** A parameter with a default value / an impure untpd.FunctionWithMods type */ - val (_, HasDefault @ _, Impure @ _) = newFlags(27, "", "<{*}>") + val (_, HasDefault @ _, Impure @ _) = newFlags(27, "", "") /** An extension method, or a collective extension instance */ val (Extension @ _, ExtensionMethod @ _, _) = newFlags(28, "") diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index fa58c467bfa5..f3540502597c 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -163,7 +163,7 @@ class PlainPrinter(_ctx: Context) extends Printer { boxText ~ toTextLocal(parent) ~ "^" ~ (refsText provided refsText != rootSetText) - final protected def rootSetText = Str("{*}") + final protected def rootSetText = Str("{cap}") def toText(tp: Type): Text = controlled { homogenize(tp) match { From d836de633a13a44a6ed47b003a4b1abd9d201a19 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 6 Mar 2023 19:36:55 +0100 Subject: [PATCH 557/657] Allow lines starting with a dot to fall outside previous indentation widths If line following some indented code starts with a '`.`' and its indentation width is different from the indentation widths of the two neighboring regions by more than a single space, the line accepted even if it does not match a previous indentation width. --- .../dotty/tools/dotc/parsing/Parsers.scala | 2 -- .../dotty/tools/dotc/parsing/Scanners.scala | 27 +++++++++++++++---- .../other-new-features/indentation.md | 15 ++++++++++- tests/neg/outdent-dot.check | 18 +++++++++++++ tests/neg/outdent-dot.scala | 13 +++++++++ tests/pos/outdent-dot.scala | 13 +++++++++ 6 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 tests/neg/outdent-dot.check create mode 100644 tests/neg/outdent-dot.scala create mode 100644 tests/pos/outdent-dot.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 2f51af8549ae..e157d5b6b7ac 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -3599,8 +3599,6 @@ object Parsers { } } - - /** DefDef ::= DefSig [‘:’ Type] ‘=’ Expr * | this TypelessClauses [DefImplicitClause] `=' ConstrExpr * DefDcl ::= DefSig `:' Type diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 933661b7c5c9..fac73bfb4992 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -611,11 +611,17 @@ object Scanners { case r: Indented => insert(OUTDENT, offset) handleNewIndentWidth(r.enclosing, ir => - val lw = lastWidth - errorButContinue( - em"""The start of this line does not match any of the previous indentation widths. - |Indentation width of current line : $nextWidth - |This falls between previous widths: ${ir.width} and $lw""")) + if next.token == DOT + && !nextWidth.isClose(r.indentWidth) + && !nextWidth.isClose(ir.indentWidth) + then + ir.otherIndentWidths += nextWidth + else + val lw = lastWidth + errorButContinue( + em"""The start of this line does not match any of the previous indentation widths. + |Indentation width of current line : $nextWidth + |This falls between previous widths: ${ir.width} and $lw""")) case r => if skipping then if r.enclosing.isClosedByUndentAt(nextWidth) then @@ -1666,6 +1672,17 @@ object Scanners { def < (that: IndentWidth): Boolean = this <= that && !(that <= this) + /** Does `this` differ from `that` by not more than a single space? */ + def isClose(that: IndentWidth): Boolean = this match + case Run(ch1, n1) => + that match + case Run(ch2, n2) => ch1 == ch2 && ch1 != '\t' && (n1 - n2).abs <= 1 + case Conc(l, r) => false + case Conc(l1, r1) => + that match + case Conc(l2, r2) => l1 == l2 && r1.isClose(r2) + case _ => false + def toPrefix: String = this match { case Run(ch, n) => ch.toString * n case Conc(l, r) => l.toPrefix ++ r.toPrefix diff --git a/docs/_docs/reference/other-new-features/indentation.md b/docs/_docs/reference/other-new-features/indentation.md index f53bf0995727..de73bb67d451 100644 --- a/docs/_docs/reference/other-new-features/indentation.md +++ b/docs/_docs/reference/other-new-features/indentation.md @@ -100,7 +100,7 @@ There are two rules: - An `` is finally inserted in front of a comma that follows a statement sequence starting with an `` if the indented region is itself enclosed in parentheses. -It is an error if the indentation width of the token following an `` does not match the indentation of some previous line in the enclosing indentation region. For instance, the following would be rejected. +It is generally an error if the indentation width of the token following an `` does not match the indentation of some previous line in the enclosing indentation region. For instance, the following would be rejected. ```scala if x < 0 then @@ -109,6 +109,19 @@ if x < 0 then x ``` +However, there is one exception to this rule: If the next line starts with a '`.`' _and_ the indentation +width is different from the indentation widths of the two neighboring regions by more than a single space, the line accepted. For instance, the following is OK: + +```scala +xs.map: x => + x + 1 + .filter: x => + x > 0 +``` +Here, the line starting with `.filter` does not have an indentation level matching a previous line, +but it is still accepted since it starts with a '`.`' and differs in at least two spaces from the +indentation levels of both the region that is closed and the next outer region. + Indentation tokens are only inserted in regions where newline statement separators are also inferred: at the top-level, inside braces `{...}`, but not inside parentheses `(...)`, patterns or types. diff --git a/tests/neg/outdent-dot.check b/tests/neg/outdent-dot.check new file mode 100644 index 000000000000..c93c3bcfba73 --- /dev/null +++ b/tests/neg/outdent-dot.check @@ -0,0 +1,18 @@ +-- Error: tests/neg/outdent-dot.scala:6:5 ------------------------------------------------------------------------------ +6 | .toString // error + | ^ + | The start of this line does not match any of the previous indentation widths. + | Indentation width of current line : 5 spaces + | This falls between previous widths: 2 spaces and 6 spaces +-- Error: tests/neg/outdent-dot.scala:11:3 ----------------------------------------------------------------------------- +11 | .filter: x => // error + | ^ + | The start of this line does not match any of the previous indentation widths. + | Indentation width of current line : 3 spaces + | This falls between previous widths: 2 spaces and 6 spaces +-- Error: tests/neg/outdent-dot.scala:13:4 ----------------------------------------------------------------------------- +13 | println("foo") // error + | ^ + | The start of this line does not match any of the previous indentation widths. + | Indentation width of current line : 4 spaces + | This falls between previous widths: 2 spaces and 6 spaces diff --git a/tests/neg/outdent-dot.scala b/tests/neg/outdent-dot.scala new file mode 100644 index 000000000000..d0e882a3c073 --- /dev/null +++ b/tests/neg/outdent-dot.scala @@ -0,0 +1,13 @@ +def Block(f: => Int): Int = f + +def bar(): String = + Block: + 2 + 2 + .toString // error + +def foo(xs: List[Int]) = + xs.map: x => + x + 1 + .filter: x => // error + x > 0 + println("foo") // error diff --git a/tests/pos/outdent-dot.scala b/tests/pos/outdent-dot.scala new file mode 100644 index 000000000000..c09bb673743a --- /dev/null +++ b/tests/pos/outdent-dot.scala @@ -0,0 +1,13 @@ +def Block(f: => Int): Int = f + +def bar(): String = + Block: + 2 + 2 + .toString + +def foo(xs: List[Int]) = + xs.map: x => + x + 1 + .filter: x => + x > 0 + println("foo") From 3b5b3c04fdf6bc1406c312ad29958ecd3717b102 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 11 May 2023 09:50:08 +0200 Subject: [PATCH 558/657] revert extra-line --- scaladoc/resources/dotty_res/styles/theme/layout/container.css | 1 - 1 file changed, 1 deletion(-) diff --git a/scaladoc/resources/dotty_res/styles/theme/layout/container.css b/scaladoc/resources/dotty_res/styles/theme/layout/container.css index 1d42c5529f00..53ede0e3dfff 100644 --- a/scaladoc/resources/dotty_res/styles/theme/layout/container.css +++ b/scaladoc/resources/dotty_res/styles/theme/layout/container.css @@ -24,7 +24,6 @@ p { height: auto; } - /* Scrollbar */ ::-webkit-scrollbar { From 8fa7187be508f4ea328bf3be2488a110472d9f68 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Thu, 11 May 2023 09:15:53 +0200 Subject: [PATCH 559/657] deps: update various sbt plugins This updates the following: - sbt-sonatype from 3.9.10 -> 3.9.20 - sbt-pgp (now under com.github.sbt) 2.0.0 -> 2.2.1 - sbt-buildinfo from 0.9.0 -> 0.11.0 - sbt-mima-plugin from 1.1.0 -> 1.1.2 --- project/plugins.sbt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index aba843ca2c3c..efc390ac93c5 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -8,14 +8,14 @@ libraryDependencySchemes += addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.12.0") -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.10") +addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.20") -addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.0") +addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.2.1") addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.13") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") -addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0") +addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.0") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.2") From 07c395bb7d0b4239ab163e2b5c8f44febfcdb2c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pa=C5=82ka?= Date: Thu, 11 May 2023 13:26:45 +0200 Subject: [PATCH 560/657] * Preserve the more restrictive syntax for typed patterns in the language specification * Make the parser's warning a migration warning --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 15 +++++++++++++-- .../test/dotty/tools/dotc/CompilationTests.scala | 1 + docs/_docs/internals/syntax.md | 5 ++++- docs/_docs/reference/syntax.md | 5 ++++- tests/neg-custom-args/i10994.check | 7 +++++++ tests/neg-custom-args/i10994.scala | 2 ++ tests/neg/t5702-neg-bad-and-wild.check | 5 ++++- tests/pos/i10994.scala | 2 +- 8 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 tests/neg-custom-args/i10994.check create mode 100644 tests/neg-custom-args/i10994.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 170332f7abe8..dfb9c5c6cb58 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2871,14 +2871,25 @@ object Parsers { if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) } else Nil - /** Pattern1 ::= Pattern2 [Ascription] + /** Pattern1 ::= PatVar Ascription + * | [‘-’] integerLiteral Ascription + * | [‘-’] floatingPointLiteral Ascription + * | Pattern2 */ def pattern1(location: Location = Location.InPattern): Tree = val p = pattern2() if in.isColon then val isVariableOrNumber = isVarPattern(p) || p.isInstanceOf[Number] if !isVariableOrNumber then - warning(em"Only variable and number literal patterns can have type ascriptions") + report.gradualErrorOrMigrationWarning( + em"""Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + |are no longer supported. Remove the type ascription or move it to a separate variable pattern.""", + in.sourcePos(), + warnFrom = `3.3`, + errorFrom = future + ) in.nextToken() ascription(p, location) else p diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index c782c78f8eb7..93bc2a5dd883 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -185,6 +185,7 @@ class CompilationTests { compileFile("tests/neg-custom-args/i13026.scala", defaultOptions.and("-print-lines")), compileFile("tests/neg-custom-args/i13838.scala", defaultOptions.and("-Ximplicit-search-limit", "1000")), compileFile("tests/neg-custom-args/jdk-9-app.scala", defaultOptions.and("-release:8")), + compileFile("tests/neg-custom-args/i10994.scala", defaultOptions.and("-source", "future")), ).checkExpectedErrors() } diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index e667a429afed..2817a7477b10 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -314,7 +314,10 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) -Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) +Pattern1 ::= PatVar ‘:’ RefinedType Bind(name, Typed(Ident(wildcard), tpe)) + | [‘-’] integerLiteral ‘:’ RefinedType Typed(pat, tpe) + | [‘-’] floatingPointLiteral ‘:’ RefinedType Typed(pat, tpe) + | Pattern2 Pattern2 ::= [id ‘@’] InfixPattern [‘*’] Bind(name, pat) InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat) SimplePattern ::= PatVar Ident(wildcard) diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 28259d94afb4..a705c5a3fd79 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -312,7 +312,10 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } -Pattern1 ::= Pattern2 [‘:’ RefinedType] +Pattern1 ::= PatVar ‘:’ RefinedType + | [‘-’] integerLiteral ‘:’ RefinedType + | [‘-’] floatingPointLiteral ‘:’ RefinedType + | Pattern2 Pattern2 ::= [id ‘@’] InfixPattern [‘*’] InfixPattern ::= SimplePattern { id [nl] SimplePattern } SimplePattern ::= PatVar diff --git a/tests/neg-custom-args/i10994.check b/tests/neg-custom-args/i10994.check new file mode 100644 index 000000000000..c540a04657c3 --- /dev/null +++ b/tests/neg-custom-args/i10994.check @@ -0,0 +1,7 @@ +-- Error: tests/neg-custom-args/i10994.scala:2:19 ---------------------------------------------------------------------- +2 | case (b: Boolean): Boolean => () // error + | ^ + | Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + | are no longer supported. Remove the type ascription or move it to a separate variable pattern. diff --git a/tests/neg-custom-args/i10994.scala b/tests/neg-custom-args/i10994.scala new file mode 100644 index 000000000000..65695ccf4352 --- /dev/null +++ b/tests/neg-custom-args/i10994.scala @@ -0,0 +1,2 @@ +def foo = true match + case (b: Boolean): Boolean => () // error diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index 36ac71b2e1e7..c461b76ea70b 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -59,7 +59,10 @@ -- Warning: tests/neg/t5702-neg-bad-and-wild.scala:13:22 --------------------------------------------------------------- 13 | case List(1, _*3:) => // error // error | ^ - | Only variable and number literal patterns can have type ascriptions + | Type ascriptions after patterns other than: + | * variable pattern, e.g. `case x: String =>` + | * number literal pattern, e.g. `case 10.5: Double =>` + | are no longer supported. Remove the type ascription or move it to a separate variable pattern. -- Warning: tests/neg/t5702-neg-bad-and-wild.scala:22:20 --------------------------------------------------------------- 22 | val K(x @ _*) = k | ^ diff --git a/tests/pos/i10994.scala b/tests/pos/i10994.scala index 99ae647466b1..b7b6a3661649 100644 --- a/tests/pos/i10994.scala +++ b/tests/pos/i10994.scala @@ -1,2 +1,2 @@ def foo = true match - case (b: Boolean): Boolean => () + case (b: Boolean): Boolean => () // warning From 0ee80f94a36bc523493fd9a8665d860e036537fd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 5 May 2023 13:06:47 +0200 Subject: [PATCH 561/657] Place staged type captures in Quote AST --- .../dotty/tools/dotc/CompilationUnit.scala | 2 +- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 +- compiler/src/dotty/tools/dotc/ast/Trees.scala | 21 ++- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../src/dotty/tools/dotc/core/Phases.scala | 4 + .../tools/dotc/core/tasty/TreePickler.scala | 2 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- .../dotty/tools/dotc/inlines/Inliner.scala | 2 +- .../dotty/tools/dotc/parsing/Parsers.scala | 4 +- .../tools/dotc/printing/RefinedPrinter.scala | 5 +- .../tools/dotc/staging/CrossStageSafety.scala | 80 +++++----- .../tools/dotc/staging/DirectTypeOf.scala | 25 ---- .../dotty/tools/dotc/staging/HealType.scala | 12 +- .../tools/dotc/staging/QuoteTypeTags.scala | 50 ++----- .../tools/dotc/transform/MegaPhase.scala | 2 +- .../tools/dotc/transform/PickleQuotes.scala | 137 ++++++++++++------ .../tools/dotc/transform/ReifiedReflect.scala | 2 +- .../dotty/tools/dotc/transform/Splicer.scala | 10 +- .../dotty/tools/dotc/transform/Splicing.scala | 89 ++++-------- .../dotty/tools/dotc/transform/Staging.scala | 2 +- .../tools/dotc/transform/TreeChecker.scala | 30 ++++ .../tools/dotc/transform/patmat/Space.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 6 +- .../src/dotty/tools/dotc/typer/ReTyper.scala | 3 +- 25 files changed, 249 insertions(+), 251 deletions(-) delete mode 100644 compiler/src/dotty/tools/dotc/staging/DirectTypeOf.scala diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 8e4989f13c3d..8415646eb16c 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -157,7 +157,7 @@ object CompilationUnit { if tree.symbol.is(Flags.Inline) then containsInline = true tree match - case tpd.Quote(_) => + case _: tpd.Quote => containsQuote = true case tree: tpd.Apply if tree.symbol == defn.QuotedTypeModule_of => containsQuote = true diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index e658a3e9a142..8a080972fb31 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -1976,7 +1976,7 @@ object desugar { trees foreach collect case Block(Nil, expr) => collect(expr) - case Quote(body) => + case Quote(body, _) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { case Splice(expr) => collect(expr) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 61009f48a8f0..f4c3240c03bd 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -690,9 +690,14 @@ object Trees { * when type checking. TASTy files will not contain type quotes. Type quotes are used again * in the `staging` phase to represent the reification of `Type.of[T]]`. * + * Type tags `tags` are always empty before the `staging` phase. Tags for stage inconsistent + * types are added in the `staging` phase to level 0 quotes. Tags for types that refer to + * definitions in an outer quote are added in the `splicing` phase + * * @param body The tree that was quoted + * @param tags Term references to instances of `Type[T]` for `T`s that are used in the quote */ - case class Quote[+T <: Untyped] private[ast] (body: Tree[T])(implicit @constructorOnly src: SourceFile) + case class Quote[+T <: Untyped] private[ast] (body: Tree[T], tags: List[Tree[T]])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { type ThisTree[+T <: Untyped] = Quote[T] @@ -1313,9 +1318,9 @@ object Trees { case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree))) } - def Quote(tree: Tree)(body: Tree)(using Context): Quote = tree match { - case tree: Quote if (body eq tree.body) => tree - case _ => finalize(tree, untpd.Quote(body)(sourceFile(tree))) + def Quote(tree: Tree)(body: Tree, tags: List[Tree])(using Context): Quote = tree match { + case tree: Quote if (body eq tree.body) && (tags eq tree.tags) => tree + case _ => finalize(tree, untpd.Quote(body, tags)(sourceFile(tree))) } def Splice(tree: Tree)(expr: Tree)(using Context): Splice = tree match { case tree: Splice if (expr eq tree.expr) => tree @@ -1558,8 +1563,8 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case tree @ Quote(body) => - cpy.Quote(tree)(transform(body)(using quoteContext)) + case Quote(body, tags) => + cpy.Quote(tree)(transform(body)(using quoteContext), transform(tags)) case tree @ Splice(expr) => cpy.Splice(tree)(transform(expr)(using spliceContext)) case tree @ Hole(isTerm, idx, args, content, tpt) => @@ -1703,8 +1708,8 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) - case Quote(body) => - this(x, body)(using quoteContext) + case Quote(body, tags) => + this(this(x, body)(using quoteContext), tags) case Splice(expr) => this(x, expr)(using spliceContext) case Hole(_, _, args, content, tpt) => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index d26735bdeb05..503c1a0b1b39 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -170,8 +170,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined = ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion) - def Quote(body: Tree)(using Context): Quote = - untpd.Quote(body).withBodyType(body.tpe) + def Quote(body: Tree, tags: List[Tree])(using Context): Quote = + untpd.Quote(body, tags).withBodyType(body.tpe) def Splice(expr: Tree, tpe: Type)(using Context): Splice = untpd.Splice(expr).withType(tpe) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index dbac7ceaaf05..479d85f6e383 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -397,7 +397,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): SeqLiteral = new SeqLiteral(elems, elemtpt) def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt) def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) - def Quote(body: Tree)(implicit src: SourceFile): Quote = new Quote(body) + def Quote(body: Tree, tags: List[Tree])(implicit src: SourceFile): Quote = new Quote(body, tags) def Splice(expr: Tree)(implicit src: SourceFile): Splice = new Splice(expr) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 00e017430a5f..3c4c45ab254a 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -211,6 +211,7 @@ object Phases { private var mySbtExtractDependenciesPhase: Phase = _ private var myPicklerPhase: Phase = _ private var myInliningPhase: Phase = _ + private var myStagingPhase: Phase = _ private var mySplicingPhase: Phase = _ private var myFirstTransformPhase: Phase = _ private var myCollectNullableFieldsPhase: Phase = _ @@ -235,6 +236,7 @@ object Phases { final def sbtExtractDependenciesPhase: Phase = mySbtExtractDependenciesPhase final def picklerPhase: Phase = myPicklerPhase final def inliningPhase: Phase = myInliningPhase + final def stagingPhase: Phase = myStagingPhase final def splicingPhase: Phase = mySplicingPhase final def firstTransformPhase: Phase = myFirstTransformPhase final def collectNullableFieldsPhase: Phase = myCollectNullableFieldsPhase @@ -262,6 +264,7 @@ object Phases { mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies]) myPicklerPhase = phaseOfClass(classOf[Pickler]) myInliningPhase = phaseOfClass(classOf[Inlining]) + myStagingPhase = phaseOfClass(classOf[Staging]) mySplicingPhase = phaseOfClass(classOf[Splicing]) myFirstTransformPhase = phaseOfClass(classOf[FirstTransform]) myCollectNullableFieldsPhase = phaseOfClass(classOf[CollectNullableFields]) @@ -449,6 +452,7 @@ object Phases { def sbtExtractDependenciesPhase(using Context): Phase = ctx.base.sbtExtractDependenciesPhase def picklerPhase(using Context): Phase = ctx.base.picklerPhase def inliningPhase(using Context): Phase = ctx.base.inliningPhase + def stagingPhase(using Context): Phase = ctx.base.stagingPhase def splicingPhase(using Context): Phase = ctx.base.splicingPhase def firstTransformPhase(using Context): Phase = ctx.base.firstTransformPhase def refchecksPhase(using Context): Phase = ctx.base.refchecksPhase diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 5662b17b6697..898ef2ed0c6c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,7 +665,7 @@ class TreePickler(pickler: TastyPickler) { pickleTree(hi) pickleTree(alias) } - case tree @ Quote(body) => + case tree @ Quote(body, Nil) => // TODO: Add QUOTE tag to TASTy assert(body.isTerm, """Quote with type should not be pickled. diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 10395a488640..659d96a386ea 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1269,7 +1269,7 @@ class TreeUnpickler(reader: TastyReader, def quotedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked - untpd.Quote(args.head).withBodyType(targs.head.tpe) + untpd.Quote(args.head, Nil).withBodyType(targs.head.tpe) def splicedExpr(fn: Tree, args: List[Tree]): Tree = val TypeApply(_, targs) = fn: @unchecked diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index ccef777380e1..54decf137b42 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -827,7 +827,7 @@ class Inliner(val call: tpd.Tree)(using Context): override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = super.typedQuote(tree, pt) match - case Quote(Splice(inner)) => inner + case Quote(Splice(inner), _) => inner case tree1 => ctx.compilationUnit.needsStaging = true tree1 diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 8d13bdeed68f..2a496b03e652 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1243,7 +1243,7 @@ object Parsers { } } in.nextToken() - Quote(t) + Quote(t, Nil) } else if !in.featureEnabled(Feature.symbolLiterals) then @@ -2480,7 +2480,7 @@ object Parsers { val body = if (in.token == LBRACKET) inBrackets(typ()) else stagedBlock() - Quote(body) + Quote(body, Nil) } } case NEW => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 75085469b7d2..47dac083cfce 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -726,11 +726,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) - case tree @ Quote(body) => + case tree @ Quote(body, tags) => + val tagsText = (keywordStr("<") ~ toTextGlobal(tags, ", ") ~ keywordStr(">")).provided(tree.tags.nonEmpty) val exprTypeText = (keywordStr("[") ~ toTextGlobal(tree.bodyType) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) val open = if (body.isTerm) keywordStr("{") else keywordStr("[") val close = if (body.isTerm) keywordStr("}") else keywordStr("]") - keywordStr("'") ~ exprTypeText ~ open ~ toTextGlobal(body) ~ close + keywordStr("'") ~ tagsText ~ exprTypeText ~ open ~ toTextGlobal(body) ~ close case Splice(expr) => val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") diff --git a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala index feb93c3c6319..8360d8e08211 100644 --- a/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala +++ b/compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala @@ -16,28 +16,36 @@ import dotty.tools.dotc.util.Property import dotty.tools.dotc.util.Spans._ import dotty.tools.dotc.util.SrcPos -/** Checks that staging level consistency holds and heals staged types . +/** Checks that staging level consistency holds and heals staged types. * * Local term references are level consistent if and only if they are used at the same level as their definition. * * Local type references can be used at the level of their definition or lower. If used used at a higher level, * it will be healed if possible, otherwise it is inconsistent. * - * Type healing consists in transforming a level inconsistent type `T` into `summon[Type[T]].Underlying`. + * Healing a type consists in replacing locally defined types defined at staging level 0 and used in higher levels. + * For each type local `T` that is defined at level 0 and used in a quote, we summon a tag `t: Type[T]`. This `t` + * tag must be defined at level 0. The tags will be listed in the `tags` of the level 0 quote (`'{ ... }`) and + * each reference to `T` will be replaced by `t.Underlying` in the body of the quote. + * + * We delay the healing of types in quotes at level 1 or higher until those quotes reach level 0. At this point + * more types will be statically known and fewer types will need to be healed. This also keeps the nested quotes + * in their original form, we do not want macro users to see any artifacts of this phase in quoted expressions + * they might inspect. + * + * Type heal example: * - * As references to types do not necessarily have an associated tree it is not always possible to replace the types directly. - * Instead we always generate a type alias for it and place it at the start of the surrounding quote. This also avoids duplication. - * For example: * '{ * val x: List[T] = List[T]() + * '{ .. T .. } * () * } * * is transformed to * - * '{ - * type t$1 = summon[Type[T]].Underlying - * val x: List[t$1] = List[t$1](); + * '{ // where `t` is a given term of type `Type[T]` + * val x: List[t.Underlying] = List[t.Underlying](); + * '{ .. t.Underlying .. } * () * } * @@ -56,11 +64,18 @@ class CrossStageSafety extends TreeMapWithStages { case tree: Quote => if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", tree.srcPos) - val body1 = transformQuoteBody(tree.body, tree.span) - val stripAnnotationsDeep: TypeMap = new TypeMap: - def apply(tp: Type): Type = mapOver(tp.stripAnnots) - val bodyType1 = healType(tree.srcPos)(stripAnnotationsDeep(tree.bodyType)) - cpy.Quote(tree)(body1).withBodyType(bodyType1) + + val tree1 = + val stripAnnotationsDeep: TypeMap = new TypeMap: + def apply(tp: Type): Type = mapOver(tp.stripAnnots) + val bodyType1 = healType(tree.srcPos)(stripAnnotationsDeep(tree.bodyType)) + tree.withBodyType(bodyType1) + + if level == 0 then + val (tags, body1) = inContextWithQuoteTypeTags { transform(tree1.body)(using quoteContext) } + cpy.Quote(tree1)(body1, tags) + else + super.transform(tree1) case CancelledSplice(tree) => transform(tree) // Optimization: `${ 'x }` --> `x` @@ -74,22 +89,18 @@ class CrossStageSafety extends TreeMapWithStages { case tree @ QuotedTypeOf(body) => if (ctx.property(InAnnotation).isDefined) report.error("Cannot have a quote in an annotation", tree.srcPos) - body.tpe match - case DirectTypeOf(termRef) => - // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` - ref(termRef).withSpan(tree.span) - case _ => - transformQuoteBody(body, tree.span) match - case DirectTypeOf.Healed(termRef) => - // Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x` - ref(termRef).withSpan(tree.span) - case transformedBody => - val quotes = transform(tree.args.head) - // `quoted.Type.of[](quotes)` --> `quoted.Type.of[](quotes)` - val TypeApply(fun, _) = tree.fun: @unchecked - if level != 0 then cpy.Apply(tree)(cpy.TypeApply(tree.fun)(fun, transformedBody :: Nil), quotes :: Nil) - else tpd.Quote(transformedBody).select(nme.apply).appliedTo(quotes).withSpan(tree.span) + if level == 0 then + val (tags, body1) = inContextWithQuoteTypeTags { transform(body)(using quoteContext) } + val quotes = transform(tree.args.head) + tags match + case tag :: Nil if body1.isType && body1.tpe =:= tag.tpe.select(tpnme.Underlying) => + tag // Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x` + case _ => + // `quoted.Type.of[]()` --> `'[].apply()` + tpd.Quote(body1, tags).select(nme.apply).appliedTo(quotes).withSpan(tree.span) + else + super.transform(tree) case _: DefDef if tree.symbol.isInlineMethod => tree @@ -137,17 +148,6 @@ class CrossStageSafety extends TreeMapWithStages { super.transform(tree) end transform - private def transformQuoteBody(body: Tree, span: Span)(using Context): Tree = { - val taggedTypes = new QuoteTypeTags(span) - val contextWithQuote = - if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext) - else quoteContext - val transformedBody = transform(body)(using contextWithQuote) - taggedTypes.getTypeTags match - case Nil => transformedBody - case tags => tpd.Block(tags, transformedBody).withSpan(body.span) - } - def transformTypeAnnotationSplices(tp: Type)(using Context) = new TypeMap { def apply(tp: Type): Type = tp match case tp: AnnotatedType => @@ -234,7 +234,7 @@ class CrossStageSafety extends TreeMapWithStages { def unapply(tree: Splice): Option[Tree] = def rec(tree: Tree): Option[Tree] = tree match case Block(Nil, expr) => rec(expr) - case Quote(inner) => Some(inner) + case Quote(inner, _) => Some(inner) case _ => None rec(tree.expr) } diff --git a/compiler/src/dotty/tools/dotc/staging/DirectTypeOf.scala b/compiler/src/dotty/tools/dotc/staging/DirectTypeOf.scala deleted file mode 100644 index 488d8ff2a88e..000000000000 --- a/compiler/src/dotty/tools/dotc/staging/DirectTypeOf.scala +++ /dev/null @@ -1,25 +0,0 @@ -package dotty.tools.dotc.staging - -import dotty.tools.dotc.ast.{tpd, untpd} -import dotty.tools.dotc.core.Contexts._ -import dotty.tools.dotc.core.Symbols._ -import dotty.tools.dotc.core.Types._ - -object DirectTypeOf: - import tpd.* - - /** Matches `x.Underlying` and extracts the TermRef to `x` */ - def unapply(tpe: Type)(using Context): Option[TermRef] = tpe match - case tp @ TypeRef(x: TermRef, _) if tp.symbol == defn.QuotedType_splice => Some(x) - case _ => None - - object Healed: - /** Matches `{ @SplicedType type T = x.Underlying; T }` and extracts the TermRef to `x` */ - def unapply(body: Tree)(using Context): Option[TermRef] = - body match - case Block(List(tdef: TypeDef), tpt: TypeTree) => - tpt.tpe match - case tpe: TypeRef if tpe.typeSymbol == tdef.symbol => - DirectTypeOf.unapply(tdef.rhs.tpe.hiBound) - case _ => None - case _ => None diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 2facb64746b3..023271960b40 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -25,7 +25,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { * * If `T` is a reference to a type at the wrong level, try to heal it by replacing it with * a type tag of type `quoted.Type[T]`. - * The tag is generated by an instance of `QuoteTypeTags` directly if the splice is explicit + * The tag is recorded by an instance of `QuoteTypeTags` directly if the splice is explicit * or indirectly by `tryHeal`. */ def apply(tp: Type): Type = @@ -43,11 +43,9 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { private def healTypeRef(tp: TypeRef): Type = tp.prefix match - case NoPrefix if tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => - tp case prefix: TermRef if tp.symbol.isTypeSplice => checkNotWildcardSplice(tp) - if level == 0 then tp else getQuoteTypeTags.getTagRef(prefix) + if level == 0 then tp else getTagRef(prefix) case _: NamedType | _: ThisType | NoPrefix => if levelInconsistentRootOfPath(tp).exists then tryHeal(tp) @@ -58,7 +56,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { private object NonSpliceAlias: def unapply(tp: TypeRef)(using Context): Option[Type] = tp.underlying match - case TypeAlias(alias) if !tp.symbol.isTypeSplice && !tp.typeSymbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => Some(alias) + case TypeAlias(alias) if !tp.symbol.isTypeSplice => Some(alias) case _ => None private def checkNotWildcardSplice(splice: TypeRef): Unit = @@ -78,7 +76,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { /** Try to heal reference to type `T` used in a higher level than its definition. * Returns a reference to a type tag generated by `QuoteTypeTags` that contains a - * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}`. + * reference to a type alias containing the equivalent of `${summon[quoted.Type[T]]}.Underlying`. * Emits an error if `T` cannot be healed and returns `T`. */ protected def tryHeal(tp: TypeRef): Type = { @@ -88,7 +86,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { case tp: TermRef => ctx.typer.checkStable(tp, pos, "type witness") if levelOf(tp.symbol) > 0 then tp.select(tpnme.Underlying) - else getQuoteTypeTags.getTagRef(tp) + else getTagRef(tp) case _: SearchFailureType => report.error( ctx.typer.missingArgMsg(tag, reqType, "") diff --git a/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala b/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala index d6f0a6576085..0b5032ea5a6d 100644 --- a/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala +++ b/compiler/src/dotty/tools/dotc/staging/QuoteTypeTags.scala @@ -1,52 +1,24 @@ package dotty.tools.dotc.staging -import dotty.tools.dotc.ast.{tpd, untpd} -import dotty.tools.dotc.core.Annotations._ +import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts._ import dotty.tools.dotc.core.Decorators._ -import dotty.tools.dotc.core.Flags._ -import dotty.tools.dotc.core.NameKinds._ import dotty.tools.dotc.core.StdNames._ -import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.util.Property -import dotty.tools.dotc.util.Spans._ -object QuoteTypeTags { +import scala.collection.mutable.LinkedHashSet - private val TaggedTypes = new Property.Key[QuoteTypeTags] +object QuoteTypeTags: - def contextWithQuoteTypeTags(taggedTypes: QuoteTypeTags)(using Context) = - ctx.fresh.setProperty(TaggedTypes, taggedTypes) + private val TaggedTypes = new Property.Key[LinkedHashSet[TermRef]] - def getQuoteTypeTags(using Context): QuoteTypeTags = - ctx.property(TaggedTypes).get -} + def inContextWithQuoteTypeTags(body: Context ?=> tpd.Tree)(using Context): (List[tpd.Tree], tpd.Tree) = + val tags = LinkedHashSet.empty[TermRef] + val transformed = body(using ctx.fresh.setProperty(TaggedTypes, tags)) + (tags.toList.map(tpd.ref(_)), transformed) -class QuoteTypeTags(span: Span)(using Context) { - import tpd.* - - private val tags = collection.mutable.LinkedHashMap.empty[TermRef, TypeDef] - - def getTagRef(spliced: TermRef): TypeRef = { - val typeDef = tags.getOrElseUpdate(spliced, mkTagSymbolAndAssignType(spliced)) - typeDef.symbol.typeRef - } - - def getTypeTags: List[TypeDef] = tags.valuesIterator.toList - - private def mkTagSymbolAndAssignType(spliced: TermRef): TypeDef = { - val splicedTree = tpd.ref(spliced).withSpan(span) - val rhs = splicedTree.select(tpnme.Underlying).withSpan(span) - val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs, EmptyTree) - val local = newSymbol( - owner = ctx.owner, - name = UniqueName.fresh(rhs.tpe.dealias.typeSymbol.name.toTypeName), - flags = Synthetic, - info = TypeAlias(splicedTree.tpe.select(tpnme.Underlying)), - coord = span).asType - local.addAnnotation(Annotation(defn.QuotedRuntime_SplicedTypeAnnot, span)) - ctx.typeAssigner.assignType(untpd.TypeDef(local.name, alias), local) - } -} + def getTagRef(spliced: TermRef)(using Context): Type = + ctx.property(TaggedTypes).get += spliced + spliced.select(tpnme.Underlying) diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index 7ad109b67751..b4e8c3acbc5c 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -402,7 +402,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { case tree: Quote => inContext(prepQuote(tree, start)(using outerCtx)) { val body = transformTree(tree.body, start)(using quoteContext) - goQuote(cpy.Quote(tree)(body), start) + goQuote(cpy.Quote(tree)(body, Nil), start) } case tree: Splice => inContext(prepSplice(tree, start)(using outerCtx)) { diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 581b320ed9df..98bca65093fa 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -9,6 +9,7 @@ import Contexts._ import Symbols._ import Constants._ import ast.Trees._ +import ast.untpd import ast.TreeTypeMap import SymUtils._ import NameKinds._ @@ -27,9 +28,7 @@ import scala.annotation.constructorOnly * * Transforms top level quote * ``` - * '{ ... - * @TypeSplice type X0 = {{ 0 | .. | contentsTpe0 | .. }} - * @TypeSplice type X2 = {{ 1 | .. | contentsTpe1 | .. }} + * '{ ... * val x1: U1 = ??? * val x2: U2 = ??? * ... @@ -45,22 +44,19 @@ import scala.annotation.constructorOnly * ``` * unpickleExprV2( * pickled = [[ // PICKLED TASTY - * @TypeSplice type X0 // with bounds that do not contain captured types - * @TypeSplice type X1 // with bounds that do not contain captured types + * @TypeSplice type A // with bounds that do not contain captured types + * @TypeSplice type B // with bounds that do not contain captured types * val x1 = ??? * val x2 = ??? * ... - * {{{ 0 | x1 | | T0 }}} // hole - * ... - * {{{ 1 | x2 | | T1 }}} // hole - * ... - * {{{ 2 | x1, x2 | | T2 }}} // hole + * {{{ 0 | x1 | | T0 }}} // hole + * ... + * {{{ 1 | x2 | | T1 }}} // hole + * ... + * {{{ 2 | x1, x2 | | T2 }}} // hole * ... * ]], - * typeHole = (idx: Int, args: List[Any]) => idx match { - * case 0 => contentsTpe0.apply(args(0).asInstanceOf[Type[?]]) // beta reduced - * case 1 => contentsTpe1.apply(args(0).asInstanceOf[Type[?]]) // beta reduced - * }, + * typeHole = Seq(a, b), * termHole = (idx: Int, args: List[Any], quotes: Quotes) => idx match { * case 3 => content0.apply(args(0).asInstanceOf[Expr[U1]]).apply(quotes) // beta reduced * case 4 => content1.apply(args(0).asInstanceOf[Expr[U2]]).apply(quotes) // beta reduced @@ -87,9 +83,6 @@ class PickleQuotes extends MacroTransform { assert(Inlines.inInlineMethod) case tree: Splice => assert(Inlines.inInlineMethod) - case _ : TypeDef if !Inlines.inInlineMethod => - assert(!tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot), - s"${tree.symbol} should have been removed by PickledQuotes because it has a @quoteTypeTag") case _ => override def run(using Context): Unit = @@ -100,7 +93,9 @@ class PickleQuotes extends MacroTransform { tree match case Apply(Select(quote: Quote, nme.apply), List(quotes)) => val (contents, quote1) = makeHoles(quote) - val pickled = PickleQuotes.pickle(quote1, quotes, contents) + val quote2 = encodeTypeArgs(quote1) + val contents1 = contents ::: quote.tags + val pickled = PickleQuotes.pickle(quote2, quotes, contents1) transform(pickled) // pickle quotes that are in the contents case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod => tree @@ -114,12 +109,12 @@ class PickleQuotes extends MacroTransform { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match case tree @ Hole(isTerm, _, _, content, _) => - if !content.isEmpty then - contents += content - val holeType = - if isTerm then getTermHoleType(tree.tpe) else getTypeHoleType(tree.tpe) + assert(isTerm) + assert(!content.isEmpty) + contents += content + val holeType = getTermHoleType(tree.tpe) val hole = cpy.Hole(tree)(content = EmptyTree, TypeTree(holeType)) - if isTerm then Inlined(EmptyTree, Nil, hole).withSpan(tree.span) else hole + cpy.Inlined(tree)(EmptyTree, Nil, hole) case tree: DefTree => val newAnnotations = tree.symbol.annotations.mapconserve { annot => annot.derivedAnnotation(transform(annot.tree)(using ctx.withOwner(tree.symbol))) @@ -139,25 +134,6 @@ class PickleQuotes extends MacroTransform { } } - /** Remove references to local types that will not be defined in this quote */ - private def getTypeHoleType(using Context) = new TypeMap() { - override def apply(tp: Type): Type = tp match - case tp: TypeRef if tp.typeSymbol.isTypeSplice => - apply(tp.dealias) - case tp @ TypeRef(pre, _) if isLocalPath(pre) => - val hiBound = tp.typeSymbol.info match - case info: ClassInfo => info.parents.reduce(_ & _) - case info => info.hiBound - apply(hiBound) - case tp => - mapOver(tp) - - private def isLocalPath(tp: Type): Boolean = tp match - case NoPrefix => true - case tp: TermRef if !tp.symbol.is(Package) => isLocalPath(tp.prefix) - case tp => false - } - /** Remove references to local types that will not be defined in this quote */ private def getTermHoleType(using Context) = new TypeMap() { override def apply(tp: Type): Type = tp match @@ -180,14 +156,78 @@ class PickleQuotes extends MacroTransform { val holeMaker = new HoleContentExtractor val body1 = holeMaker.transform(quote.body) - val body2 = - if quote.isTypeQuote then body1 - else Inlined(Inlines.inlineCallTrace(ctx.owner, quote.sourcePos), Nil, body1) - val quote1 = cpy.Quote(quote)(body2) + val quote1 = cpy.Quote(quote)(body1, quote.tags) (holeMaker.getContents(), quote1) end makeHoles + /** Encode quote tags as holes in the quote body. + * + * ```scala + * '{ ... t.Underlying ... u.Underlying ... } + * ``` + * becomes + * ```scala + * '{ + * type T = {{ 0 | .. | .. | .. }} + * type U = {{ 1 | .. | .. | .. }} + * ... T ... U ... + * } + * ``` + */ + private def encodeTypeArgs(quote: tpd.Quote)(using Context): tpd.Quote = + if quote.tags.isEmpty then quote + else + val tdefs = quote.tags.zipWithIndex.map(mkTagSymbolAndAssignType) + val typeMapping = quote.tags.map(_.tpe).zip(tdefs.map(_.symbol.typeRef)).toMap + val typeMap = new TypeMap { + override def apply(tp: Type): Type = tp match + case TypeRef(tag: TermRef, _) if tp.typeSymbol == defn.QuotedType_splice => + typeMapping.getOrElse(tag, tp) + case _ => mapOver(tp) + } + def treeMap(tree: Tree): Tree = tree match + case Select(qual, _) if tree.symbol == defn.QuotedType_splice => + typeMapping.get(qual.tpe) match + case Some(tag) => TypeTree(tag).withSpan(tree.span) + case None => tree + case _ => tree + val body1 = new TreeTypeMap(typeMap, treeMap).transform(quote.body) + cpy.Quote(quote)(Block(tdefs, body1), quote.tags) + + private def mkTagSymbolAndAssignType(typeArg: Tree, idx: Int)(using Context): TypeDef = { + val holeType = getTypeHoleType(typeArg.tpe.select(tpnme.Underlying)) + val hole = cpy.Hole(typeArg)(isTerm = false, idx, Nil, EmptyTree, TypeTree(holeType)) + val local = newSymbol( + owner = ctx.owner, + name = UniqueName.fresh(hole.tpe.dealias.typeSymbol.name.toTypeName), + flags = Synthetic, + info = TypeAlias(typeArg.tpe.select(tpnme.Underlying)), + coord = typeArg.span + ).asType + local.addAnnotation(Annotation(defn.QuotedRuntime_SplicedTypeAnnot, typeArg.span)) + ctx.typeAssigner.assignType(untpd.TypeDef(local.name, hole), local).withSpan(typeArg.span) + } + + /** Remove references to local types that will not be defined in this quote */ + private def getTypeHoleType(using Context) = new TypeMap() { + override def apply(tp: Type): Type = tp match + case tp: TypeRef if tp.typeSymbol.isTypeSplice => + apply(tp.dealias) + case tp @ TypeRef(pre, _) if isLocalPath(pre) => + val hiBound = tp.typeSymbol.info match + case info: ClassInfo => info.parents.reduce(_ & _) + case info => info.hiBound + apply(hiBound) + case tp => + mapOver(tp) + + private def isLocalPath(tp: Type): Boolean = tp match + case NoPrefix => true + case tp: TermRef if !tp.symbol.is(Package) => isLocalPath(tp.prefix) + case tp => false + } + } object PickleQuotes { @@ -286,7 +326,10 @@ object PickleQuotes { * this closure is always applied directly to the actual context and the BetaReduce phase removes it. */ def pickleAsTasty() = { - val pickleQuote = PickledQuotes.pickleQuote(body) + val body1 = + if body.isType then body + else Inlined(Inlines.inlineCallTrace(ctx.owner, quote.sourcePos), Nil, body) + val pickleQuote = PickledQuotes.pickleQuote(body1) val pickledQuoteStrings = pickleQuote match case x :: Nil => Literal(Constant(x)) case xs => tpd.mkList(xs.map(x => Literal(Constant(x))), TypeTree(defn.StringType)) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala b/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala index bd0c063cd21a..6e73d683fa2c 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifiedReflect.scala @@ -75,7 +75,7 @@ trait ReifiedReflect: .select(defn.Quotes_reflect_TypeRepr_of) .appliedToType(tpe) .appliedTo( - tpd.Quote(TypeTree(tpe)) + tpd.Quote(TypeTree(tpe), Nil) .select(nme.apply) .appliedTo(quotesTree) ) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 8f45fd94d205..741c770e2c77 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -44,7 +44,7 @@ object Splicer { * See: `Staging` */ def splice(tree: Tree, splicePos: SrcPos, spliceExpansionPos: SrcPos, classLoader: ClassLoader)(using Context): Tree = tree match { - case Quote(quotedTree) => quotedTree + case Quote(quotedTree, Nil) => quotedTree case _ => val macroOwner = newSymbol(ctx.owner, nme.MACROkw, Macro | Synthetic, defn.AnyType, coord = tree.span) try @@ -136,7 +136,7 @@ object Splicer { * See: `Staging` */ def checkValidMacroBody(tree: Tree)(using Context): Unit = tree match { - case Quote(_) => // ok + case Quote(_, Nil) => // ok case _ => type Env = Set[Symbol] @@ -155,7 +155,7 @@ object Splicer { case Block(Nil, expr) => checkIfValidArgument(expr) case Typed(expr, _) => checkIfValidArgument(expr) - case Apply(Select(Quote(body), nme.apply), _) => + case Apply(Select(Quote(body, _), nme.apply), _) => val noSpliceChecker = new TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match case Splice(_) => @@ -203,7 +203,7 @@ object Splicer { case Typed(expr, _) => checkIfValidStaticCall(expr) - case Apply(Select(Quote(quoted), nme.apply), _) => + case Apply(Select(Quote(quoted, Nil), nme.apply), _) => // OK, canceled and warning emitted case Call(fn, args) @@ -240,7 +240,7 @@ object Splicer { override protected def interpretTree(tree: Tree)(implicit env: Env): Object = tree match { // Interpret level -1 quoted code `'{...}` (assumed without level 0 splices) - case Apply(Select(Quote(body), nme.apply), _) => + case Apply(Select(Quote(body, _), nme.apply), _) => val body1 = body match { case expr: Ident if expr.symbol.isAllOf(InlineByNameProxy) => // inline proxy for by-name parameter diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index b482e9311c74..bf7eb2dedb7d 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -23,6 +23,7 @@ import dotty.tools.dotc.quoted._ import dotty.tools.dotc.config.ScalaRelease.* import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.staging.QuoteTypeTags +import dotty.tools.dotc.staging.QuoteTypeTags.* import scala.annotation.constructorOnly @@ -88,7 +89,7 @@ class Splicing extends MacroTransform: tree match case tree: Quote => val body1 = QuoteTransformer().transform(tree.body)(using quoteContext) - cpy.Quote(tree)(body = body1) + cpy.Quote(tree)(body1, tree.tags) case tree: DefDef if tree.symbol.is(Inline) => // Quotes in inlined methods are only pickled after they are inlined. tree @@ -117,18 +118,6 @@ class Splicing extends MacroTransform: val newSplicedCode1 = splicer.transformSplice(tree.expr, tree.tpe, holeIdx)(using spliceContext) val newSplicedCode2 = Level0QuoteTransformer.transform(newSplicedCode1)(using spliceContext) newSplicedCode2 - case tree: TypeDef if tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => - val tp @ TypeRef(qual: TermRef, _) = tree.rhs.tpe.hiBound: @unchecked - quotedDefs += tree.symbol - val hole = typeHoles.get(qual) match - case Some (hole) => cpy.Hole(hole)(content = EmptyTree) - case None => - val holeIdx = numHoles - numHoles += 1 - val hole = tpd.Hole(false, holeIdx, Nil, ref(qual), TypeTree(tp)) - typeHoles.put(qual, hole) - hole - cpy.TypeDef(tree)(rhs = hole) case _: Template => for sym <- tree.symbol.owner.info.decls do quotedDefs += sym @@ -174,14 +163,13 @@ class Splicing extends MacroTransform: * ``` * is transformed into * ```scala - * {{{ | T2 | x, X | (x$1: Expr[T1], X$1: Type[X]) => (using Quotes) ?=> {... ${x$1} ... X$1.Underlying ...} }}} + * {{{ | T2 | x, X | (x$1: Expr[T1], X$1: Type[X]) => (using Quotes) ?=> '{... ${x$1} ... X$1.Underlying ...} }}} * ``` */ private class SpliceTransformer(spliceOwner: Symbol, isCaptured: Symbol => Boolean) extends Transformer: private var refBindingMap = mutable.LinkedHashMap.empty[Symbol, (Tree, Symbol)] /** Reference to the `Quotes` instance of the current level 1 splice */ private var quotes: Tree | Null = null // TODO: add to the context - private var healedTypes: QuoteTypeTags | Null = null // TODO: add to the context def transformSplice(tree: tpd.Tree, tpe: Type, holeIdx: Int)(using Context): tpd.Tree = assert(level == 0) @@ -229,33 +217,23 @@ class Splicing extends MacroTransform: else super.transform(tree) case CapturedApplication(fn, argss) => transformCapturedApplication(tree, fn, argss) - case Apply(Select(Quote(body), nme.apply), quotes :: Nil) if level == 0 && body.isTerm => + case Apply(Select(Quote(body, _), nme.apply), quotes :: Nil) if level == 0 && body.isTerm => body match case _: RefTree if isCaptured(body.symbol) => capturedTerm(body) case _ => withCurrentQuote(quotes) { super.transform(tree) } - case Quote(body) if level == 0 => - val newBody = - if body.isTerm then transformLevel0QuoteContent(body)(using quoteContext) - else if containsCapturedType(body.tpe) then capturedPartTypes(body) - else body - cpy.Quote(tree)(newBody) + case tree: Quote if level == 0 => + if tree.body.isTerm then transformLevel0Quote(tree) + else if containsCapturedType(tree.body.tpe) then capturedPartTypes(tree) + else tree case _ => super.transform(tree) - private def transformLevel0QuoteContent(tree: Tree)(using Context): Tree = + private def transformLevel0Quote(quote: Quote)(using Context): Tree = // transform and collect new healed types - val old = healedTypes - healedTypes = new QuoteTypeTags(tree.span) - val tree1 = transform(tree) - val newHealedTypes = healedTypes.nn.getTypeTags - healedTypes = old - // add new healed types to the current, merge with existing healed types if necessary - if newHealedTypes.isEmpty then tree1 - else tree1 match - case Block(stats @ (x :: _), expr) if x.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot) => - Block(newHealedTypes ::: stats, expr) - case _ => - Block(newHealedTypes, tree1) + val (tags, body1) = inContextWithQuoteTypeTags { + transform(quote.body)(using quoteContext) + } + cpy.Quote(quote)(body1, quote.tags ::: tags) class ArgsClause(val args: List[Tree]): def isTerm: Boolean = args.isEmpty || args.head.isTerm @@ -341,35 +319,26 @@ class Splicing extends MacroTransform: .getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tpe)))._2 bindingSym - private def capturedPartTypes(tpt: Tree)(using Context): Tree = - val old = healedTypes - healedTypes = QuoteTypeTags(tpt.span) - val capturePartTypes = new TypeMap { - def apply(tp: Type) = tp match { - case typeRef: TypeRef if containsCapturedType(typeRef) => - val termRef = refBindingMap - .getOrElseUpdate(typeRef.symbol, (TypeTree(typeRef), newQuotedTypeClassBinding(typeRef)))._2.termRef - val tagRef = healedTypes.nn.getTagRef(termRef) - tagRef - case _ => - mapOver(tp) + private def capturedPartTypes(quote: Quote)(using Context): Tree = + val (tags, body1) = inContextWithQuoteTypeTags { + val capturePartTypes = new TypeMap { + def apply(tp: Type) = tp match { + case typeRef: TypeRef if containsCapturedType(typeRef) => + val termRef = refBindingMap + .getOrElseUpdate(typeRef.symbol, (TypeTree(typeRef), newQuotedTypeClassBinding(typeRef)))._2.termRef + val tagRef = getTagRef(termRef) + tagRef + case _ => + mapOver(tp) + } } + TypeTree(capturePartTypes(quote.body.tpe.widenTermRefExpr)) } - val captured = capturePartTypes(tpt.tpe.widenTermRefExpr) - val newHealedTypes = healedTypes.nn.getTypeTags - healedTypes = old - tpt match - case block: Block => - cpy.Block(block)(newHealedTypes ::: block.stats, TypeTree(captured)) - case _ => - if newHealedTypes.nonEmpty then - cpy.Block(tpt)(newHealedTypes, TypeTree(captured)) - else - tpt + cpy.Quote(quote)(body1, quote.tags ::: tags) private def getTagRefFor(tree: Tree)(using Context): Tree = val capturedTypeSym = capturedType(tree) - TypeTree(healedTypes.nn.getTagRef(capturedTypeSym.termRef)) + TypeTree(getTagRef(capturedTypeSym.termRef)) private def withCurrentQuote[T](newQuotes: Tree)(body: => T)(using Context): T = if level == 0 then @@ -392,7 +361,7 @@ class Splicing extends MacroTransform: Splice(closure, tpe) private def quoted(expr: Tree)(using Context): Tree = - tpd.Quote(expr).select(nme.apply).appliedTo(quotes.nn) + tpd.Quote(expr, Nil).select(nme.apply).appliedTo(quotes.nn) /** Helper methods to construct trees calling methods in `Quotes.reflect` based on the current `quotes` tree */ private object reflect extends ReifiedReflect { diff --git a/compiler/src/dotty/tools/dotc/transform/Staging.scala b/compiler/src/dotty/tools/dotc/transform/Staging.scala index 35a67e670457..43cbe80ce8c4 100644 --- a/compiler/src/dotty/tools/dotc/transform/Staging.scala +++ b/compiler/src/dotty/tools/dotc/transform/Staging.scala @@ -31,7 +31,7 @@ class Staging extends MacroTransform { override def allowsImplicitSearch: Boolean = true override def checkPostCondition(tree: Tree)(using Context): Unit = - if (ctx.phase <= splicingPhase) { + if (ctx.phase <= stagingPhase) { // Recheck that staging level consistency holds but do not heal any inconsistent types as they should already have been heald tree match { case PackageDef(pid, _) if tree.symbol.owner == defn.RootClass => diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 6d066ff4dc76..d51f5d283946 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -20,6 +20,8 @@ import ast.{tpd, untpd} import util.Chars._ import collection.mutable import ProtoTypes._ +import staging.StagingLevel +import inlines.Inlines.inInlineMethod import dotty.tools.backend.jvm.DottyBackendInterface.symExtensions @@ -657,6 +659,34 @@ object TreeChecker { else super.typedPackageDef(tree) + override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = + if ctx.phase <= stagingPhase.prev then + assert(tree.tags.isEmpty, i"unexpected tags in Quote before staging phase: ${tree.tags}") + else + assert(!tree.body.isInstanceOf[untpd.Splice] || inInlineMethod, i"missed quote cancellation in $tree") + assert(!tree.body.isInstanceOf[untpd.Hole] || inInlineMethod, i"missed quote cancellation in $tree") + if StagingLevel.level != 0 then + assert(tree.tags.isEmpty, i"unexpected tags in Quote at staging level ${StagingLevel.level}: ${tree.tags}") + + for tag <- tree.tags do + assert(tag.isInstanceOf[RefTree], i"expected RefTree in Quote but was: $tag") + + val tree1 = super.typedQuote(tree, pt) + for tag <- tree.tags do + assert(tag.typeOpt.derivesFrom(defn.QuotedTypeClass), i"expected Quote tag to be of type `Type` but was: ${tag.tpe}") + + tree1 match + case Quote(body, targ :: Nil) if body.isType => + assert(!(body.tpe =:= targ.tpe.select(tpnme.Underlying)), i"missed quote cancellation in $tree1") + case _ => + + tree1 + + override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = + if stagingPhase <= ctx.phase then + assert(!tree.expr.isInstanceOf[untpd.Quote] || inInlineMethod, i"missed quote cancellation in $tree") + super.typedSplice(tree, pt) + override def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = { val tree1 @ Hole(isTerm, _, args, content, tpt) = super.typedHole(tree, pt): @unchecked diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index b18cc55364d8..7238756454b3 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -312,7 +312,7 @@ object SpaceEngine { def isIrrefutableQuotedPattern(unapp: tpd.Tree, implicits: List[tpd.Tree], pt: Type)(using Context): Boolean = { implicits.headOption match // pattern '{ $x: T } - case Some(tpd.Apply(tpd.Select(tpd.Quote(tpd.TypeApply(fn, List(tpt))), nme.apply), _)) + case Some(tpd.Apply(tpd.Select(tpd.Quote(tpd.TypeApply(fn, List(tpt)), _), nme.apply), _)) if unapp.symbol.owner.eq(defn.QuoteMatching_ExprMatchModule) && fn.symbol.eq(defn.QuotedRuntimePatterns_patternHole) => pt <:< defn.QuotedExprClass.typeRef.appliedTo(tpt.tpe) diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 3a5ea05726a5..8b8858e16596 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -61,7 +61,7 @@ trait QuotesAndSplices { // TODO typecheck directly (without `exprQuote`) val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.body) val quotedExpr = typedApply(exprQuoteTree, pt)(using quoteContext) match - case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => untpd.Quote(quotedExpr).withBodyType(tpt.tpe) + case Apply(TypeApply(fn, tpt :: Nil), quotedExpr :: Nil) => untpd.Quote(quotedExpr, Nil).withBodyType(tpt.tpe) makeInlineable(quotedExpr.select(nme.apply).appliedTo(quotes).withSpan(tree.span)) } @@ -75,7 +75,7 @@ trait QuotesAndSplices { record("typedSplice") checkSpliceOutsideQuote(tree) tree.expr match { - case untpd.Quote(innerExpr) if innerExpr.isTerm => + case untpd.Quote(innerExpr, Nil) if innerExpr.isTerm => report.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.srcPos) return typed(innerExpr, pt) case _ => @@ -442,7 +442,7 @@ trait QuotesAndSplices { val quoteClass = if (quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass val quotedPattern = - if (quoted.isTerm) tpd.Quote(shape).select(nme.apply).appliedTo(quotes) + if (quoted.isTerm) tpd.Quote(shape, Nil).select(nme.apply).appliedTo(quotes) else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(quotes) val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index f8cfd4ca3ae0..ddc7a02fb4fb 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -98,7 +98,8 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking override def typedQuote(tree: untpd.Quote, pt: Type)(using Context): Tree = assertTyped(tree) val body1 = typed(tree.body, tree.bodyType)(using quoteContext) - untpd.cpy.Quote(tree)(body1).withType(tree.typeOpt) + for tag <- tree.tags do assertTyped(tag) + untpd.cpy.Quote(tree)(body1, tree.tags).withType(tree.typeOpt) override def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = assertTyped(tree) From 95d20669eefe23e0e1375e735ae0d91ed031a4c3 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Thu, 11 May 2023 14:53:08 +0200 Subject: [PATCH 562/657] deps: bump sbt-pack to latest 0.17 This pr bumps sbt-pack to the latest 0.17 and also accounts for a breaking change that was introduced in https://github.com/xerial/sbt-pack/pull/324 meaning that `pack` now must be scoped. --- project/Build.scala | 2 +- project/plugins.sbt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index c5586c68f981..30c3aded5be4 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1949,7 +1949,7 @@ object ScaladocConfigs { } lazy val DefaultGenerationConfig = Def.task { - def distLocation = (dist / pack).value + def distLocation = (dist / Compile / pack).value DefaultGenerationSettings.value } diff --git a/project/plugins.sbt b/project/plugins.sbt index aba843ca2c3c..2d8c9d22928d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -12,7 +12,7 @@ addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.10") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.0") -addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.13") +addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.17") addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3") From 2e712686411e0634605d3bcb6232ce06ffddd298 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 10 May 2023 15:47:09 +0200 Subject: [PATCH 563/657] Remove `tpt` from `Hole` Now the type is kept in the `Hole` node. --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 43 +++++++++---------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 6 +-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 4 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 4 +- .../tools/dotc/printing/RefinedPrinter.scala | 6 +-- .../tools/dotc/quoted/PickledQuotes.scala | 6 +-- .../tools/dotc/transform/PickleQuotes.scala | 7 +-- .../dotty/tools/dotc/transform/Splicing.scala | 2 +- .../tools/dotc/transform/TreeChecker.scala | 13 +++--- .../tools/dotc/typer/QuotesAndSplices.scala | 3 +- .../src/dotty/tools/dotc/typer/ReTyper.scala | 3 ++ 12 files changed, 52 insertions(+), 47 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index f4c3240c03bd..f3d7369d8d5d 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -746,6 +746,19 @@ object Trees { s"TypeTree${if (hasType) s"[$typeOpt]" else ""}" } + /** Tree that replaces a level 1 splices in pickled (level 0) quotes. + * It is only used when picking quotes (will never be in a TASTy file). + * + * @param isTerm If this hole is a term, otherwise it is a type hole. + * @param idx The index of the hole in it's enclosing level 0 quote. + * @param args The arguments of the splice to compute its content + * @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle. + */ + case class Hole[+T <: Untyped](override val isTerm: Boolean, idx: Int, args: List[Tree[T]], content: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] { + type ThisTree[+T <: Untyped] <: Hole[T] + override def isType: Boolean = !isTerm + } + /** A type tree whose type is inferred. These trees appear in two contexts * - as an argument of a TypeApply. In that case its type is always a TypeVar * - as a (result-)type of an inferred ValDef or DefDef. @@ -1035,20 +1048,6 @@ object Trees { def genericEmptyValDef[T <: Untyped]: ValDef[T] = theEmptyValDef.asInstanceOf[ValDef[T]] def genericEmptyTree[T <: Untyped]: Thicket[T] = theEmptyTree.asInstanceOf[Thicket[T]] - /** Tree that replaces a level 1 splices in pickled (level 0) quotes. - * It is only used when picking quotes (will never be in a TASTy file). - * - * @param isTerm If this hole is a term, otherwise it is a type hole. - * @param idx The index of the hole in it's enclosing level 0 quote. - * @param args The arguments of the splice to compute its content - * @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle. - * @param tpt Type of the hole - */ - case class Hole[+T <: Untyped](override val isTerm: Boolean, idx: Int, args: List[Tree[T]], content: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] { - type ThisTree[+T <: Untyped] <: Hole[T] - override def isType: Boolean = !isTerm - } - def flatten[T <: Untyped](trees: List[Tree[T]]): List[Tree[T]] = { def recur(buf: ListBuffer[Tree[T]] | Null, remaining: List[Tree[T]]): ListBuffer[Tree[T]] | Null = remaining match { @@ -1406,9 +1405,9 @@ object Trees { case tree: Thicket if (trees eq tree.trees) => tree case _ => finalize(tree, untpd.Thicket(trees)(sourceFile(tree))) } - def Hole(tree: Tree)(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = tree match { + def Hole(tree: Tree)(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree)(using Context): Hole = tree match { case tree: Hole if isTerm == tree.isTerm && idx == tree.idx && args.eq(tree.args) && content.eq(tree.content) && content.eq(tree.content) => tree - case _ => finalize(tree, untpd.Hole(isTerm, idx, args, content, tpt)(sourceFile(tree))) + case _ => finalize(tree, untpd.Hole(isTerm, idx, args, content)(sourceFile(tree))) } // Copier methods with default arguments; these demand that the original tree @@ -1431,8 +1430,8 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) - def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = - Hole(tree: Tree)(isTerm, idx, args, content, tpt) + def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content)(using Context): Hole = + Hole(tree: Tree)(isTerm, idx, args, content) } @@ -1567,8 +1566,8 @@ object Trees { cpy.Quote(tree)(transform(body)(using quoteContext), transform(tags)) case tree @ Splice(expr) => cpy.Splice(tree)(transform(expr)(using spliceContext)) - case tree @ Hole(isTerm, idx, args, content, tpt) => - cpy.Hole(tree)(isTerm, idx, transform(args), transform(content), transform(tpt)) + case tree @ Hole(isTerm, idx, args, content) => + cpy.Hole(tree)(isTerm, idx, transform(args), transform(content)) case _ => transformMoreCases(tree) } @@ -1712,8 +1711,8 @@ object Trees { this(this(x, body)(using quoteContext), tags) case Splice(expr) => this(x, expr)(using spliceContext) - case Hole(_, _, args, content, tpt) => - this(this(this(x, args), content), tpt) + case Hole(_, _, args, content) => + this(this(x, args), content) case _ => foldMoreCases(x, tree) } diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 503c1a0b1b39..76e16cc00a90 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -176,6 +176,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Splice(expr: Tree, tpe: Type)(using Context): Splice = untpd.Splice(expr).withType(tpe) + def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpe: Type)(using Context): Hole = + untpd.Hole(isTerm, idx, args, content).withType(tpe) + def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree = (if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp) @@ -397,9 +400,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Throw(expr: Tree)(using Context): Tree = ref(defn.throwMethod).appliedTo(expr) - def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = - ta.assignType(untpd.Hole(isTerm, idx, args, content, tpt), tpt) - // ------ Making references ------------------------------------------------------ def prefixIsElidable(tp: NamedType)(using Context): Boolean = { diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 479d85f6e383..91299be537e8 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -424,7 +424,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def Export(expr: Tree, selectors: List[ImportSelector])(implicit src: SourceFile): Export = new Export(expr, selectors) def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats) def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot) - def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(implicit src: SourceFile): Hole = new Hole(isTerm, idx, args, content, tpt) + def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree)(implicit src: SourceFile): Hole = new Hole(isTerm, idx, args, content) // ------ Additional creation methods for untyped only ----------------- diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 898ef2ed0c6c..645c6f81e539 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -685,11 +685,11 @@ class TreePickler(pickler: TastyPickler) { .appliedTo(expr) .withSpan(tree.span) ) - case Hole(_, idx, args, _, tpt) => + case Hole(_, idx, args, _) => writeByte(HOLE) withLength { writeNat(idx) - pickleType(tpt.tpe, richTypes = true) + pickleType(tree.tpe, richTypes = true) args.foreach(pickleTree) } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 659d96a386ea..98bd7152ff37 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1457,7 +1457,7 @@ class TreeUnpickler(reader: TastyReader, val idx = readNat() val tpe = readType() val args = until(end)(readTree()) - Hole(true, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) + Hole(true, idx, args, EmptyTree, tpe) case _ => readPathTree() } @@ -1491,7 +1491,7 @@ class TreeUnpickler(reader: TastyReader, val idx = readNat() val tpe = readType() val args = until(end)(readTree()) - Hole(false, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) + Hole(false, idx, args, EmptyTree, tpe) case _ => if (isTypeTreeTag(nextByte)) readTree() else { diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 47dac083cfce..0b539db283ed 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -735,12 +735,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case Splice(expr) => val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") - case Hole(isTerm, idx, args, content, tpt) => + case Hole(isTerm, idx, args, content) => val (prefix, postfix) = if isTerm then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") val contentText = toTextGlobal(content) - val tptText = toTextGlobal(tpt) - prefix ~~ idx.toString ~~ "|" ~~ tptText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix + val tpeText = toTextGlobal(tree.typeOpt) + prefix ~~ idx.toString ~~ "|" ~~ tpeText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix case CapturesAndResult(refs, parent) => changePrec(GlobalPrec)("^{" ~ Text(refs.map(toText), ", ") ~ "}" ~ toText(parent)) case _ => diff --git a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala index 89efcb4272ec..7596549fe401 100644 --- a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala @@ -100,7 +100,7 @@ object PickledQuotes { private def spliceTerms(tree: Tree, typeHole: TypeHole, termHole: ExprHole)(using Context): Tree = { def evaluateHoles = new TreeMap { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match { - case Hole(isTerm, idx, args, _, _) => + case Hole(isTerm, idx, args, _) => inContext(SpliceScope.contextWithNewSpliceScope(tree.sourcePos)) { if isTerm then val quotedExpr = termHole match @@ -165,7 +165,7 @@ object PickledQuotes { val tree = typeHole match case TypeHole.V1(evalHole) => tdef.rhs match - case TypeBoundsTree(_, Hole(_, idx, args, _, _), _) => + case TypeBoundsTree(_, Hole(_, idx, args, _), _) => // To keep for backwards compatibility. In some older version holes where created in the bounds. val quotedType = evalHole.nn.apply(idx, reifyTypeHoleArgs(args)) PickledQuotes.quotedTypeToTree(quotedType) @@ -173,7 +173,7 @@ object PickledQuotes { // To keep for backwards compatibility. In some older version we missed the creation of some holes. tpt case TypeHole.V2(types) => - val Hole(_, idx, _, _, _) = tdef.rhs: @unchecked + val Hole(_, idx, _, _) = tdef.rhs: @unchecked PickledQuotes.quotedTypeToTree(types.nn.apply(idx)) (tdef.symbol, tree.tpe) }.toMap diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 98bca65093fa..5abc92681295 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -14,6 +14,7 @@ import ast.TreeTypeMap import SymUtils._ import NameKinds._ import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.config.ScalaRelease.* import scala.collection.mutable @@ -108,12 +109,12 @@ class PickleQuotes extends MacroTransform { private val contents = List.newBuilder[Tree] override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case tree @ Hole(isTerm, _, _, content, _) => + case tree @ Hole(isTerm, _, _, content) => assert(isTerm) assert(!content.isEmpty) contents += content val holeType = getTermHoleType(tree.tpe) - val hole = cpy.Hole(tree)(content = EmptyTree, TypeTree(holeType)) + val hole = untpd.cpy.Hole(tree)(content = EmptyTree).withType(holeType) cpy.Inlined(tree)(EmptyTree, Nil, hole) case tree: DefTree => val newAnnotations = tree.symbol.annotations.mapconserve { annot => @@ -197,7 +198,7 @@ class PickleQuotes extends MacroTransform { private def mkTagSymbolAndAssignType(typeArg: Tree, idx: Int)(using Context): TypeDef = { val holeType = getTypeHoleType(typeArg.tpe.select(tpnme.Underlying)) - val hole = cpy.Hole(typeArg)(isTerm = false, idx, Nil, EmptyTree, TypeTree(holeType)) + val hole = untpd.cpy.Hole(typeArg)(isTerm = false, idx, Nil, EmptyTree).withType(holeType) val local = newSymbol( owner = ctx.owner, name = UniqueName.fresh(hole.tpe.dealias.typeSymbol.name.toTypeName), diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index bf7eb2dedb7d..ff5dc5042eaf 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -181,7 +181,7 @@ class Splicing extends MacroTransform: val ddef = DefDef(meth, List(bindings), newTree.tpe, newTree.changeOwner(ctx.owner, meth)) val fnType = defn.FunctionType(bindings.size, isContextual = false).appliedTo(bindingsTypes :+ newTree.tpe) val closure = Block(ddef :: Nil, Closure(Nil, ref(meth), TypeTree(fnType))) - tpd.Hole(true, holeIdx, refs, closure, TypeTree(tpe)) + tpd.Hole(true, holeIdx, refs, closure, tpe) override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index d51f5d283946..e50fb9d8b09c 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -688,7 +688,10 @@ object TreeChecker { super.typedSplice(tree, pt) override def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = { - val tree1 @ Hole(isTerm, _, args, content, tpt) = super.typedHole(tree, pt): @unchecked + val tree1 @ Hole(isTerm, idx, args, content) = super.typedHole(tree, pt): @unchecked + + assert(idx >= 0, i"hole should not have negative index: $tree") + assert(isTerm || tree.args.isEmpty, i"type hole should not have arguments: $tree") // Check that we only add the captured type `T` instead of a more complex type like `List[T]`. // If we have `F[T]` with captured `F` and `T`, we should list `F` and `T` separately in the args. @@ -696,8 +699,8 @@ object TreeChecker { assert(arg.isTerm || arg.tpe.isInstanceOf[TypeRef], "Expected TypeRef in Hole type args but got: " + arg.tpe) // Check result type of the hole - if isTerm then assert(tpt.typeOpt <:< pt) - else assert(tpt.typeOpt =:= pt) + if isTerm then assert(tree1.typeOpt <:< pt) + else assert(tree1.typeOpt =:= pt) // Check that the types of the args conform to the types of the contents of the hole val argQuotedTypes = args.map { arg => @@ -712,8 +715,8 @@ object TreeChecker { else defn.QuotedTypeClass.typeRef.appliedTo(arg.typeOpt.widenTermRefExpr) } val expectedResultType = - if isTerm then defn.QuotedExprClass.typeRef.appliedTo(tpt.typeOpt) - else defn.QuotedTypeClass.typeRef.appliedTo(tpt.typeOpt) + if isTerm then defn.QuotedExprClass.typeRef.appliedTo(tree1.typeOpt) + else defn.QuotedTypeClass.typeRef.appliedTo(tree1.typeOpt) val contextualResult = defn.FunctionOf(List(defn.QuotesClass.typeRef), expectedResultType, isContextual = true) val expectedContentType = diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 8b8858e16596..ea2b380dc0ed 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -116,8 +116,7 @@ trait QuotesAndSplices { } def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = - val tpt = typedType(tree.tpt) - assignType(tree, tpt) + throw new UnsupportedOperationException("cannot type check a Hole node") /** Types a splice applied to some arguments `$f(arg1, ..., argn)` in a quote pattern. * diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index ddc7a02fb4fb..1fa6e967fbe1 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -111,6 +111,9 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking val expr1 = typed(tree.expr, quoteType)(using spliceContext) untpd.cpy.Splice(tree)(expr1).withType(tree.typeOpt) + override def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = + promote(tree) + override def localDummy(cls: ClassSymbol, impl: untpd.Template)(using Context): Symbol = impl.symbol override def retrieveSym(tree: untpd.Tree)(using Context): Symbol = tree.symbol From 449cbc226e8facb3d69fd9cfb9b5213ba65e43a7 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Tue, 9 May 2023 19:58:18 +0200 Subject: [PATCH 564/657] Warn when calling methods of topClasses on Predef or on package objects Co-Authored-By: Dale Wijnand Co-Authored-By: itielsa <38278165+itielsa@users.noreply.github.com> --- .../tools/dotc/reporting/ErrorMessageID.scala | 2 + .../dotty/tools/dotc/reporting/messages.scala | 19 +++++ .../dotty/tools/dotc/typer/RefChecks.scala | 20 +++++ compiler/test/dotty/tools/utils.scala | 4 +- tests/neg/i17266.check | 84 +++++++++++++++++++ tests/neg/i17266.scala | 49 +++++++++++ 6 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 tests/neg/i17266.check create mode 100644 tests/neg/i17266.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index cc2741bcb614..1802139de502 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -194,6 +194,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case MissingArgumentListID // errorNumber: 178 case MatchTypeScrutineeCannotBeHigherKindedID // errorNumber: 179 case AmbiguousExtensionMethodID // errorNumber 180 + case CallToAnyRefMethodOnPredefID // errorNumber: 181 + case CallToAnyRefMethodOnPackageObjectID // errorNumber: 182 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 98a5d73223ac..299d15e65443 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2288,6 +2288,25 @@ class PureExpressionInStatementPosition(stat: untpd.Tree, val exprOwner: Symbol) |It can be removed without changing the semantics of the program. This may indicate an error.""" } +class CallToAnyRefMethodOnPredef(stat: untpd.Tree, method: Symbol)(using Context) + extends Message(CallToAnyRefMethodOnPredefID) { + def kind = MessageKind.PotentialIssue + def msg(using Context) = i"Suspicious call to ${hl("Predef." + method.name)}" + def explain(using Context) = + i"""Top-level unqualified calls to ${hl("AnyRef")} or ${hl("Any")} methods such as ${hl(method.name.toString)} are + |resolved to calls on ${hl("Predef")}. This might not be what you intended.""" +} + +class CallToAnyRefMethodOnPackageObject(stat: untpd.Tree, method: Symbol)(using Context) + extends Message(CallToAnyRefMethodOnPackageObjectID) { + def kind = MessageKind.PotentialIssue + def msg(using Context) = i"Suspicious top-level call to ${hl("this." + method.name)}" + def explain(using Context) = + i"""Top-level calls to ${hl("AnyRef")} or ${hl("Any")} methods are resolved to calls on the + |synthetic package object generated for the current file. This might not be + |what you intended.""" +} + class TraitCompanionWithMutableStatic()(using Context) extends SyntaxMsg(TraitCompanionWithMutableStaticID) { def msg(using Context) = i"Companion of traits cannot define mutable @static fields" diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 73331046b41f..108a141e28c6 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1090,6 +1090,17 @@ object RefChecks { end checkImplicitNotFoundAnnotation + def checkSynchronizedCall(tree: Tree, symbol: Symbol)(using Context) = + if symbol.exists && defn.topClasses.contains(symbol.owner) then + tree.tpe match + case tp: NamedType => + val prefixSymbol = tp.prefix.typeSymbol + if prefixSymbol == defn.ScalaPredefModuleClass then + report.warning(CallToAnyRefMethodOnPredef(tree, symbol), tree) + if prefixSymbol.isPackageObject && !tp.symbol.isConstructor then + report.warning(CallToAnyRefMethodOnPackageObject(tree, symbol), tree) + case _ => () + } import RefChecks._ @@ -1168,6 +1179,15 @@ class RefChecks extends MiniPhase { thisPhase => report.error(ex, tree.srcPos) tree } + + override def transformSelect(tree: Select)(using Context): Tree = + checkSynchronizedCall(tree, tree.denot.symbol) + tree + + override def transformIdent(tree: Ident)(using Context): Tree = + checkSynchronizedCall(tree, tree.symbol) + tree + } /* todo: rewrite and re-enable diff --git a/compiler/test/dotty/tools/utils.scala b/compiler/test/dotty/tools/utils.scala index bfedc338f25a..75918674146c 100644 --- a/compiler/test/dotty/tools/utils.scala +++ b/compiler/test/dotty/tools/utils.scala @@ -17,8 +17,10 @@ import scala.util.control.{ControlThrowable, NonFatal} import dotc.config.CommandLineParser +object Dummy + def scripts(path: String): Array[File] = { - val dir = new File(this.getClass.getResource(path).getPath) + val dir = new File(Dummy.getClass.getResource(path).getPath) assert(dir.exists && dir.isDirectory, "Couldn't load scripts dir") dir.listFiles.filter { f => val path = if f.isDirectory then f.getPath + "/" else f.getPath diff --git a/tests/neg/i17266.check b/tests/neg/i17266.check new file mode 100644 index 000000000000..11742a5f2378 --- /dev/null +++ b/tests/neg/i17266.check @@ -0,0 +1,84 @@ +-- [E181] Potential Issue Error: tests/neg/i17266.scala:4:2 ------------------------------------------------------------ +4 | synchronized { // error + | ^^^^^^^^^^^^ + | Suspicious call to Predef.synchronized + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level unqualified calls to AnyRef or Any methods such as synchronized are + | resolved to calls on Predef. This might not be what you intended. + --------------------------------------------------------------------------------------------------------------------- +-- [E182] Potential Issue Error: tests/neg/i17266.scala:9:7 ------------------------------------------------------------ +9 | this.synchronized { // error + | ^^^^^^^^^^^^^^^^^ + | Suspicious top-level call to this.synchronized + |--------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level calls to AnyRef or Any methods are resolved to calls on the + | synthetic package object generated for the current file. This might not be + | what you intended. + --------------------------------------------------------------------------------------------------------------------- +-- [E181] Potential Issue Error: tests/neg/i17266.scala:22:2 ----------------------------------------------------------- +22 | wait() // error + | ^^^^ + | Suspicious call to Predef.wait + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level unqualified calls to AnyRef or Any methods such as wait are + | resolved to calls on Predef. This might not be what you intended. + -------------------------------------------------------------------------------------------------------------------- +-- [E182] Potential Issue Error: tests/neg/i17266.scala:25:7 ----------------------------------------------------------- +25 | this.wait() // error + | ^^^^^^^^^ + | Suspicious top-level call to this.wait + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level calls to AnyRef or Any methods are resolved to calls on the + | synthetic package object generated for the current file. This might not be + | what you intended. + -------------------------------------------------------------------------------------------------------------------- +-- [E181] Potential Issue Error: tests/neg/i17266.scala:32:2 ----------------------------------------------------------- +32 | wait(10) // error + | ^^^^ + | Suspicious call to Predef.wait + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level unqualified calls to AnyRef or Any methods such as wait are + | resolved to calls on Predef. This might not be what you intended. + -------------------------------------------------------------------------------------------------------------------- +-- [E182] Potential Issue Error: tests/neg/i17266.scala:35:7 ----------------------------------------------------------- +35 | this.wait(10) // error + | ^^^^^^^^^ + | Suspicious top-level call to this.wait + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level calls to AnyRef or Any methods are resolved to calls on the + | synthetic package object generated for the current file. This might not be + | what you intended. + -------------------------------------------------------------------------------------------------------------------- +-- [E181] Potential Issue Error: tests/neg/i17266.scala:42:2 ----------------------------------------------------------- +42 | hashCode() // error + | ^^^^^^^^ + | Suspicious call to Predef.hashCode + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level unqualified calls to AnyRef or Any methods such as hashCode are + | resolved to calls on Predef. This might not be what you intended. + -------------------------------------------------------------------------------------------------------------------- +-- [E182] Potential Issue Error: tests/neg/i17266.scala:45:7 ----------------------------------------------------------- +45 | this.hashCode() // error + | ^^^^^^^^^^^^^ + | Suspicious top-level call to this.hashCode + |-------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level calls to AnyRef or Any methods are resolved to calls on the + | synthetic package object generated for the current file. This might not be + | what you intended. + -------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i17266.scala b/tests/neg/i17266.scala new file mode 100644 index 000000000000..1edcebf4781c --- /dev/null +++ b/tests/neg/i17266.scala @@ -0,0 +1,49 @@ +// scalac: -Werror -explain + +def test1 = + synchronized { // error + println("hello") + } + +def test2 = + this.synchronized { // error + println("hello") + } + +object MyLib + +def test3 = + import MyLib.* + synchronized { // not an error (should be?) + println("hello") + } + +def test4 = + wait() // error + +def test5 = + this.wait() // error + +def test6 = + import MyLib.* + wait() // not an error (should be?) + +def test7 = + wait(10) // error + +def test8 = + this.wait(10) // error + +def test9 = + import MyLib.* + wait(10) // not an error (should be?) + +def test10 = + hashCode() // error + +def test11 = + this.hashCode() // error + +def test12 = + import MyLib.* + hashCode() // not an error (should be?) From 5c4e59731038e942460f3f40c4a23a78000047e4 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Thu, 11 May 2023 15:06:59 +0200 Subject: [PATCH 565/657] Warn on unqualified top-level calls to Any or AnyRef methods --- .../tools/dotc/reporting/ErrorMessageID.scala | 3 +- .../dotty/tools/dotc/reporting/messages.scala | 19 +-- .../dotty/tools/dotc/typer/RefChecks.scala | 21 +-- tests/neg/i17266.check | 148 +++++++++--------- tests/neg/i17266.scala | 127 +++++++++++++-- 5 files changed, 199 insertions(+), 119 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 1802139de502..fc679210db17 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -194,8 +194,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case MissingArgumentListID // errorNumber: 178 case MatchTypeScrutineeCannotBeHigherKindedID // errorNumber: 179 case AmbiguousExtensionMethodID // errorNumber 180 - case CallToAnyRefMethodOnPredefID // errorNumber: 181 - case CallToAnyRefMethodOnPackageObjectID // errorNumber: 182 + case UnqualifiedCallToAnyRefMethodID // errorNumber: 181 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 299d15e65443..d205b816214c 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -2288,23 +2288,14 @@ class PureExpressionInStatementPosition(stat: untpd.Tree, val exprOwner: Symbol) |It can be removed without changing the semantics of the program. This may indicate an error.""" } -class CallToAnyRefMethodOnPredef(stat: untpd.Tree, method: Symbol)(using Context) - extends Message(CallToAnyRefMethodOnPredefID) { +class UnqualifiedCallToAnyRefMethod(stat: untpd.Tree, method: Symbol)(using Context) + extends Message(UnqualifiedCallToAnyRefMethodID) { def kind = MessageKind.PotentialIssue - def msg(using Context) = i"Suspicious call to ${hl("Predef." + method.name)}" + def msg(using Context) = i"Suspicious top-level unqualified call to ${hl(method.name.toString)}" def explain(using Context) = i"""Top-level unqualified calls to ${hl("AnyRef")} or ${hl("Any")} methods such as ${hl(method.name.toString)} are - |resolved to calls on ${hl("Predef")}. This might not be what you intended.""" -} - -class CallToAnyRefMethodOnPackageObject(stat: untpd.Tree, method: Symbol)(using Context) - extends Message(CallToAnyRefMethodOnPackageObjectID) { - def kind = MessageKind.PotentialIssue - def msg(using Context) = i"Suspicious top-level call to ${hl("this." + method.name)}" - def explain(using Context) = - i"""Top-level calls to ${hl("AnyRef")} or ${hl("Any")} methods are resolved to calls on the - |synthetic package object generated for the current file. This might not be - |what you intended.""" + |resolved to calls on ${hl("Predef")} or on imported methods. This might not be what + |you intended.""" } class TraitCompanionWithMutableStatic()(using Context) diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 108a141e28c6..fe28a8b18833 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1090,16 +1090,11 @@ object RefChecks { end checkImplicitNotFoundAnnotation - def checkSynchronizedCall(tree: Tree, symbol: Symbol)(using Context) = - if symbol.exists && defn.topClasses.contains(symbol.owner) then - tree.tpe match - case tp: NamedType => - val prefixSymbol = tp.prefix.typeSymbol - if prefixSymbol == defn.ScalaPredefModuleClass then - report.warning(CallToAnyRefMethodOnPredef(tree, symbol), tree) - if prefixSymbol.isPackageObject && !tp.symbol.isConstructor then - report.warning(CallToAnyRefMethodOnPackageObject(tree, symbol), tree) - case _ => () + def checkAnyRefMethodCall(tree: Tree)(using Context) = + if tree.symbol.exists + && defn.topClasses.contains(tree.symbol.owner) + && (!ctx.owner.enclosingClass.exists || ctx.owner.enclosingClass.isPackageObject) then + report.warning(UnqualifiedCallToAnyRefMethod(tree, tree.symbol), tree) } import RefChecks._ @@ -1180,12 +1175,8 @@ class RefChecks extends MiniPhase { thisPhase => tree } - override def transformSelect(tree: Select)(using Context): Tree = - checkSynchronizedCall(tree, tree.denot.symbol) - tree - override def transformIdent(tree: Ident)(using Context): Tree = - checkSynchronizedCall(tree, tree.symbol) + checkAnyRefMethodCall(tree) tree } diff --git a/tests/neg/i17266.check b/tests/neg/i17266.check index 11742a5f2378..7e07e3d43de4 100644 --- a/tests/neg/i17266.check +++ b/tests/neg/i17266.check @@ -1,84 +1,88 @@ -- [E181] Potential Issue Error: tests/neg/i17266.scala:4:2 ------------------------------------------------------------ 4 | synchronized { // error | ^^^^^^^^^^^^ - | Suspicious call to Predef.synchronized + | Suspicious top-level unqualified call to synchronized |--------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Top-level unqualified calls to AnyRef or Any methods such as synchronized are - | resolved to calls on Predef. This might not be what you intended. + | resolved to calls on Predef or on imported methods. This might not be what + | you intended. --------------------------------------------------------------------------------------------------------------------- --- [E182] Potential Issue Error: tests/neg/i17266.scala:9:7 ------------------------------------------------------------ -9 | this.synchronized { // error - | ^^^^^^^^^^^^^^^^^ - | Suspicious top-level call to this.synchronized - |--------------------------------------------------------------------------------------------------------------------- - | Explanation (enabled by `-explain`) - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Top-level calls to AnyRef or Any methods are resolved to calls on the - | synthetic package object generated for the current file. This might not be - | what you intended. - --------------------------------------------------------------------------------------------------------------------- --- [E181] Potential Issue Error: tests/neg/i17266.scala:22:2 ----------------------------------------------------------- -22 | wait() // error - | ^^^^ - | Suspicious call to Predef.wait - |-------------------------------------------------------------------------------------------------------------------- - | Explanation (enabled by `-explain`) - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Top-level unqualified calls to AnyRef or Any methods such as wait are - | resolved to calls on Predef. This might not be what you intended. - -------------------------------------------------------------------------------------------------------------------- --- [E182] Potential Issue Error: tests/neg/i17266.scala:25:7 ----------------------------------------------------------- -25 | this.wait() // error - | ^^^^^^^^^ - | Suspicious top-level call to this.wait - |-------------------------------------------------------------------------------------------------------------------- - | Explanation (enabled by `-explain`) - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Top-level calls to AnyRef or Any methods are resolved to calls on the - | synthetic package object generated for the current file. This might not be - | what you intended. - -------------------------------------------------------------------------------------------------------------------- --- [E181] Potential Issue Error: tests/neg/i17266.scala:32:2 ----------------------------------------------------------- -32 | wait(10) // error - | ^^^^ - | Suspicious call to Predef.wait - |-------------------------------------------------------------------------------------------------------------------- - | Explanation (enabled by `-explain`) - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Top-level unqualified calls to AnyRef or Any methods such as wait are - | resolved to calls on Predef. This might not be what you intended. - -------------------------------------------------------------------------------------------------------------------- --- [E182] Potential Issue Error: tests/neg/i17266.scala:35:7 ----------------------------------------------------------- -35 | this.wait(10) // error - | ^^^^^^^^^ - | Suspicious top-level call to this.wait - |-------------------------------------------------------------------------------------------------------------------- - | Explanation (enabled by `-explain`) - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Top-level calls to AnyRef or Any methods are resolved to calls on the - | synthetic package object generated for the current file. This might not be - | what you intended. - -------------------------------------------------------------------------------------------------------------------- --- [E181] Potential Issue Error: tests/neg/i17266.scala:42:2 ----------------------------------------------------------- -42 | hashCode() // error - | ^^^^^^^^ - | Suspicious call to Predef.hashCode - |-------------------------------------------------------------------------------------------------------------------- - | Explanation (enabled by `-explain`) - |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Top-level unqualified calls to AnyRef or Any methods such as hashCode are - | resolved to calls on Predef. This might not be what you intended. - -------------------------------------------------------------------------------------------------------------------- --- [E182] Potential Issue Error: tests/neg/i17266.scala:45:7 ----------------------------------------------------------- -45 | this.hashCode() // error - | ^^^^^^^^^^^^^ - | Suspicious top-level call to this.hashCode +-- [E181] Potential Issue Error: tests/neg/i17266.scala:17:2 ----------------------------------------------------------- +17 | synchronized { // error + | ^^^^^^^^^^^^ + | Suspicious top-level unqualified call to synchronized |-------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Top-level calls to AnyRef or Any methods are resolved to calls on the - | synthetic package object generated for the current file. This might not be - | what you intended. + | Top-level unqualified calls to AnyRef or Any methods such as synchronized are + | resolved to calls on Predef or on imported methods. This might not be what + | you intended. -------------------------------------------------------------------------------------------------------------------- +-- [E181] Potential Issue Error: tests/neg/i17266.scala:108:2 ---------------------------------------------------------- +108 | wait() // error + | ^^^^ + | Suspicious top-level unqualified call to wait + |------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level unqualified calls to AnyRef or Any methods such as wait are + | resolved to calls on Predef or on imported methods. This might not be what + | you intended. + ------------------------------------------------------------------------------------------------------------------- +-- [E181] Potential Issue Error: tests/neg/i17266.scala:115:2 ---------------------------------------------------------- +115 | wait() // error + | ^^^^ + | Suspicious top-level unqualified call to wait + |------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level unqualified calls to AnyRef or Any methods such as wait are + | resolved to calls on Predef or on imported methods. This might not be what + | you intended. + ------------------------------------------------------------------------------------------------------------------- +-- [E181] Potential Issue Error: tests/neg/i17266.scala:121:2 ---------------------------------------------------------- +121 | wait(10) // error + | ^^^^ + | Suspicious top-level unqualified call to wait + |------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level unqualified calls to AnyRef or Any methods such as wait are + | resolved to calls on Predef or on imported methods. This might not be what + | you intended. + ------------------------------------------------------------------------------------------------------------------- +-- [E181] Potential Issue Error: tests/neg/i17266.scala:128:2 ---------------------------------------------------------- +128 | wait(10) // error + | ^^^^ + | Suspicious top-level unqualified call to wait + |------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level unqualified calls to AnyRef or Any methods such as wait are + | resolved to calls on Predef or on imported methods. This might not be what + | you intended. + ------------------------------------------------------------------------------------------------------------------- +-- [E181] Potential Issue Error: tests/neg/i17266.scala:134:2 ---------------------------------------------------------- +134 | hashCode() // error + | ^^^^^^^^ + | Suspicious top-level unqualified call to hashCode + |------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level unqualified calls to AnyRef or Any methods such as hashCode are + | resolved to calls on Predef or on imported methods. This might not be what + | you intended. + ------------------------------------------------------------------------------------------------------------------- +-- [E181] Potential Issue Error: tests/neg/i17266.scala:141:2 ---------------------------------------------------------- +141 | hashCode() // error + | ^^^^^^^^ + | Suspicious top-level unqualified call to hashCode + |------------------------------------------------------------------------------------------------------------------- + | Explanation (enabled by `-explain`) + |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Top-level unqualified calls to AnyRef or Any methods such as hashCode are + | resolved to calls on Predef or on imported methods. This might not be what + | you intended. + ------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/i17266.scala b/tests/neg/i17266.scala index 1edcebf4781c..5b74ea76810b 100644 --- a/tests/neg/i17266.scala +++ b/tests/neg/i17266.scala @@ -6,7 +6,7 @@ def test1 = } def test2 = - this.synchronized { // error + this.synchronized { // not an error (should be?) println("hello") } @@ -14,36 +14,131 @@ object MyLib def test3 = import MyLib.* - synchronized { // not an error (should be?) + synchronized { // error println("hello") } def test4 = + 1.synchronized { // not an error (should be?) + println("hello") + } + +object Test4: + synchronized { // not an error + println("hello") + } + +object Test5: + def test5 = + synchronized { // not an error + println("hello") + } + +object Test6: + import MyLib.* + synchronized { // not an error + println("hello") + } + +object Test7: + import MyLib.* + def test7 = + synchronized { // not an error + println("hello") + } + +/* +object Test7b: + def test8 = + import MyLib.* + synchronized { // already an error: Reference to synchronized is ambiguous. + println("hello") + } +*/ + +class Test8: + synchronized { // not an error + println("hello") + } + +class Test9: + def test5 = + synchronized { // not an error + println("hello") + } + +class Test10: + import MyLib.* + synchronized { // not an error + println("hello") + } + +class Test11: + import MyLib.* + def test7 = + synchronized { // not an error + println("hello") + } + +trait Test12: + synchronized { // not an error + println("hello") + } + +trait Test13: + def test5 = + synchronized { // not an error + println("hello") + } + +trait Test14: + import MyLib.* + synchronized { // not an error + println("hello") + } + +trait Test15: + import MyLib.* + def test7 = + synchronized { // not an error + println("hello") + } + +def test16 = wait() // error -def test5 = - this.wait() // error +def test17 = + this.wait() // not an error (should be?) -def test6 = +def test18 = import MyLib.* - wait() // not an error (should be?) + wait() // error -def test7 = +def test19 = + 1.wait() // not an error (should be?) + +def test20 = wait(10) // error -def test8 = - this.wait(10) // error +def test21 = + this.wait(10) // not an error (should be?) -def test9 = +def test22 = import MyLib.* - wait(10) // not an error (should be?) + wait(10) // error + +def test23 = + 1.wait(10) // not an error (should be?) -def test10 = +def test24 = hashCode() // error -def test11 = - this.hashCode() // error +def test25 = + this.hashCode() // not an error (should be?) -def test12 = +def test26 = import MyLib.* - hashCode() // not an error (should be?) + hashCode() // error + +def test27 = + 1.hashCode()// not an error (should be? probably not) From 77eb00f681462704597349f33cb8566bf49a04d2 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 08:26:48 +0200 Subject: [PATCH 566/657] test: add in regression test for #5700 --- tests/pos/i5700.scala | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/pos/i5700.scala diff --git a/tests/pos/i5700.scala b/tests/pos/i5700.scala new file mode 100644 index 000000000000..69892dea16f4 --- /dev/null +++ b/tests/pos/i5700.scala @@ -0,0 +1,5 @@ +// https://github.com/lampepfl/dotty/issues/5700 +object noRecursionLimit: + type M = { type T[+A]; type Ev >: T[Any] <: T[Nothing] } + val M: M = ().asInstanceOf[M] + def dcast(m: M.T[Any]): M.T[Int] = m: M.Ev From b080235ed1e7aba3a5d603dd34ae27360f521958 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 08:32:34 +0200 Subject: [PATCH 567/657] test: add regression test for #7790 --- tests/pos/i7790.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/pos/i7790.scala diff --git a/tests/pos/i7790.scala b/tests/pos/i7790.scala new file mode 100644 index 000000000000..0a0e2d2ce347 --- /dev/null +++ b/tests/pos/i7790.scala @@ -0,0 +1,9 @@ +// https://github.com/lampepfl/dotty/issues/7790 +trait Foo: + given Int = 10 + def map(f: Int ?=> Int) = f + def map(f: Int ?=> String) = f + +@main def Test = + val m: Foo = ??? + m.map((x: Int) ?=> x) From 77a44df63655dfa4a7dcad2148e76e2813f8623d Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 08:37:13 +0200 Subject: [PATCH 568/657] test: add in regression test for #7653 --- tests/pos/i7653.scala | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/pos/i7653.scala diff --git a/tests/pos/i7653.scala b/tests/pos/i7653.scala new file mode 100644 index 000000000000..8511b6eef69b --- /dev/null +++ b/tests/pos/i7653.scala @@ -0,0 +1,22 @@ +// https://github.com/lampepfl/dotty/issues/7653 + +object options2 { + type Option[T] = { + def isEmpty: Boolean + } + type None[T] = Option[T] + val none: () => Option[Nothing] = () => + new { + def isEmpty = true + } + val mkNone0: [T] => () => Option[Nothing] = [T] => + () => + new { + def isEmpty = true + } + val mkNone: [T] => () => Option[T] = [T] => + () => + new { + def isEmpty = true + } +} From 01e7c73e2ebc7c451faf13366b5978aa260beb28 Mon Sep 17 00:00:00 2001 From: som-snytt Date: Thu, 11 May 2023 23:51:57 -0700 Subject: [PATCH 569/657] Test require indent after colon at EOL in REPL (#17452) Follow-up https://github.com/lampepfl/dotty/pull/16466 with test from https://github.com/lampepfl/dotty/pull/14092 which was sadly ignored. Co-authored-by: Gagandeep kalra --- compiler/test/dotty/tools/repl/ReplCompilerTests.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index bcb08cd232d7..ecdfeb512e1b 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -347,6 +347,12 @@ class ReplCompilerTests extends ReplTest: assertEquals("java.lang.AssertionError: assertion failed", all.head) } + @Test def `i13097 expect lambda after colon` = contextually: + assert(ParseResult.isIncomplete("val x = List(42).foreach:")) + + @Test def `i13097 expect template after colon` = contextually: + assert(ParseResult.isIncomplete("class C:")) + object ReplCompilerTests: private val pattern = Pattern.compile("\\r[\\n]?|\\n"); From e9272d5d3a61d87cfe7abe5280db03bae13b41bd Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 10:04:35 +0200 Subject: [PATCH 570/657] docs: remove legacy information from core data structures (#17464) This PR is taken from the conversation in https://github.com/lampepfl/dotty/issues/4015. > I had a look at it, I think we should just drop core-data-structures. > There's nothing that's still valuable to know today. So I dropped most of this, but I also kept a bit of background information that I found interesting and moved it into the dotc-scalac page where we have some more info about symbols, denotations, and differences between dotc/scalac. I thought it fit in nicely there, but let me know if you'd think it should go somewhere else. closes #4015 ~Based on the comment in https://github.com/lampepfl/dotty/pull/16776#issuecomment-1542404541, I made this against language reference stable. I'm assuming this is correct?~ --- docs/_docs/internals/core-data-structures.md | 117 ------------------- docs/_docs/internals/dotc-scalac.md | 60 ++++++++-- docs/sidebar.yml | 1 - 3 files changed, 50 insertions(+), 128 deletions(-) delete mode 100644 docs/_docs/internals/core-data-structures.md diff --git a/docs/_docs/internals/core-data-structures.md b/docs/_docs/internals/core-data-structures.md deleted file mode 100644 index d42a24f0e426..000000000000 --- a/docs/_docs/internals/core-data-structures.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -layout: doc-page -title: Core Data Structures ---- - -(The following is work in progress) - -## Symbols and SymDenotations - - - why symbols are not enough: their contents change all the time - - they change themselvesSo a `Symbol` - - reference: string + sig - - -Dotc is different from most other compilers in that it is centered around the idea of -maintaining views of various artifacts associated with code. These views are indexed -by tne - -A symbol refers to a definition in a source program. Traditionally, - compilers store context-dependent data in a _symbol table_. The - symbol then is the central reference to address context-dependent - data. But for `scalac`'s requirements it turns out that symbols are - both too little and too much for this task. - -Too little: The attributes of a symbol depend on the phase. Examples: -Types are gradually simplified by several phases. Owners are changed -in phases `LambdaLift` (when methods are lifted out to an enclosing -class) and Flatten (when all classes are moved to top level). Names -are changed when private members need to be accessed from outside -their class (for instance from a nested class or a class implementing -a trait). So a functional compiler, a `Symbol` by itself met mean -much. Instead we are more interested in the attributes of a symbol at -a given phase. - -`scalac` has a concept for "attributes of a symbol at - -Too much: If a symbol is used to refer to a definition in another -compilation unit, we get problems for incremental recompilation. The -unit containing the symbol might be changed and recompiled, which -might mean that the definition referred to by the symbol is deleted or -changed. This leads to the problem of stale symbols that refer to -definitions that no longer exist in this form. Scala 2 compiler tried to -address this problem by _rebinding_ symbols appearing in certain cross -module references, but it turned out to be too difficult to do this -reliably for all kinds of references. Scala 3 compiler attacks the problem at -the root instead. The fundamental problem is that symbols are too -specific to serve as a cross-module reference in a system with -incremental compilation. They refer to a particular definition, but -that definition may not persist unchanged after an edit. - -`scalac` uses instead a different approach: A cross module reference is -always type, either a `TermRef` or ` TypeRef`. A reference type contains -a prefix type and a name. The definition the type refers to is established -dynamically based on these fields. - - -a system where sources can be recompiled at any instance, - - the concept of a `Denotation`. - - Since definitions are transformed by phases, - - -The [Dotty project](https://github.com/lampepfl/dotty) -is a platform to develop new technology for Scala -tooling and to try out concepts of future Scala language versions. -Its compiler is a new design intended to reflect the -lessons we learned from work with the Scala compiler. A clean redesign -today will let us iterate faster with new ideas in the future. - -Today we reached an important milestone: The Dotty compiler can -compile itself, and the compiled compiler can act as a drop-in for the -original one. This is what one calls a *bootstrap*. - -## Why is this important? - -The main reason is that this gives us a some validation of the -*trustworthiness* of the compiler itself. Compilers are complex beasts, -and many things can go wrong. By far the worst things that can go -wrong are bugs where incorrect code is produced. It's not fun debugging code that looks perfectly -fine, yet gets translated to something subtly wrong by the compiler. - -Having the compiler compile itself is a good test to demonstrate that -the generated code has reached a certain level of quality. Not only is -a compiler a large program (44k lines in the case of dotty), it is -also one that exercises a large part of the language in quite -intricate ways. Moreover, bugs in the code of a compiler don't tend to -go unnoticed, precisely because every part of a compiler feeds into -other parts and all together are necessary to produce a correct -translation. - -## Are We Done Yet? - -Far from it! The compiler is still very rough. A lot more work is -needed to - - - make it more robust, in particular when analyzing incorrect programs, - - improve error messages and warnings, - - improve the efficiency of some of the generated code, - - embed it in external tools such as sbt, REPL, IDEs, - - remove restrictions on what Scala code can be compiled, - - help in migrating Scala code that will have to be changed. - -## What Are the Next Steps? - -Over the coming weeks and months, we plan to work on the following topics: - - - Make snapshot releases. - - Get the Scala standard library to compile. - - Work on SBT integration of the compiler. - - Work on IDE support. - - Investigate the best way to obtaining a REPL. - - Work on the build infrastructure. - -If you want to get your hands dirty with any of this, now is a good moment to get involved! -To get started: . - diff --git a/docs/_docs/internals/dotc-scalac.md b/docs/_docs/internals/dotc-scalac.md index 3f88502934b7..03baad375eb1 100644 --- a/docs/_docs/internals/dotc-scalac.md +++ b/docs/_docs/internals/dotc-scalac.md @@ -6,7 +6,50 @@ title: "Differences between Scalac and Dotty" Overview explanation how symbols, named types and denotations hang together: [Denotations1] -## Denotation ## +## Some background + +Dotc is different from most other compilers in that it is centered around the +idea of maintaining views of various artifacts associated with code. These views +are indexed by tne. + +A symbol refers to a definition in a source program. Traditionally, compilers +store context-dependent data in a _symbol table_. The symbol then is the central +reference to address context-dependent data. But for `scalac`'s requirements it +turns out that symbols are both too little and too much for this task. + +### Too little + +The attributes of a symbol depend on the phase. Examples: Types are +gradually simplified by several phases. Owners are changed in phases +`LambdaLift` (when methods are lifted out to an enclosing class) and Flatten +(when all classes are moved to top level). Names are changed when private +members need to be accessed from outside their class (for instance from a nested +class or a class implementing a trait). So a functional compiler, a `Symbol` by +itself met mean much. Instead we are more interested in the attributes of a +symbol at a given phase. + +### Too much + +If a symbol is used to refer to a definition in another compilation unit, we get +problems for incremental recompilation. The unit containing the symbol might be +changed and recompiled, which might mean that the definition referred to by the +symbol is deleted or changed. This leads to the problem of stale symbols that +refer to definitions that no longer exist in this form. Scala 2 compiler tried +to address this problem by _rebinding_ symbols appearing in certain cross module +references, but it turned out to be too difficult to do this reliably for all +kinds of references. Scala 3 compiler attacks the problem at the root instead. +The fundamental problem is that symbols are too specific to serve as a +cross-module reference in a system with incremental compilation. They refer to a +particular definition, but that definition may not persist unchanged after an +edit. + +`scalac` uses instead a different approach: A cross module reference is always +type, either a `TermRef` or ` TypeRef`. A reference type contains a prefix type +and a name. The definition the type refers to is established dynamically based +on these fields. + +## Denotation + Comment with a few details: [Denotations2] A `Denotation` is the result of a name lookup during a given period @@ -21,7 +64,7 @@ A `Denotation` is the result of a name lookup during a given period Denotations of methods have a signature ([Signature1]), which uniquely identifies overloaded methods. -### Denotation vs. SymDenotation ### +### Denotation vs. SymDenotation A `SymDenotation` is an extended denotation that has symbol-specific properties (that may change over phases) * `flags` @@ -31,7 +74,7 @@ A `SymDenotation` is an extended denotation that has symbol-specific properties `SymDenotation` implements lazy types (similar to scalac). The type completer assigns the denotation's `info`. -### Implicit Conversion ### +### Implicit Conversion There is an implicit conversion: ```scala core.Symbols.toDenot(sym: Symbol)(implicit ctx: Context): SymDenotation @@ -42,7 +85,7 @@ implicit conversion does **not** need to be imported, it is part of the implicit scope of the type `Symbol` (check the Scala spec). However, it can only be applied if an implicit `Context` is in scope. -## Symbol ## +## Symbol * `Symbol` instances have a `SymDenotation` * Most symbol properties in the Scala 2 compiler are now in the denotation (in the Scala 3 compiler). @@ -57,7 +100,7 @@ if (sym is Flags.PackageClass) // Scala 3 (*) `(*)` Symbols are implicitly converted to their denotation, see above. Each `SymDenotation` has flags that can be queried using the `is` method. -## Flags ## +## Flags * Flags are instances of the value class `FlagSet`, which encapsulates a `Long` * Each flag is either valid for types, terms, or both @@ -74,7 +117,7 @@ if (sym is Flags.PackageClass) // Scala 3 (*) `ModuleVal` / `ModuleClass` for either of the two. * `flags.is(Method | Param)`: true if `flags` has either of the two -## Tree ## +## Tree * Trees don't have symbols - `tree.symbol` is `tree.denot.symbol` - `tree.denot` is `tree.tpe.denot` where the `tpe` is a `NamdedType` (see @@ -86,13 +129,10 @@ if (sym is Flags.PackageClass) // Scala 3 (*) obtained from the symbol that the type refers to. This symbol is searched using `prefix.member(name)`. - -## Type ## +## Type * `MethodType(paramSyms, resultType)` from scalac => `mt @ MethodType(paramNames, paramTypes)`. Result type is `mt.resultType` -`@todo` - [Denotations1]: https://github.com/lampepfl/dotty/blob/a527f3b1e49c0d48148ccfb2eb52e3302fc4a349/compiler/src/dotty/tools/dotc/core/Denotations.scala#L27-L72 [Denotations2]: https://github.com/lampepfl/dotty/blob/a527f3b1e49c0d48148ccfb2eb52e3302fc4a349/compiler/src/dotty/tools/dotc/core/Denotations.scala#L77-L103 [Signature1]: https://github.com/lampepfl/dotty/blob/a527f3b1e49c0d48148ccfb2eb52e3302fc4a349/compiler/src/dotty/tools/dotc/core/Signature.scala#L9-L33 diff --git a/docs/sidebar.yml b/docs/sidebar.yml index 1e791472bceb..d640f15ffa6f 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -189,7 +189,6 @@ subsection: subsection: - page: internals/backend.md - page: internals/classpaths.md - - page: internals/core-data-structures.md - page: internals/contexts.md - page: internals/dotc-scalac.md - page: internals/higher-kinded-v2.md From 8cc9ba3cc0f047f0e112a01f0407d46ea21afafd Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 12 May 2023 10:03:47 +0200 Subject: [PATCH 571/657] Only set needsInlining if not transforming the inlined call. --- compiler/src/dotty/tools/dotc/core/Mode.scala | 3 +++ .../dotty/tools/dotc/transform/PostTyper.scala | 15 ++++++++------- tests/neg/i17168.scala | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 40a45b9f4678..ea63eb6a419b 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -141,4 +141,7 @@ object Mode { * Type `Null` becomes a subtype of non-primitive value types in TypeComparer. */ val RelaxedOverriding: Mode = newMode(30, "RelaxedOverriding") + + /** We are checking the original call of an Inlined node */ + val InlinedCall: Mode = newMode(31, "InlinedCall") } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 597b5aa5885f..09f0fa49c527 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -283,16 +283,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase if tree.isType then checkNotPackage(tree) else - if tree.symbol.is(Inline) && !Inlines.inInlineMethod then - ctx.compilationUnit.needsInlining = true checkNoConstructorProxy(tree) + registerNeedsInlining(tree) tree.tpe match { case tpe: ThisType => This(tpe.cls).withSpan(tree.span) case _ => tree } case tree @ Select(qual, name) => - if tree.symbol.is(Inline) then - ctx.compilationUnit.needsInlining = true + registerNeedsInlining(tree) if name.isTypeName then Checking.checkRealizable(qual.tpe, qual.srcPos) withMode(Mode.Type)(super.transform(checkNotPackage(tree))) @@ -344,8 +342,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case tree: TypeApply => if tree.symbol.isQuote then ctx.compilationUnit.needsStaging = true - if tree.symbol.is(Inline) then - ctx.compilationUnit.needsInlining = true + registerNeedsInlining(tree) val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree) for arg <- args do checkInferredWellFormed(arg) @@ -363,7 +360,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case Inlined(call, bindings, expansion) if !call.isEmpty => val pos = call.sourcePos CrossVersionChecks.checkExperimentalRef(call.symbol, pos) - super.transform(call) + withMode(Mode.InlinedCall)(transform(call)) val callTrace = Inlines.inlineCallTrace(call.symbol, pos)(using ctx.withSource(pos.source)) cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(using inlineContext(call))) case templ: Template => @@ -505,6 +502,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase private def normalizeErasedRhs(rhs: Tree, sym: Symbol)(using Context) = if (sym.isEffectivelyErased) dropInlines.transform(rhs) else rhs + private def registerNeedsInlining(tree: Tree)(using Context): Unit = + if tree.symbol.is(Inline) && !Inlines.inInlineMethod && !ctx.mode.is(Mode.InlinedCall) then + ctx.compilationUnit.needsInlining = true + /** Check if the definition has macro annotation and sets `compilationUnit.hasMacroAnnotations` if needed. */ private def registerIfHasMacroAnnotations(tree: DefTree)(using Context) = if !Inlines.inInlineMethod && MacroAnnotations.hasMacroAnnotation(tree.symbol) then diff --git a/tests/neg/i17168.scala b/tests/neg/i17168.scala index 6e82cc15abfc..c31889c979b7 100644 --- a/tests/neg/i17168.scala +++ b/tests/neg/i17168.scala @@ -1,3 +1,3 @@ type F[X <: String] = X -val a = summon[F[Int] =:= Int] // error \ No newline at end of file +val a = summon[F[Int] =:= Int] // error From c27f8705b5f65410de923a2ceb95768588bd6c77 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 May 2023 09:15:00 +0200 Subject: [PATCH 572/657] Avoid retraversing parts of the tree that do not contain Quote trees --- compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 5abc92681295..e7a628f68663 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -95,9 +95,8 @@ class PickleQuotes extends MacroTransform { case Apply(Select(quote: Quote, nme.apply), List(quotes)) => val (contents, quote1) = makeHoles(quote) val quote2 = encodeTypeArgs(quote1) - val contents1 = contents ::: quote.tags - val pickled = PickleQuotes.pickle(quote2, quotes, contents1) - transform(pickled) // pickle quotes that are in the contents + val contents1 = contents.map(transform(_)) ::: quote.tags + PickleQuotes.pickle(quote2, quotes, contents1) case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod => tree case _ => From 75b01265713e6bd9f7869bc3323a3430266a3b1d Mon Sep 17 00:00:00 2001 From: Lucas Leblanc <44496264+Dedelweiss@users.noreply.github.com> Date: Fri, 12 May 2023 10:31:55 +0200 Subject: [PATCH 573/657] Refactor: Sync Contributing section from Scala-lang Guide to Dotty website (#17459) --- .../images/contribution/breakpoint.jpg | Bin 0 -> 137341 bytes .../images/contribution/call-stack.jpg | Bin 0 -> 305288 bytes .../contribution/conditional-breakpoint.jpg | Bin 0 -> 146193 bytes .../images/contribution/create-config.jpg | Bin 0 -> 140865 bytes .../images/contribution/debug-console.jpg | Bin 0 -> 157192 bytes .../images/contribution/import-build.jpg | Bin 0 -> 64305 bytes .../contribution/launch-config-file.jpg | Bin 0 -> 61379 bytes .../images/contribution/start-debugger.jpg | Bin 0 -> 102513 bytes docs/_assets/images/contribution/toolbar.jpg | Bin 0 -> 111169 bytes .../contributing/architecture/context.md | 53 +++++ docs/_docs/contributing/architecture/index.md | 14 ++ .../contributing/architecture/lifecycle.md | 90 ++++++++ .../_docs/contributing/architecture/phases.md | 108 +++++++++ .../contributing/architecture/symbols.md | 70 ++++++ docs/_docs/contributing/architecture/time.md | 68 ++++++ docs/_docs/contributing/architecture/types.md | 147 ++++++++++++ .../contributing/contribute-knowledge.md | 12 - docs/_docs/contributing/getting-started.md | 68 ++++-- docs/_docs/contributing/index.md | 45 ++++ docs/_docs/contributing/issues/areas.md | 70 ++++++ docs/_docs/contributing/issues/cause.md | 115 ++++++++++ docs/_docs/contributing/issues/checklist.md | 135 +++++++++++ docs/_docs/contributing/issues/debugging.md | 189 ++++++++++++++++ docs/_docs/contributing/issues/efficiency.md | 24 ++ docs/_docs/contributing/issues/index.md | 17 ++ docs/_docs/contributing/issues/inspection.md | 181 +++++++++++++++ .../other-debugging.md} | 20 +- docs/_docs/contributing/issues/reproduce.md | 127 +++++++++++ docs/_docs/contributing/issues/testing.md | 212 ++++++++++++++++++ docs/_docs/contributing/procedures/index.md | 4 + docs/_docs/contributing/procedures/vulpix.md | 1 - docs/_docs/contributing/testing.md | 207 ----------------- docs/_docs/contributing/tools/index.md | 5 + docs/_docs/contributing/tools/scalafix.md | 2 - docs/_docs/contributing/workflow.md | 19 ++ docs/sidebar.yml | 26 ++- 36 files changed, 1770 insertions(+), 259 deletions(-) create mode 100644 docs/_assets/images/contribution/breakpoint.jpg create mode 100644 docs/_assets/images/contribution/call-stack.jpg create mode 100644 docs/_assets/images/contribution/conditional-breakpoint.jpg create mode 100644 docs/_assets/images/contribution/create-config.jpg create mode 100644 docs/_assets/images/contribution/debug-console.jpg create mode 100644 docs/_assets/images/contribution/import-build.jpg create mode 100644 docs/_assets/images/contribution/launch-config-file.jpg create mode 100644 docs/_assets/images/contribution/start-debugger.jpg create mode 100644 docs/_assets/images/contribution/toolbar.jpg create mode 100644 docs/_docs/contributing/architecture/context.md create mode 100644 docs/_docs/contributing/architecture/index.md create mode 100644 docs/_docs/contributing/architecture/lifecycle.md create mode 100644 docs/_docs/contributing/architecture/phases.md create mode 100644 docs/_docs/contributing/architecture/symbols.md create mode 100644 docs/_docs/contributing/architecture/time.md create mode 100644 docs/_docs/contributing/architecture/types.md delete mode 100644 docs/_docs/contributing/contribute-knowledge.md create mode 100644 docs/_docs/contributing/issues/areas.md create mode 100644 docs/_docs/contributing/issues/cause.md create mode 100644 docs/_docs/contributing/issues/checklist.md create mode 100644 docs/_docs/contributing/issues/debugging.md create mode 100644 docs/_docs/contributing/issues/efficiency.md create mode 100644 docs/_docs/contributing/issues/index.md create mode 100644 docs/_docs/contributing/issues/inspection.md rename docs/_docs/contributing/{debugging.md => issues/other-debugging.md} (94%) create mode 100644 docs/_docs/contributing/issues/reproduce.md create mode 100644 docs/_docs/contributing/issues/testing.md delete mode 100644 docs/_docs/contributing/testing.md diff --git a/docs/_assets/images/contribution/breakpoint.jpg b/docs/_assets/images/contribution/breakpoint.jpg new file mode 100644 index 0000000000000000000000000000000000000000..748088c269c98ddff0acfc7968b4f6b1331f83cd GIT binary patch literal 137341 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 z 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Ǫp4{+(|HHM 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 ViKq^~LTvC}{fBjbN&nBv{{SdTgS`L% literal 0 HcmV?d00001 diff --git a/docs/_assets/images/contribution/call-stack.jpg b/docs/_assets/images/contribution/call-stack.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8fac2371a6c18c490a680e5463722e442d2bc2e6 GIT binary patch literal 305288 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�Gz9SFe;)e9w2pMUgV!kIGL9z>L@RI!4-S07gHemr}Q zzFPMBY#?>%II`sIUvpjL4uoM>v$AdgFVk^MN1PW}UJ>&5JLlwnqgo2tyMkeavK7c) q2d(d}D=i@q+E12kcj##0Ke8?n`jLMUSb$bAeiG%U7PaV~#s2~5j>Odf literal 0 HcmV?d00001 diff --git a/docs/_assets/images/contribution/conditional-breakpoint.jpg b/docs/_assets/images/contribution/conditional-breakpoint.jpg new file mode 100644 index 0000000000000000000000000000000000000000..11bab89d3f477d75aeecc14aa2bb88822edb763b GIT binary patch literal 146193 zcmdqJbzB|IvMxNhJ0!RVcXvo|g1bX-cXtR8+%;Ix;DO*ykcGPhC%6Q6zl-G6z2AMl zbIw2C{oP(`rk|;*uIcKjnU<-w9=<&+1L!gm(h>j!1Oy-j{s0f(pEZelSegNVv@{(6 z4*&oxfDZu!K!A}tcq8}?>w_^31k_JDBp999$4!17mD(?gHK{-$DPBc@M^^ z;0=6=pU1!FlG1XDq-;#AEKF=H;F2tC?0hWjd~6(~tXzB?oP4Z2U}=b~U*~zO!(#&1 z3gVa6M^zBN;UX{x5?mvwM-ea`>bKg#f)8Q;f^mMsk8%)@zx2R@${5(Fd^Gz=^pJOUyTSfCpHv8A9Op`l=4prOIi zzF8zOuTuzOlKry>oPY za(Z@tad~z9XxF2izo%b@{fk`~V7nlpp`oDR9_@mFbO&!x7|<|etgx8RmEc}EVv(~2 z!ehUP`B>48K*6qjh-2h5iui<*GMh}EMkKmB+8*Z7yyu|{P=C-;}8tR3&yp-rtYsS+p zjy?M#&w@5kaLUo%?(cJcXlA1Q$gX3@&>39<0Ypn}4>>uoUYgDOAx}`oOq#z&ppI7< zaPm`hfQ*&U^png3oU7FDE`se#N~oH-pX?!<<06ZaWP#3pNGghp=UPC-XSa;(clX(yA2deZt%r+MM(+uNcJ z5Ar*g^Tr#!93}@G-DA(5HP<1y0zs=c)9>s`IW;*pQ<;TS8Dqaa?{}xT%bbcLJ%PyX z)K=u=7hWiH(go(R0yDF2x0*+@ssl~rS%jcKq}t*_vSj89ENL-%lOejKVEZkqq_HAXl$|=! zY(kwSg*hq9m6RHpf}k{|A5L79vxMLvU>}91ef7@mi~0J#A^fWh0$^1TO}|=?LsNUh z0yWP{pfXE}$c;-vx|gCr9%8eMo`QHI+&dK*Z5%hMsd)RHxpc!EsezK$loIROQR_7M zg2Q-z1aegRmvTnzecKLnw}&5 zL?ubhssBd%iUKk``TGriUv95+YUOGOPpHhEj{W5`+vneYh zMRfS8J!@7M845ZeE&wqtmT$t}E^PoVg*S<3$K5P?N4WxeURG)d!MWRlQbtvjqKKxS zwT<`;H_n+Z*lF-WL|+AYg!dZLoi%#xV%g`UtMk*_emim4NpXOKue zh)zOIocJRHn`a0^(clSI+=;KR_xK0p^wkZ^P$vE~C=q^x7Yd)}f2iLGB>U`LDe!>o zJ5^AscEdYJha3|GR^z1aavZcI(;XA=pd6Qth5J$)f@cuF$i+GV##4bch-GPTP|NDd82W|F@L`yk2-#wftJTwji;3`>lua|{gN>J z>~>pdfr6;TrgM|0cUOgAZgCzVBE4;RpOObStlu5KQnkm93DDVpgi=nE4CI(| z;_Y2NSvum7KPfJfkF%Rsq=&i)Y$ws3gdjMq$9Yw?n_J5+^mGlg2X{8pXlIWBqfSc=|47&}&d)L8*;u4dC%#FsMPX2<%DpoGdFy5%?D4lTSjv2C2a_EVPmTPN^jVLzS8!Ekk0jgPMn8 z7v!UET_}yG^NA8aO}4$Qtxe%BF|JjFL2F@QHX#)!imOkPc9{279B9IQ`*I?uGwDsQ zhq0+}M-SZD10WpG?LKRAyZ}2=#s59 z`mw!*^xrg^FJ%;zpOjqFKq>ckKIL*^_oTUs?hSHabK8u{UY$^N_gY_A|6Ci()A{PP zPgam)MuEWDeY3StC{Xbv#}TLVO8|0g5N^z_y32yysysX<%TBl^t$>q;aQ5mP)t66s zuj;vCy(!e1Bt6=nza^xH!-aMp*7w}K^Ewd|qG$}nP+x?qGQ=#HTl(g$u7=$yhjTiT zTtfrN(uSrtPk1MZV}@e4votV+k3lZ+j(3@~ts9LTLykR0pj}M@~i6;ttv&BkTZnMSaZ4}Q^Ud9375hLpl1Kg z*gSl;I;J$$2r9R*%Cz9S^|K;uMVPsCqm3N|GLI4Zn$@b9*PvE;KL3CYib*Dlge9ZT zZW1&zbH_2Ytu;cyliJ`$8wNL;1N_u8CpNYLPdMtUw4=zNtW3>@ItwInG}3j*aO7pc z@luVB2J#1f*FtmJR2FQcV%t7-#DW6^XoGG*pQ15zAqRsZQ6#uk5Hb)nszAinU1?FM zBwf=G(c6sYw5ie1kS4oR)Q$%`+6J9=b{!+QgP->D60zu*R0$m?GK4!-ZLK-xzN?afpt*6cCqG!O1TFMJeRhFlhFUUzW{2p4&Y_$j9)jQ|S)a~QNIM$chEZ*C_Mon?0 z3@Xs)2a%-Hz~m@(7(%6yWRa(v3w%9a@SENUX}SL5(B%C9WYBq+vMsLsXw;KLV$U-C zQN$HJW2a!7Q|py1uRCAKcT~r~-XS25$JsY#S#_Zy$9aVdjV?t2tu|SEqR*+&@?F(@ zB7iO@ppva{9)grado)C?ub76SZ}^@!b+kDO`DFz0OHI>6t!TPU3n2EJZ8N{x2Vvv| zgATLvlQzGxdWZ|ArA%{O!`nGY!rP8%jzM9WcS>yZx{Qrw3-!8h`vGZQ+-~P?tC>;a zkX8F)p1vm#b3=-q+}+7jD4nvN{DSK%0l3tqRNb%!;SUA`SKBea`#F zd{T%KnX1?~CQfGOKOMBTIYJnBsY*BcV`f|6H5Sgz%-0Y1r%BsR`FY>_7nn%6zJE0B z$XuwfZ}^Vtz_b3&pg^Cd>hNrGvbwZA5%*)g8T^m}zrx_$XXtyJ0+{sROv{y7R-5mX zY|(%)H;~cl5=n=4ik-S(S`*5Ve$J3fCXb5HBM4VYJlUMxbr&N+ztknV?#9_f=1bI6 zi`f%~ZOLwKZE|IJPHIePOr7sI9Xcq-y5{;`sJDSnrqM0^hBd=;xQtZ~d35UmFlA@# zMD2<6slj;t#t1Sh?bz$u@KAasaR?;WB8Hv}4LT^!wiRtPBnafCjEUqOCfZVacAMMJ z__g&N4$g7X72(-Nn5WstbhUO=g+zB-L=nns}s;YNeNSj*G3({ei^tV;4E2F zb7XuEdi^p9N3NA?G>|CLtl_PeK<(?-@jTQH`p!YTLYZ$gZDG$0EtARLL@XqPK-==X z8ZYxG9Na9tL@O=%HDKj4R^}O6p;XKz!amZ5^ zC1?L^Uz)hhJcBNxJIWHevb&+QsU(J@_zSKNAhx%=;G1~FEB}=6Rs)ausGU#av7Ey+GcJHgnf90dAbqxOw z!dq>%nd5^V7KrKKkgLGEcmAB966DKwGv%m!ETqZWz0$eKQHuG7dQdd7qcG>i#s|OM z3V~~rsp&7;{gf{$X`3UZ;Q}0Zb_Z1i{So5~Pw?E!fFWOtRAGj@b?QiAL{W{J{1Uh0 z-l`(5DIR1=;&iJCCrVb&r!cZM^~ika{=EgFj_@_%PeV*p=aSIU4i;_&i{9LX=o^I( zq>!;muL`&preP;UHW9PsW0qGJY>m$pH26v6r{{^Y_j2YxVm2TZX^(f*kF5BBKI$cVpUtuUbYOpGG<@K`LRvAH0-oBNsl1VW`uW5hAkrBVEszt-vkelIRM1YH zd)XlUyn!Js@XcJ+XNoJMe1oSuyW~DD|1}}Eym4$-{ zkD%aX@x`H@+>>u}gCdQp?8iAHZY32$A1z|K*`Ew4{(xvXw5uCpgLnYWQitL#n~jV4 zowcuWNvNofqQt7A`exZ^8OzCNp4B8Ezq5jU9|0P19F!wYDw5eLeB~g`vw;bx>*N^N zLvdJG#yO~cV4mqf`Au4JG#^%qn$Roo^wvt+N4!J^i<>~E!bufXhj)k%^>tt%NOo=d z#fFgY@hP2jffXx5DPwL-iHufQ{%C;6s-P!Do+M7*X%WDOxhPJ;%`~1+`FFd)sq3l*D9Hw&|%zF?eLtNSF|Bd2Nsloh4g@U2+8T&OWO+ zT5{+_!lfYSX3C4p%-ZFDM5xHzQ0_pFbwEY4<1I9ws@5s?Sgxx(F^X`@?g{-yqAWZt zl%F z&LwuCU3UMg39{>&qe2FMI`2mz3oXhQATyq;6vvq9{n5J||LW<%?-zDg8fmc#_R4-K zJiCem2OR3#Ux;N**)v+%x%`g~#u5r9$}GAT(Ud#+1_#V9f@NA(;ZBNo&Ks&sU@(SY zodaX;$t$zR%kn8~#Aw&p9q83(#~9hTGAy~J!wvn(c6F<y~s;zI%&#t(A>n^R`RsnzMj_JtSRW5RGnD= zaZj+lJtEM3e*1>_Q8J3LSy)@3=Jd|uBK&UTgkbI%+cRSC3h^iDcy9bGXA#ZiAH`oU zps_Z$l1U~nE=A|`+bc}{R3*54Av$1h-=hek7MgfG^%zTs;h6glr`_95#ik$XEC^5n zpGk&_hwt<56THS&=p6?7w0dz*0YdN~e_f5~948(WogTOYLl$qVP1z7pK#&c$fjcCw z9r+T+gdr=SN2+}da7SBLvo(pN|Fo!-JhA?^ZH73R(~^k+y%<;Pvr^3us4QN*;W|NH zkQIhr!Wp$f|&7t{3`6d3_jg?MOnxM%p~sUX|%s~+R~-lGmYBu4Xm=&l&4-9byqLZ`H7vwjWe5HSP>J(9&6cE)rZ@-)4cz& zvlbK322oYUy5(1$^u^k2Xld`$@Z~oT;e^a(5f`cCbtZYCfH5MtvCW6a*4!O0V>%T) zs!m8N44Ru#itg~BgBx&g3*z?^w5++{LzEKl3O^>@>XC_cC(iK%^+(=4-<>Ms*q8=g zVlZ?t?FQczkO_a*x%Y#+J9z-6vn&`Fm;IMW?z|$eZHZQdjcQW4JMQi!mtg$EZtCI6 z&oH`#GDt&AL;ByJw@?s?eW*nJKIw6W&F2d4lgL(&C(9>E?qMH*o=Ogk#ouZ;mQFQ9 zcC(w!KE8o;yTj_3_f%Kp>$C3(NgJ*w5X);PwG_4T)ow_`$OtPDKg`i<)U^mP9h>1v z;a1m?dCm?*3j_sMa#9Q#Jzin@ImOW@yS$gs7Wcoz5blfGV-%X-fM;7Q3h=8+y6~cy zeUZpj*}-8mG%y-OX;a3Y$fFAd;<;FxFguY>Jk~O+^k=Na(Y)7xNr(&;0J-eABxvc4 zeNXjmJ#!-O{y+lbL}~g6kE@!0O{^IAY{yzi+9c&d>c`-YoI$Lu@aFN#-ON%Ao6xHZ zx3#@T^$Q8B0?3ZxZl$Xd7_*?vkO4={>Op)Xz*+@+)apskzH^vzSqqae%%;r zPDv8C^39R5*_cwhn?Zakz)cv}7&CpkJ5xEoy@!iFmwXtB_<3GlWbVZ4^%7LzL8v;R zxw|!o*{H@79ahc_rC@pZcWXmS$rx$1gpho3!(J7P-HBrPJd{hMKA34tnmBC>b1`>l zG7PA;{F?pQ)%nez898k2Vhd9D2*{zYUNZ6G3ZUE)uc@rnjmjT<(GigBkH%sitcxde z>}{Waeh$)R5HcU`x6c&C4^3YWm;qtV!K7#=e(>afm(Pb76!Toh>mED4p!~ZC0Y*DV z4r}K=dsKq-=Ge!Eh}DqSIkfcAxW?qwHm>!jH!9GqV$UgK+hV0xuhr!2wwdk9n6TWY zp$Ohl1kgYc&nm%mI#njZJ692!l_FhA2#>yRS2?aDf*@JAxTCAijlh|AB4@8md!wIU zF79=cWSSek#02{z{LQO2(8twrqAe)a_Y6|F(5I)xVRVpL89o(rcd7YTi!*v)_o6e4 z2t;*^c>Mr8&mDt2&n(iJ!GoT1TmVrG_I)F94eXXlgM=XTn(Xm;o}X7`$^6dtSu$FN z-kdw(S{g*)y|<$#IW2+%g_EM`VZr+KwTet-fD|c9PtOjYN_B9Ax14_8;_wrxvcHsCnUOKVUP`k#+wWxu7pp9hU+}`GFp}$HCaKFWAG~y?t(ZH z(zmqwc*YK~Vq0s=^t5ng!pRheS!NHw`F+CiX2%F2R^}pFvK`8B{|`x`kZ!ux^U0Wo z6VxdCwYLnPgMeNmDQY{ZS;xpuNI`l+iA^_*$kD{rtcx7u*c@xqmIESB5H^Hs`O1yn z{E;>YKA~0;7a~E(;amF-59k5V37x)Q8V!Boi^}_qL37jUbs{x%;0Ur4Kz(&WX}Ek= z;Avm$);$+71F`|n{f^|9mz?!4G`=*y7bOasq1vSEczZKYkPjzPw|j+3mpMxsNgaY5 zTh^_p_u`C#(BzOsVX6o|*OUR{J(MG<3tqQhN-p960uY@;4VAU6kn&BMegQ#T@Kr*B zma$XG9QGR?#x21sl61P%m-H zj{}a4!Fq$$&Q)HS&t<_hptr3cIT!Ovf(z4}0De;_ISW=v9{vYzuo1fHdlDCO!h5QO zl~~rKhB*8 z{HKsz-5>p3kwn9b*D&xioZOwYwe_2ew4@7W-fNpnh}VSo`EPz)g(GFXiW5M`-4rm! zVK%KXLgz52AYJ{$OC(M*%kEo7UkVxux$U$;C2%{C_bg7xZ@$w?mFK7Mzwz#5e@sCw9monE6 z#f{F;!^uN_ibZ_U-e`)#bS1S;w88KE=Lx$JplOGE=?KYuk#wgfChMnNX*-!6?eFsz zR&9ilb$NR!Zvi}d$ ze_|JyRJiQE30o9$u%}MoY+YMICuPH{#qA0Q{r2>%fcyQy?8xj@mgvdtM|5KkvI-C$ zbX0dvre0J1@$KfqdaMdfD=h;7MI@@Efjpn;!Wm&!YIR6=!KZ!(6IJoOIm2_(xan4bi7}6UmDu?<<7&;@*8tKJ z$d5a40&chO@=Sa~6|Oe?4=NpoCO6&OweaP-z8!x% z6L2A-8}kU-A))V)TA{5&ub*u@2Nn1^_i`%yQ1%~Ye*k<#g}SJ3PlPYNeX5g5Gi_kd z+jZxz@N~|VtpgqTc@7)+UE&S0o=KXSYsRyx5KB`dItHHM?j9)pdnG1Pzd446d#ZQE zlr4l1^@z5*J%`INrYOH5&q-2^IiOAx3zi9eqnr5MZI!yd@Aa^D*uT}G$T?2kMt?{! zOxgNkj6Kn`BmZl?jd82rt@$(*$99vbFK=|k^9@?{xjGY!a0lOaSJb@0bI^YrDuIhZIu|kF5SwER;sp|Xy6#C~$D?LsF;@_%QHO3A}cue9} z?(t7k;K5xRA8}f87AAynoeyS6Sv&wF2Kv@qp}?q~k!b{0Pc1)y*d;I0h6{elE}gxz zDif33Nr^In5iR+VI3YnC(3`A=D%?@g$Nh?jTQj=vd}mp6{d%rroq^>uGz&v+^z-5J z@nan2P+-K?=zAJFgIMu_O4Xs~K}Onr&?ijISZT^*>D=r9z8=m9xGW$N3-*7}zQ;5NS`%Z7AjjlSbwUdY!24QEC>!xt)8n5n*I!Ef(G8rhGAYXBWAfk+|fPygc0g!ns$tH75Tp}$HH@z#D9l&YD`v6R|j^%y=&nA??d|)U$ zlm_@;Zcuq&DkDu$$a~h`z5w-hJ7x|G%kyn~G7uw|Ky)f;e4`6p77kBIQsqjrKjF1` z>$iZ~aSH<&j*4RgO8>hJ;$a4OSb_X3Eh=iDsH`9%Eh`R=jRMCtW!V_pIzxk_t8DFD zoRlSBkb=W>NZ~fYfl|2OkS#s{>y@#y!*fMN+24bO{yjgRdW?GlfN{o0UH{JiFDWP{ zrq0ITAR|(+l!&o|6F4jk!VrwP++7?VaWWWV8(X|G0pk)dW^e*m5RAV)(v5!MACK7l zCw`1J1F)S`l|{j}VS_QL`CqWnU$C)-lPy?=11v*nVrvK152^VJn>=ElM{H~33a;DF z^k^Znshye%IHv?}gn%R<4afnC;NHy$a0M&@8}PeMMsRKirZ@x2VE&8$P#*thc|~w3 zBXB88z!+RY46p}mfmc7v1CQ4LECWt|wym=nJKIkZ1o8_2fLVNaIC%yDaB%=|7ya;X z^YP)~E*l)zHV*)8c7K+)&jJ9RGcY~+Pa0(=0H6f}KuzbLG@~Q{sC@?jxHAr~oL>Dr z4-|M0X$B72J1zhK6fFS28UX-AonP$+<~{O(oM`}11-F&#AONI*L-?r7!Mb(-gWcfJ zyg%FRzm)l{{eH&MK|w-3e!&SE{Dpykj7oz+goA^HM@B?OMnXhFLP5nuM?u9vMM6S< zf{uZOje~=OjP?}w2{tY!HV*dBSUM6eJYv|8aWw3XY|Nd<7K^1py8t z`#qM90POfbgU%kC;#UIK5E2#w4jhn12)@65MWp@CdyF^x8EE(LTUZx56gt?IZyEj& z`2PgqpZ_j`4_}U@F9I;f1I*YjX+o)Z5&BiI>Fvy5jwyXYO37-BAh)HOl>S-<_G6j@ zYJ1yXN#IN7&FW@tRa=XwB;+mQm3Ht_X0iLu(qJ&3BVRCA8*$cqRYTBW<(qrK$7=VF z28?CfnOp_w!#b(!g^4MA=EaG%`DD!kayjOL9NDhf^!lw)V!3~u0M0BV5n@|*m4P^I z_?HL&!2)D$Gch7l$c~Ly%*fw4W&4WPWtcF}R-d%EW~;iSWF<{#$o6~}OiS+wQDeqP z&qOr*v*;t@`TOC2bp%&LA+XG`Av86eUmucXJ)lxrCTsG9QAf-2gIA!F^)q|IE*2ye ze3u%WEE`!-9kT(5Z7MceEA>EI98AuEpe;tc|E%3F-X7oL7DG5oKO-nvh2hRIXwaPywj}B|=7+;Za%d6M_rz%05 z0uYCPXL1a*K^(6Aq7jjZ`~-iIc@h9%#031LLVZ8|nS*yQMp?fgW1`4U009mt{VhS{ zqlAR4IJ`Pmipf+d7~Wk!^;mXu0DwdcDTu>sq!}-C9eCyepfa?VME+xE095@O^ALa@ z2cj@3PhviL39LnzHfW3XN&mC(Rnd%7Zpl(305dHO0HDR5r9^x)oSNLjN>6z1@IK(j z1$721($j+SzEd-wsx|` ztXU4KIg=sUyAiKNwCb;0SN9D*5wmltI{WcEcwtNG)rlvLCQ<{XRW7+MH3_>g%x|=E zyaF{pp^XR}G#;hw_fLBb?SVdFxZb^RS=;E`4E|ZmbPrwaVSf61CmEHYs*YgSp%>Iu&|D$o*dCVvWAQkV5PdF&J)$ zxXf!76yu=HM=7M0r{7CqQ{IMekC!ybb4-B1r;2iC!vcL$!0S7ezoWp!p1xU~WDp&>0 zLXJfMn0b!NRBy1;LVOwZ8S>i60$?yhCO{wGVc6o8s-9WR3`^PSHq7#mRWlZPRBLu< zPwr->H_&q~M3h+hI(Fu^w6>bP1e{6;)rb?YOcPOd?f zGH*#ct4z}V>2S#D)2Mi}Qp#ygp^|dW$Rz!mT^j;*{Gr^g%Vb;4Vm}OZV^o58(O~k~?>XvL0 z+lg419MC2jp*UW;FO>VF*Ryy3bNN50kjkWXuSak#rb4?Lc7Ch&=$Dx)J8g<- zwnOCH3b79;n$W7I3Fb4L$=TgAJPs5qJXq&2PP2O!NMt$6Al z-_TO=(YO=$`fC!@+$H|7oZiEWsL^`+(hup*)9bT|F>l6+H446rpI&O6jhowW?q#N~ z@APi|IDWUIV@yqAPUPcYS{CWGFXvCvY^yW0qbPgrb!aur98fT^LcCcX>XlbzbnNaa zIyyI0&ga#CrN+Lsn1R-7c_!37IXEoQVxJd@Tr10N9bUuOyHUJlrxAzgby2(Ax3&kG z&Y3dkFPE#Ki0eE!7nF$EUD-Z;MT{>>@2lB6Y?J0iwwe3sob%YJb}K4VnxlYrKlHpt ze@W6fGj3q)R=Xu~(dEQIsUS%~@U}~kuI9(-jO7ZmW_z8S)z*4`Wc|u|OJ>9!3X}d2 zw|SfDT*vIn9pa+UQo@-(PWRp=3xJPD*{5!Ppfu0}@F;IszYb_}d@IhEv=Io_> zec{rC?V07t>Hgwp(_F4kP$}I6he%1^`_mZh*VZL4mck*tzPV$(XeRvCJv7_!zyGKo zY3eQa%Dh`W)B(XFMb+3KBd1+Jl3*poR6)@I2rQ&3iUw7#2OEVaNrQJYyKkVrXedWz{ zbpB&=S0OWx%kQ@?2ynJ})%w9efi8cM(N-YT+k>O_YHM!*E{h#e8O=;+2n5BXZ%r zvphbdh`c{J^V4vE@%6Dq%}ji4AAc%nK(TrLy*6^fWk^Tk>(a}s^H(5r0OH)(>WlX1 zpc%g;cI}*aPk71)(_CM&kPQPRx>v!bo=&>SrCO=#N&~W8o=)$LJd>MJKEh-t*jBIr zW8k$oxyciMt>Z<|cg+Q1vS-{;`UWFS!dvoe7e3P?{>-PUA%n?(m^jIQ4j$o(%q-tw zGTcRrGu|TV+^*|2(GlFPaUZ*Jtb`ipnwsY|AB}D<*2JfsnD&03|n4Pd^tE~i)G zJRTFIjnz$Uf6^eW6*@Z6Md+)4e8FUVu-LIx{^~Stb1SkmO^}2_=gYD{twGn$Y*qD= zFrqkk4lk4~m66xUlbraBIXYB1D>G|31|wf9l3uW{o)>}vG~1_#K}3<9VMZ0wOh*a3 z3}lj+k9hcxCNv6}4|r$3ZZAmx(s7M?@%qNkz`fnG)%!3DAR-bwOhnE2g_YOImlR=D!-A8Y=m z1c|f+U^Ly`JG>8R?6`g|q%d_WNnO4o@;4!{m)2R5GXxr6=TYUkp! z2xq_HIqL|(d`)I@@C)%DxC^bdZ`<_BjJQoZ3WY0SrJ@K4zd@`VqZ`mI+3g?hsJ9pG z?Yr$%Vkdbu@aZWPcec+hb#f_qR0=qGG4N5RfNETsGnLN??}Yqwk_Z6^dlLKUE0RVz z`2(c=2Y_4D@9&-IW7j$Rj`9S(b5$lp>TFU zxV~jqc`ac$S0(d#}sRHK-Piy1@CiEWx+#xu^BINR8s z|4OiMx1x2`JxamL+PLI;mC$m#qIDU6d}g+0m-nc%?rU1aWx_A8mP+V9RE`0Z0+Ss!H`Un3J zJodK4Z-_dNSOcGI82ublJi5a0FIOlnkK4RM>p$kZCkQkATRE`e|BfdBKxrB0StqzC zYNYg&spD3qx#L#N{YH?T&Hll=yscX%dxc&}JH@~1hAC-I7c!`*UH82QyL9LW4-evl z6%H>y-M{k6n002v|%rMZj~GcQLA9+Y@|a+lxBU~UF?Bd}BYBjkzqFhuuG z8H?b(S51bP#_AtRo;aP^uJ6r*mYUy<>UV5Y508>irKAf}gw0)@8)$U<{QkuLy}<8d zAdH=Cs{tQ*WiE1`9|36rO{zRVZB%Vx|2e(@cz*4TmY0}r2yfqEpumJ8(0A=wp%aNB zFl0`{yz#!gm#97f&>SAUdAh@ynh~6^6Wi?Od`3MymXVPVey6|t8M3T}%U^Q9+~qdI zT%kwYPvrNL|CT>G1#BgG$WU~)5>Vw9sZ1K|1nC5Mq__-Tom}5p0BzZt?&*N$zGRjP z`GHmb?h7m5+Wom&GSuXnv}ZlB&krr_Pe$kd)j$AvyhRAmZ&<@)#K7x$0E9pH{5qX4 zb|mBcl=O2Q@b6^NdmxM-J6^5G#mc=MuIs9+YeRrCwY#tRQY>9xO+fARgrb%~W0h!t zUO)<9u#Mu~MKP03UV)C&TxkPqM4&Q$56I-c40O6y$v1@fxd)S_Bv8I;MZMHoJ3hvi zAti1qLhcPatIbG;5~cQ3;Pxta?inttp(i$e*MM(uiu$s{s>AIqSETZ5x}zM914hGU z2QKIFx$T<63d_9uvNy#q7-9L0*2lxWq)!UBbLjPrKl=G7NJ7=3KS8 z^?#~L)I|K6i2$uV_NYu2gmc#E;TD=(yUb`1BOd2%P$X8>MS5iN%y=LYg z?y6^%PaLlLb*B+v5q5q{?$0ZlrHOxjMxZ22#Ul&-xi1JU?$2`s(!58syE>lpbHpd+Nq!o)4O!%+Tqay==5>0@YG9b#T4{o48 zr$XB<{@G8JwC^ zq!ZA$Grv}scY?>N(4C}U8m24=!yz~rs!_v;3fok?Q33u@@?R1J87?=Prh!nr0y(%r ztqCEs`9g|GsY$8Gh5kkI-wz+}_&dYsOOKBJco|ZPNwLY~%aj;p>L()dBC=vW|6br< zD3lbi-)T6*3X*8zMHs_bQRTV=5Bcdn^0FTPni)Cs7fJvT{!v0Oj5aRZ+w#uNsH{DcXsaJJYwa?DD z2uzI{eq_SBA-}q}wR#ogS-Ez-0`HbY8vwROPzjx-RFB~x%;LAF(1ANBcYI%EornLb zwDrYW)2D(wHqrB?js*ZNTi46_C!E+<#^!i_Iwn>L6% zK)IQGk*R0#JG`Y!ge0QY8RmsM8!RiX88THmJ;#NHya?6rIpT~Rhl9JD|$12mw6LMt{ zYpu`NCnS5jyWMa?(^Z0=KIJX0d(q8V+N;k|)BgkWX8XkVMDS%*ja+e7rpzntszMu2 zoeZz)Y3=!@z5G@xZ9ek6Eb#C*j`L62w?!A-B{pKkZ%f)Jjq$1T5q|6pUWkI6zAiYRJ}3Of+8`wQ-`w)=ghwZZ8kbcqSxp0v z8KEW<0UVK=ycb8!{;c{w4+m_SG-wyD(Kdt z=GnsJn=WhGP}OX}uF{wGP6NZ5J)P^zb(6k-&$-CotN8dNl zAgpm*)?O{$>*VE9-o9mKN@DM8tj_gG&!jN*z69=SUf(MB4|HA<30Ey+K26s`uP2U* zJoN*{S~)|<1sf;VPwpiv|6VuX@$s;$G@y`~AfiV1$dl8UZQ!l;!x%VW`xnuFJ9KeE zQPDPDH>VQ;Kh?lo2-?%&e-UcY{u=>D=I7z#>QVf2Kch8SqgPni&yqtKC%U!D{6b}- zhOvSmidl(l1$QSI@rAU>@;+$;XX@qHwd$d~iyQGhrPXGPIb2kC#mbD&)a>zGt33nH z2r#eEV(e>*sPO03iy>4AsCM|}dQ1^yMSR6rx$_|TlKQkPE+fh9`vTpp)f--NK6ZuR z*TBfT8VghNQ~p9#D3+l(Raj8t118Ed=Yc;T_pk$9@$a3N__V%KWsIn?)`fRP2$uN-fuOlgblQW>h3_=zR&s65Xq<+RfkP zo$M)cmg+TRYkokSC`+PBp@M$fyAyN9Hxk{JP&~G;Em`JSXKQa$KK20E%KB7?+0*rS0*l_79AIB7x`U;0eV)asGh@^>fYq9~l2c`qviuCz(HKe>DA{ zr2mY5dc4Sg=KqcJe@#PENn!{{{kpgRR>A*pU`N1?&$2;H#ykE$@RPT_d_rPhjlmD^ z|Dg3>$xsfbwLt?}A`s<9(vsM4?VkF=<^{n6}GHCDz{GT^) zzy1~n{Fg$I&@dR7SlH;WIHauXENqH~Psk{qad3)Il5>Gy-@}7n?nA&pt_Jz%Iv`oGipuBRnINZ>|li>Q|Jf?M>j5+iO)Vt3R0#if-Ed4tEf7>w6+z7 z&NCEMD||BN_Xb&SOv1sjzq{8a{u$bs?=zEyA5tHL)m!nb;IL%1Ff=}0g}!z&xhq49 z%YwvCv}{8rH2i?~qS+d=eG(>ZGTF^>hR=9=LrTH7O5I-B+zRcfYC@4jB1&^V30|ar zZn5$cR<(*8vd=uDG8t0#x4Z$o`Vk_DVH0_CUCpGCd+%A<)L7fyPIr-(WEt|pX6Qr{ zUc4##icJee^Z=L;xD+TVMSX~0w@8t$VDn?;5?AEaI@Y-(l^}hlLXWRnyn&#BhXjMc zpGX)}<3XzpT8ywSd_IZD5u-^eNj9xA1~nT8yW7C~=UP>3m#P=5 z6;1yC1mw0G4|H+Oh12}Fx)rA@3P1u5!=(5z)>E3dawXZdid9ht#OJI#b<|d!7Ol*w z2}pi}+HmzogMO`FtBvLbZ=VfV+!`NoRYrId8RHvp2nu>r5x63VEz)W1k$`GjtIARu z%=4|nB%R)13M)lbcIQq`e;A8Z_s z@0M)zGaa~|O7MNutg<&L{Xn~&w{39T%*dGh@`_!(Q?=(CPKjxdfe<^HQM$0gM7r|& zi^ZN#S~&L7(A8*eT!>dT4wda)rgehj=fq*+*`pR!ccd4+MwuOYPd0Q?>`dzu|5IjN zew+GVT*ce<>sKwAM{wXMMeGOQ$2KBHBoj4O&9w62U@?f`)jbM=j4*lVbLs)raEoL% z2q7XTeveUkOu`n{upyPONCPNe0VF(=QI<5ATf_tuKTv4Zpqr~@>NdJxXq~Ca{p|g^ zA!d;qVIEpAh=$`8`O8gV6x~XK@5Goq2&z3sPK8iXm%CfJc-=Cro_z%cZ#62!d|h>Q zH(>-`RT{wxZ7z%9bRy@cRogXNnC3O@=)+z8AMV~Vx~`>33_W6IW@cu#WHB?N#mr2W zWid08r6ZP(n3)P+Bx|D{nYEdVNQiA7JrhXpqG{?=v(1*YY8}N= zHHT!QHF3?F?H6115^*!4K8Z5jD%6y6h@?nh=dWwj#y;7l@7#?`2(-&VD;?M%*U_$A zdMS53P}6b=d+PNnW8mm1;)*lw^IXx%GagWhD5TkvjXy52_FG9pJDTR0`Sr@0$oR7* zjONJHCybqn_1c6nTs~P^VTmBGVHT#J1AGrrhqiwJg3e{JTr$cEPVQ$TabTL|vuJq) z&^bH75@q;5&S6EqRiXsHTkhgTGRH-~UVv2XUqO>_EUZ=Cf3q-GChVe{_p2vV5=&L?}vkDtW z+Z8BA6!Yo{X4iy4-bqJ_7r?vB6c6c(DT)`&^_*mrqcD-zkC>QDs+prRGx6q`ANJ}0 z{8J0f41D*uai|u_X~y!%%Cu3V)q6p=*{|_+Li^{?w-t;<9d_K@;*sRVH;tt7`J_a9 zn#kx}@c2@72o>oD7IXaKz0~Lp`J_bCQZ_+bXHpl>xIsN>o#p~V zyo{%q{vN|mk!s1W1Bp@ID5aE>j->aH`8rw~1L6B#=Lw#I8<9?2fB4cI3pt6)>1KyN!s1BUqSPOiGB>=jzH;|n6(P{ zXU0)JTo)_FX+=(j2q(oMOjAlBPMzQead@$z!RsrL&*h9fTm&h*uKU;1u{q?(vLnQ` z_pEPxC-)}0-?GDq=ad2wNwEK(#% z8e~~aQF&Q*3Vy8>&^Mm_E6ja`A_tUUs1)NO(4nJTiQqzBk75AcUI`!;wMGs*q-SoX zT(5`x;)2NEocR{MI^}QdG~I>b?4_GyoD}1mBX&-^OEbTgskXa)Bci86X>IP!B=NbafnM{} zSjtvrt6!QQ`m{xp;?X-{u`)!5UHW;bXMtu>G8T^Cw-{ZWkty;?POO*NrbW=5WQG2> zllfNoZ)lY0IE;8m^(sFZ_L$K1>|(NW#bHKXz8%SDq>m1#2Wz#6q}1UNpWCdTN~pI2 z7J)99J8&(B@-2lklalPIqEJO1kJa>p-(*b{dz~NnxorrsstK!HWjC0YyZuWTQ{`D} zO{oP;TOFG!u)*kJ1fakSTPw`Ugt9?@0L~2z*>1YaO7QX_HhaXH91xoh*&jiJ)>q?^ z=ns(EkP$SW)(6PQWwqB?p^&oh&HRP1%x7RAucxkGrLwFwKcYmk=eY z5a#NcHH|`-osVwWy%hcnber7Y0DAx1Rg(=BUveA*C@E$^5c00A`8}1d^y8^19AjQP z_QdSH&;s7l_7#WSYoaf)Kc^*B?#)~Rzx;dyp;Y(u_%h~F4pkohk^R|X(56K0qEg^-!zz3)4pK$YCc`&j=NpEMwtC`#FC=veij z`24;SwmiO)6|`fyJvkjy-jgeg*pn0eMOS4E42Z(^LM!b4-rZz;_lh74t5czgIjzoB zTT-pr!wAXVY5^6N{+3MA_6K0J&iecgl{Y}>2@|pKVM_-2&W(9FlC$TkSq8m%_cuE_Uv&;_pB)ZKBUiE zvB*{D9y9HWM2{PlLMAVMyZERAifeQZlkAOIX5oq~E@RiCHEBDT(H;-W6~-_ni*F5Y z7GH9{FXJYG)cdX71{gv85tM$1qJv!)NL<$92VQHh=w^pJq&EM+=D5rPKR5#|5?YNk z3ucM2$Ys9B18?D}YgMX#r43T6K}c?E%-5`~HLlcSNdzvm=vqSL=AY5d)U)kfAGG;A zmM=M^c6f?IWh<#KCM|M6gQWGUMN6FKg5Ih(M_jssnUUHBhA-~Wf7%1&dB*OfMuo# zSr#~^sxcBH-jzQ92E8b8?VD_eX`EofL|hYL=C%4h){2;77u7yZ#_GO|ttmSWVyVf1 zhN34^3?&M^=LTE6QTP4d9-dLpnV@zi%>kMikP?+C>_COLH#;3t=i4$ zRTlncY3B`izGL}fNA=D=&okr#r)v4YC|#o8{KW->UD}|;jN8GaM&WIIzNSsT@NTP~f!Bns z=cQLsxmk`|i)q97dD$gHvR;lpYn;FjbwLEr`hKOiIh;V^JTXOj2VT+<^tUbhaODy8LG;k{QJxJA$`g zz*j}hB%->Z-0eVewRXzYSN1;Tf?-X)mZhW-%U4C&)Fb`_F2im{6>g|hTR{{dvI7EbFs zOp7qj4Gzj-OYn1(7U9L%oN4K{)rT}|8>rphxmSP;)XU}S{$(JL!QY*W%XmB=4+@oe zT56WT9}|>{nyGH$cqEB^U@k7j4Syhg^s5ja5gDZZMZiDsma2YSRaG?xU@;ER4+c8X z8?vL6IbYy#byDdlXPayI$gTpDhgGO0hb)dOrzaijKEj`62v2n~IfyV*uhiiMDr2E- zI2+*Hw#^bsT_+gB(A3e{vgly-i&8z*=RWgPu zl})fJc^|oz)+w|Soz{9FWmNseB_>`U3SUPoWiS+4>kptVBtI1ne@Nzb?YC8!(!nn$ zWChjFrECM306nzk%p7)UZ`!%m;IZ{sPFxpFE|`M~S03n4tzUcu>GHD{76g!a&#+QI zO>d^9$OEq3%+~NCYKN@tM7=s7x#RQy09Iw!NzX5~U7|Cc*GaAE#O+(M2z@DvJB%@b z`8sQ+u}`DBosU>fpjO-;~T`vE6&Txx{t>=kj}%%LRYKO>-$Y9s&fX3r$o%<0&8M zsK^XU`sfY=k^puckBdj3%_>O9NZGIu-{S`Z6=k$i&wlD-u9N*`SEP9YMye9Bj)O8~ zXsdabWb(o!T|?n-6~XQGpSB7`=<(fm)T?o z1sK)N72T&ed#)}dstq&&npw?<(rFH_wdJc12-YrzeR(`{&!4GZ)+o!}uhrJk=R3_T zLx^pHiE)`Y$=I}a``8)D6+=}5$VV0Qbxf&nlro{lOg6YSwW|nN zGP<=}1HU=1vT0WO$pc4qr@3G>Q3NDgA5<9hN1xq}Nd1{kj$$flnZz?3RmO~g34;W9 z80xi&MJMl!G4y;H8pC@V!$dN-Sl>2^LWn5LI$7i}ZSQO|jc*ewkgrNj1|lGm9|-OH zomYz@xt_bSFR*ibwl-6Sb;D5JH#9i|i}Xlqo~X-o>Ohx#l|lMAk^4zstUr!L7`ZnG zx+cTM-Rp+|>#x^(9cIGgZ}=oeJfLfA9^vLiL3`~aYbwireB?!!;)0m;T65FR&-_S& z`4L~V2q7P`1g|G`19M;Z=SEZA-}>xHwXG*H_2#G8zG^e5SttD>VdDN^JBIYm#A*z@ zNp!!fbv-Bnjm~2Vu*tC|Ut>_JliWe=l0c2BMVpMDTABe^c~V85ISCbg19H*X>iM+7 zC!&>Nq`Rw0+|gG+gZ8OE0AT;wBa|2W22=}z6ZSS!kKVUtFbD9zt2noA8<3yqJ_0)o zN-A)x21`%JKy+MRV9`d~{@0-Y73Tj)^#?S0D65s5s4z`E{JHN;&3~Epe+2oz51cXh z$o&<4SSAS3xkNKpa5)=rlu}+-#=`bOANN%My2seJJ0?K3iuM(~7_V$M^i;d-jGiV+ zS}p9&Ub_kyE0~L$rZN;t5B9C2{djN$)@l_!LE!9J{Zn_K<&G-L{Ezt*K?b=lTDc^~ zwf>2(W59R2kKh;y>$I78TFN7#Hk1*c*fFVp7s7Sa<|n4oBdn?IM&@B_Iy&;--;}9w z;^PLnb5_;;z6}|EXB9(!TfT3Gp(J)LwR&0ky=&7@KocnNTt?e z>;a;rPD_>X=nyr5hF9A4F&v8jOQN{G%jzyff-p8jg88p+#YSQBuflk0f>4i6&rhfu z1Fagxxz$u8*k2*o828FDieh^+fDZBGG8Qu9!d&ljX$M8RodS2YXg-hIu`%P65wOJE zEah)q++-)uHzP1a(b&A75}jK8Sy|0kbg=YcN?my@<65QgVyngGm2=R1lp@t`XC$)5 z=dk4P3he3=?uYs`f#P1-4v1Q|0dRi+PTk*d3HYL_qb-R@Y&3-DOtS5LM(}<=>4Fth z-Rj}sc=ShpI|>d32kLf;#zUNTJH{kF2aqtVbok?Aa~NIHZg7cNAlkN8>=dKR%{yCb z_Db~ZqzJ~$N%IM&uiSG;`~fUI&T>n339=VO8Bf+OhM5?y2w*VT)ECBBrDvK{;Vs3I zqBYupRK$efr{if#OtIv!2^xQGYFVd(*QQxy|E4J9t>`}aRms;xpMIs?K&sVvN?hr% zzyD@uA-mw5BFbP~6pJh}-{UAWAqXQ|m~#!E;zNZs`%U5%*57Y0l1vVy1CDi;x=gU5 z`ITq~9?Ri-3+)CL2iNGgG#AK@E!(#umT|%XKA40sC|`f?$L_T1geu#v<&$ZbABcoP za^xd;r5c@yn=2F zI8-WJ7T1coqDTB2FGH?nQ+Bi}#UkQd?tC?ol5k}4z+~geCZmVVVN`4VCFg7W!noPr z4Z9?}jD$3c6-!DXz?R35@nR(px!(1QlI|H-#v(VQk_iLV{g#wq*T&53#h2IO{lySbwuQxXiUCVhzda@yAzu#j+QG z->ghY*XEWyc90MwGgL1(j1h=2Dftj+FNf|$w19qgHX4^snn+Xh>MXZ;8wCWKmw-Lg z72FUdHeX@^PbI(ZK$PljombgE0BcEsgMsmgc83^ogf=*mqGlv@tQSu+7u>} zLu*POe_x!mwvjZuDCRqDv48*#5f(Vd@1#P<$b(9Y75(vBQ=NH7mfPgu z=~llatLXX=Q~>&|5Ya*Z&|^^5EObUv2}*h*`PX{O=gKR{zZW2VH+Bf-s^T?w#Ie&T z4-&*cpvu5=&cIL|JbF4YcTSmtN+>`_yswqTm7 z#}|+ezwhhyst{Q>^z5XPDn?}Itv}+*#R^Ioi^cLEClHJ`T^qXaRK^??%+5-k@bRi% z%>PRb5{}0yF^Wkpm2$XtWzMr?^j!^o;12+qBUwjuX9z4k@KMqcPGj1GL*%3-qNNpG_dwlxm#39`j8&b`G{}ISZ)P{lV7(V~;w*36Q`f=dkCe(gTUE8%y z$oZ)}t`|AoS7=o7xrE+JGc5q?51{AjJ1WjUsa~F3CRd|)htX`fs*VcDGjtx&XQR+I zYiMIv``V#g;*%KvY3N^!uQb}gU}HK?GID8+F;6Cwp`cZio zf>$c}+Hyblwm#=EHf6$ygEMrlc~K=O+>ZSV;FIx`GJwWxfbhHcwa1q^rog9cN}kV% zmAJh*uHa=Lz7y@rt0Pdf z578NRL2z)F&3k-mc@2M{mV~TQCQ>Zh)G*)v_577SDK$m_BUb>%#Hk57dpw!@h4^pI zpo?{CX1g~CzksV@kMB&`&l}wZYBo5JLy=D%5_A_-_h-eI^^#>B(e47<`Caos|HQDU z2NpcE?9*ZeftQlc=)ckAkOz5__`YKjqBXq7q(xHftRZ0x4YD(;U?I-Q&UJ_nG8mUz zOHsN_A4RItL6u4y)4=WbnGq`Mo)1kgIypA6M&1(P+*4a@I_oUhV=Edr?Ulp!TYibW zNk&R^7%p{T(WPO=uZb|i23t-yHKI!R1NZ_Yn&T-c3tsV4kS~I-Eff^_wJ3B2?bCGQ zmBrB>>nJPAM}Jp4gQO1|XvphhG=O8bTvJC?C|%w};ZaCiODCJcipzP1Kt+F7$F-Hh zu3z+?nG&(sKpzxVM)#pf9Y^jUA+f0O={Lr~&;?-gyDM85hD7F(5E|Dp2wO@q;|gW zovemrpHHgejYh`EKQU-wJM`4$o^Fmmxfmk7fVaaQ9e%_?tRL>dYYhZhpjg~>3_IPs zx^iiarCiJAN}Pl*wIWzj-$&aOM(OLwh?Y{4n_fSem~I=XXw!}Rg#te&O>ucIv22!G zkSxW|NKzYXB^r~PXS&HaCv3GQG#MKMRMc70LF(s~vV$^4ftHqL%9E?`&~D@dFL!y) z6=uoj`je@P-aZBA)0cx{)A;Dvy6#tuvC6Hy<*SMf&Ys4a{e~2xRq~Bwru1UL8U2Zv zS5rx9wD=1w=s1y6|y70#!&?#bCJ&+e z%9wp&w{8E~ubkGUiY2T;+78OfSG?BiMs1AYopg-aNDORm6=&hh4^EG8*}dq_M?V&X zxO9SnTd$S?U!)4^z^WvpTLgtc`7M|IMmA-!fQ|%_r8YMT+u){W?8IgDLd<{35qO08 z0gr35J#}EKhSdI^>C5_Vgj<+>2)u^KGxBWU&5o2MOs8!_o!t1ihVvvo{R_ zr;0B+l2;oI=<1CYo#n@Tgx1dQ&j_bmhOHbt@=FfYyBNp$7Hq>}Y-l>EDw&hf9SR!y zd(`v<2Mz&hQbfN=8FJ?O$e&eU~El^inK7Zc1!=mGT}O zWQNh!hN#S%jj|niwDiXhWsP#&yEd%Nu)Eh8UyHQOO$j3>wJ4c|T0YS(ZOc1wuq>1l z9wAM{Y@KZ)_Qt-^lNhnrFAoXD`svuC&iwMPPytCc;xE|#a}k&id-GXxz~psCe@2O{ z{gkV838))m^S@i6Kvwrb_hYDZO$56HrQhnSRivvT-tcYt-Ae6AuiJK;lL0StHjW}f z3Do@e|A__$tQD1yKuFV>jRH&;YRl^gV~?u^duF_S(MMih@4jHe>6fz1sO4BIb!Abu zxX-xO`X%v|vySA$Vo?I<`-=v>2t^sKW<{$Iz_!vN)EpufJ*^b1$XMhh4`;; z-@=&IYD-18kF=RiqEN_7H@k!BI~sqGcw z;$S)(+}Hhrgnh(!Qxf3pvQ+#i_X9E9=&7@^DuZK zwdV{Xg7^=9t?1~5%tF$ zB}odbTL5*bl!fc4s*bLubXdC@4WNEQNdjZPzu033LspE|WL#uhbiBBDqT8YOw1;gv zH_JEkaJfzzCo`%e*v-)~qf_+cwL=*cZj9yt6P>0qm4(T+=VaJm-iCIz(SdLgN83P* z{{y(KA9B0OOQ_UCLomrMwi#GeBQs4Xv12DK*lX6 zhgzndf`aXkxS-^gDzU8-ZYt$lb@Xwjih^r7AS}$tqdzW6D0-IQdCwc7Og0I0Yb>~G zeQn6eIwF^~e|#W$4!BL>W*S>{{filyd79Rq~vXB z26+FskDD`yBSmnZUv=Kr^~C@@E2NPo-+35V4L<5g@8YA#l1gw|gFJr#+9{R~R=t-& z%!;3#Nj5bm8NfauK7ts3x_a^=xj}l+$lJ{Ou+~#V`!LP}hk`@}|43Q)cSgUD1Zf|6 z-2O^W2c=@12#tz?NlMPH>im(SuC{k}PV^Tx1?!iB9S#v?Gnd_KEKV^slkatXll;&UOeJ;IdZz*!1M*4(Mq_l-Fuv-rou0=`!{SSUu=G@l zb>+kZjY}RQICqN@dy1tP4CbD;JOiO#I?(zUuA?dYf*z}=O_S~(5Au@1L`HUQ*6kzl z3_qrtrXnt#G)DFVnf)<-)rle&?o#!&^j!Y>hrcYEf-rV6WU~lI*&^ju(OpwEM#S?} zu*fD=KF2HOc&0>Q3byD3bwP$=K}$SyV%V4&s8+NA;z3N-KDII>IQw|rXTHI{+g29a zpr>sxAI+>reA;gP>rCAx)ai>#(_2N=TC0Hxwth%z+&lM{(1@7esTG3e{ z_Fs3}N;V#&Xp~Isv%)9_C8)FP;be;_fOr;h)YJ5hnmj;;VNtVyS&`WzU}6-F8MVoW z>ZhfAcHfY7AS|7zFC%iH)2lhZjiIlIV*E}S}V*`_o3iAkOb zEKIIsPU`k^hi1_9eQ_2szu&p8f zRD6Lwmb5hNHT%4`SpqZ%4eOf=AwZBRS=Ox5Zl5%qVb&-NzfJ4Mg&YHqh&DuCSd#cL zsCMWjI^>PaoO`h_K~wrraX-V@B@LJ7a3Ap-&EYDIHdVnf)p^D{= znlo($rKlo-NQ^M7p6vpcY_%3QHr?29Lc3|QT`XRW!VXzkuK0}|zexXtpidZk3@DC~ zUns@$2DR_>SfKcKMW1^+n_ll|Lo0c*mudToEb6W~k7V%}yQa!kSGiEtJ@#3$lb8}F z?&F|zD}M$rNG^Cgn4J)?WZM6B-ph79#&^AGfl}pDRB4qq^bzdCizGprg8ia2StrI1 ztw8k2DuQ3vZ>CdGM1WsVSPNF%@GNJ?vM@Qt7i({V1K#jE;S zZ+_n+`j@6wF0AP%2yY>CWa%}+gdS)~WAPGIla-@fq&{iwki?mgD5+KUEa}k6$+&n$ z*IZlP2@q`*quNBsH+xf|d`n2}n#AeG07SJivhTRw0CKtLF;}q=)dEGPSVgW~nAc6x zIJ`!M(G82#Y?VFqy|~PtX8sNt0tv$gd&+DTsv*lz8J+@5>osgLi!F5RhkO%kIvq;+ zN0afcm52x||5K0YbAr(`0nw{>Y3*$WG|P2EvyfsVJ0^C~d6pV#=@m1LpT@`p1^gB2 z(iUh5FQn_#cH-D8Uk5ValD!HXRGZ2=d19D^7mYNvGiY2CgT2~Iq$PV4&FKtFG#A9< z8yi#Y4h>$^Ds0FlWm3EFcvA!z2$G~|1t9iIs5OPT3wQ@xQ&YSuG3>K%o~YO9c1z4n zUG}-&D0!zX zP5cz*yRYNy4HpktzN@T7Z`TSyg5Y(KW4qw=b64 zaWslht*gt?Q$d%1oECjtzX^OV8{jtQl%_-QM?zB;<5J67X(R$&wIfk;7P z8oG7ut&qgo6Uuc2nCIf~Ea|D7AyDzcB`?Py@1&_v@v6;)B>LqV&a`zCcJ`R21`pku zqPtUq=#bozX!7DolLGgs5)=b{R`N7~EH)feO!*xHfHy$h{==7fSQK05JA;V!Bu|PS ziXbWmB;osK47(+cwH7&>5cw_hp~AzmxQ)|bnU;vO@r#msVUVjGDNh&S{QFc&oo}Nzq2Y?xu?_M4A;o;>IW9({1Aiw8n$i%esPoUN$W|(c>2y|dbEsm~EM`mR3&D69U zNr}#2Ed?)gn`NQ7r%3#)P*7ltjWAV##3O0{eo`^RfxXk`NXI@r{#l{Pb$UN=zvWOY zX}M3pzD`1oKvMuTe7Wb4*opw71gjkqYKv*VlhZ<0cpaT=8J?hj9r4oaAE@K>rYLLV z>F|PWm^GF#7e>oyudNrhwc|HaJFi_4-Pa=b2T;|s@~&3c)WJ)wYbI#c6?G8sI{?F9 zgFHgIfFDE_QX(z7j^Q;Vt!oyMNUqOY8^mbcWQ@~TEM9uuOb6zzJnED|dTS4m`#e>T z#bDmx)m$9WIK_*$orlPdOOtGWDjz$XNb>|li>V#NX>4jK5W%2D-C?cCT{zEd85LYm z#Ij6hqRaM*ad2fSm-k9`*5)=X`K{oM1R{u9%D_tCL(VkQ6yN8!bq~`mvh~vKdQBS|7-L0`3R@%?Jd1neM!i~m&+{i%t3vttUsZ4hZ(yBemR*uJ8 zDN5zstW_4#e(^E^(wJg5S{hWyO;AMfY5y@gY@A))>!p%t_SsL(q~D9G3R)&X2P6Ij zw1w)f+viW8AZ>@uKdURtM9s(zvyfg-_$E`${=AMN%Z5u0*UC;o?n1;#Ueor&y&RDZ z+y1d;9#|wAYSLAzf&kIdOy9Ra&bE=5|Ebj-SmqE7U3sDCt24$f@|_w}IwrX9K_8R0 z*3fHapIz+ny2FK^gbZe@@A&lb7K_!ogKhm4hK-T+pUV}>iP#aPu$PFM zOIZhWns@sZV{@9*peMhZixi4S%de6mR==iR!8d-8O*hI;oD>F4yUgs3&!`S^RHELUiR0k#IO`L~x-TA9Q5*)8`9<`V@M}5juAdlp z0`QYVRMz+@{ShF?h1aR~OX!lH8Zq4x-_E{XHf>CNFj7;9$2?PT4=Z-5<|52?a|QC< zSm-uOUcC65X^CIhVJlTiOl2mR}^Pu2+go`UMk~#5r?2?Hd$0;gRuih#9 zdBu##2ksM+QPG45=%PMD;!@qyDB1u|PpxqEGvl~jV)``1E6;GzMd(Ut5TjFiPM<7- z#ZsT^H@6$SNd!Z@JezpkWijTquM#4F)`N~DmPr-;$;p~`vZrD{cc1&Zum?|fuMl}- zKfb#AHU6ZjGszg~BQHq8k%rK=xb*4q&d+vhD~e9Yxu$#-ftrBmLEteM8+ab?}<5B*ew zwwP;uZkr*K{#0IuzOg;<6;i2IHJhY$z>*XrBoZ!kNh0zHd_LW8tJK~NH-*AbA_uQ6 znWisF`t&@QZ{r#f6DNr1BXHEW_4h_}kQBMCc658Lft|#f;)t_ncVCd$k@c?(%0S8pKMht%d zNsn2l`F6q|W-^u<4&VL3h|Z1;m5=HhUer9W6w^UyiD$P)pZ@^7Uxg=fYIOVbjy^1Z z*gg6fcw6R#QEpg%`~h^llN|m7q$}wC<}o0{ALi|ExTu=&{BAWuj6%6C%U*wkF~WO~ zJey~nWWMu@Pm4Gv-kOMt6c7#H1^55Kqt)f3k{RG;e+~-H&t^#G&sp_3@3eln3?XXk zm8O{RV{EhiY9k5uR`lrM?8C%Gty# zLWQ}zW}$LFIG608l^&%^PG`UL#jYh}5^}EPCDge|re+G*&%k&!3Y)Z2T`-{@6kATj z1MR4zcka;ikuyka;%YMG%v&U3icL3w=ce=`pgZ?(6H-pytx^TUKxrvsyPNO45HWP7 zHrUfr;Dj7li%_RmtDbljO-(2telCB>L+d&Y9KZwG%K+ZkaTq5(0y7#_LnANiG{qRc zRBhB(t*qh3c^LYz-?0ou`16kDT-;C;)usGn5vph==#YArf?lbZW1 zl0REI&q@|L%?c5OpCb-zE3^Vx&xxpbNF@Hqtcv9?_U$24a+7*M%W~bZ&8K(4_cN`0z zRZ{){re2wDRa`FBio|V=i27byI`wrZQtATHf1)gTmOE&U5hM|Hkyss(Xdp0{!^paY ztqO;)Gk35}bvL?%tnWMzFYLdjvP?nP z0IKRZ{ei~rv;ty~;M(0)?cQ1>Y|Qiprj^4Sk8X*dGS-@eRLUnPjoqHCah0fU&U}vw zIsrOy?H4g2C*Cj9eY+f)H%jO`IO19IG697Afyz^XapW2zt%q{M0lHtg@_2&A6wIp+ zy6QfhJ{b9qsn~{0c%!h_WEop~ufQ%VI2_j3wG30;tE@3>#X7%UUV6)uIIXaiO{yZ3 zNCrsOHQfB~{ZNprP20X27!=C1?5xIcM;-S$b>`M8zu)D6BcFIr z_X37&K26fv!8nB;5wSx|JkU!{S}S_K3$j9IB<%`f+9#N6pO+pV>QbUu0ZMPz6li4y zMK2R&9kCB6cMNZjG&z*)Ek^m4GMlNd<@+45cAlETGjm~`#lKG$>@&LCsbDB(wY1Q7 zcr3zAH7vIBM7dzgBdUWk1&zsL3d+>Tcp@z@3qv(ZqJZ7nr2>p&H90!d$TK3p6O_3f zegWdZeLkl-=S2qAy=Gv$k6KFKPpZ_#AZIXLVe%g4bf#SlgpN$G?!pBHs=q~=J=-dL zR#{^{Z?BWhsnyRHr|cyFx<M+D{rjb73k*PpbcJjyw?IRhEXWyyX(tl zPN2_ykn%Di(O8>4$L}1Bv4+a$uIpMzsieZ(%#^hve^TXFVS^*5jKREAtdyM5HHtY1mvXUhLG#J`-4U)U@QE zy3L~jy!afs?QHR8f1N*P2ZVyA@{%n50eDS z_k?5zVj(3q|JCSO^ZO(YcqoM}m0fO1QdX-t0gx32IVelYEEy?ve*}~*h3yt}z;z3x49IyjI4<+$ z4u&&FMx)`Y@i5&eb8ER>$!a9K%jp|-G+q_(h&{C(jIMEe7aOp*3p2B%5%y6DTaulh z%#D6LY`a6J*lp<|%iw!N+C$)uNyYSPzKC*At80Qk!x;b@o~Zz7D{w>)V|bBz+FMDE z&(^)=+phT)48pQ*v|Sm+*wG zm~~XkjH=l73hL_xzTQ4DrX?F6$Z}Yto=yirQZui*BRVS$^NrYZF{-%=$>~k?}ys?R4PYStoR(m=SC_g?^(VtfYp_&cp37 z@)}!cU;Z*!Hs3gTsI!J)MuNz`7a$H2N&Spse-{_I@+}3PmgJBG`obKIaBlaiN`Kf9Jmo-F((K+axSH3#Xx)AAZ@2r~52YP=sLD|m~m*UKeR6I{R z^awfL!X|K%+1S1FDphmiY3!&Fz}!C!8mstuM)~Wow*viXb@uJ+xUoQ|*Uy7h5U~6m z1WFEg^7?u42N3?2%7=6;hAE#=^UFLk6I|T=A=ti?nCs&{qHD1TfmyXq&iF~rrI0@i zb>&6s`m1Tbt(gEJpTzoU^w&DgSWiPg4g;46q+j|vsM66WtIbg_7*zX_iW*U>u)DkG zFh{^qvyL1Ts@W{5FbCJ{*9Y3`49Vc*S!S!u;<=xSgiVT)$)UG|Qqv|ScKm734h1cO9wdtI zMfi}q0_Bp{)^k)Z%pr-^ts*gfc&UB@no28X+|DPLpdSY70pCsW;PRuTt|y9DRi1!**Zmk?ZY$Hju-H-I^gM2(y;YdGD#er06OM+T^)Z$Q;F}U20ehUYGli zfoO8Mlv*_kZ-&Yq=o@mq7C#+mCQaj)Z2Z5W@c45s*?t&61;3<;!{}1o>zXnC6j#;}{&e^PYs0^Z^O*X0~`p1!Ciu4zJ ze3>h!G@k-HTWS^I{;GgQNB7MUIBo8_RGk8|rYVl%ORG-v?AB!V_5*EpVx)vCuHb1c z)&HDT{a^Fu|3A!PYYH*!lqPjU(ukdJ9j}#PZ!BC>pk~d9bD3>o0-g}jG0$!{i=aHVpO zC1?Gd4sx1j^?Ow}h&|9|syc=#muf**w;`gAW{?U!j3JF%T1|8bI?#mp@NJb*_Mk~I zX{$&lC`>qD|6xbA4?!&00QTvI1q#9hj~(;u`+L$wBGBxV2w86n)q0x2*Kyr<6|U%t z9ZaoBBkA~CxRjemRJ+4@W{F)@o-E~Ofh;$p^%Ke?9Q>*7`*v@cj;?}QA~H-qVM~c| z9noO=-e~hR{t`ZV?nxl`m^`Ul5c8Q|3W>QlyD;5g4#k{lqe!tz83!skj2s(G5Ppz& zA%BOJg7X}hN|Qh$u&w?Fa#`%Qh{`=h68YE<#^X;XrR#7&UVm{dh+?U=f7NLS7`r`P zo3I;vjd(#CW3+Z+;-~iOK{)RE@MogA=2`T|`A8tR|4#i}`UFEayH5hRbMD^bdP_+b zzWZ73k8`i7{EZkS17u$TrACnz1*-YYJ+N7hi+^ZgLi>(!JKun78MXR}L!s%e z^Bnsk%5~HV-`8TkZPk9dVbA{@0@g^{z-fW)WO|UDBMCVWyUu}^+sj@iz>zPS{;lt3 zMaVaW#R5;1?Kwh>sHAIZy9~2Uf@!e~g_&RJg3P8bz_jEJ7f&}&qM}vODHWr>?f}O8 zNf9;aw7byPETcXwH1gIvZ#OOBTDi$QDR+B0jHgttWx7>tm0C7(duZqa(ldc_qfErP zeC0gv8pUX_VCaLQJzh>rZki8*kcx)l*ExeWn3PsEWlZIQWsVbW0JQ7eKu&*gn!luf z;Q3nOi36(0NydHY3(Z)&WKFUG__}hnuo~{bH zu_P=2(p=W7MwO(}UmdYC`S^x0*5~o(@62a~R4P695A}d@lW}r>Q$=~Z3~?~>!RSXcJfi^uL1f8Mvh^bgO@FU`B}8n=57j)SiiC&QON zqn=b&XRNYH{{Z5i)D5!5T|X5AU}VyN5i^_=D<5*yyrj*`=TBG~KhSGVob22W*$H6> zteN}Me*A%szG6SqnEMs%aY%oyRVho(l&&DA$mUG+xYTkgkPL&ljfE)UIHOWsy^tPj zS|v5Q|AB*t4Ckuny$$QgR0);MGitrArLYo;$P4~d{Mlf^We16NQJ#%Oom-Kwa-G8evwij+#cNWW;a z_vvh2g!SjESTAeNh=5#@1G2}bQevinEHp>L=(#JY9Uh3yo5P$z4E3`9WH47h*HVEZ zPx+|V@6~A2t~v}uIeh$}6IMGivjl2*_7>7^A&QYOX1l7EHGcAvf zTNS8U@TlcnEJe-pLw;hm&x1ns9U44nJL<|%Gq6HivQzLX10^;y*KMX-Kf9Sg?8>Un zWhKKU%QQ(Ls>`|t<*7GE<^Ik+90V>;38uijG%$ZFqi!c4!3dD9Reg-zfSP=w;ueVJ zb(~TI3cE3)6Y-YLqZrqz!Ec=E1rBo`tH)K^Qka}QON6^XUabYb?eb`SFIqx2jwd%(1@c3HIo>L7 zqWwA~guB!cc4yVq7jM5(L3tNs?#3dHft)kGcFcak)aoKwg+>JBxx`5O0hZb{;!H44 z*vZ^tbPSv`gJhW_$yyqw*}s1+XA$`Wh<_79Yp|DmN3EGeK>WCs?9&%IH^bA5MEfol zVVpd_vsCe8+P;aCb36aq;{3t;ft0!rIe;`O!r5~&jlzqm7aT%PlG;kUXOgDVkF2%R zBILU^OL#&guZ%XnE}Kd=1*b$F{@!UY4cEb>dk9yG{*h&xiyygxL9qls))%G*(qZTX z0W~tzx7neEbp|D((i!5%nDAlgh#i5M(A~a~V%JmUAA_M|A#d3F8B!jn;LbB4p{AIO6^t}Tr&)u@coQ#$S%X&Ay<)ZTN8jM}RVReh zCOtJ(cyCf^MN&wda?$1OHK8q(kE=pRmz^c!IIXIXnS)Q1#22YvvdJmsZ{Qy3F_3qV zLiNJ>6gT{pfeF-0HF%}*M(AJKrb5t(JiZ}~^fOmzZ>UR?E79P2 z(tf^-GFYvbvtIBFOY8G1@lR3&cGZLrkIp63-O_&J`bH4_tVX6D{+M(i*ZLXQhJ)U) znrUc*h4-rv;t?p7>5nR2d6cEF#*e9u961!ZVf^9PLF zoL#QA?U4L?DxhVRlU4|we$H53Qd>{ijIr#DJziu=ED&K;y??jlrc`oT#Xj*wI(}0Sty!3``=NAbM^_gu z1DmmQC|y~#sVZ_fqC13&NkrzFqSw~FYO$BOE{_Fki$sV0Id-H<_C^IaBG!XukEhab z{t~NRj%rY&LWIF71$y2eC%%x2%R5jOOE<+dW0JH!LI_HU1HMY=RR>tWp?FAO!4CX8 zgG%tnje0=dv~4@vmvd->qyPC(CbW$(F{v>8jC;RhpJw?hv6OQYk@Qv5Q#jvOTD~m* zg*;DVa?e=`D#V8Tgq`*skiSuAgv?Y&U`3OOKfP z5tZV`R*wVU%XcB+;fcvnOkO3nMSJKFU_qa_0a=e@2S)CvVsXtIzW) zi8Y37lbMBx`iWZGg|EUFAGa|n2b(stdFa}zRgKh+cSHU8q{bCXkvQmY^gd;tfxuCK z(u!sa`4S5^&E!E)Joxj&(ULxd)*Pk}D=@ zRsCzXS~|jlY0g+AJ=cnpUYo01k^fE*Y(s|9ajMv;*xYe)i{cKyk)huf8FFVPwYj}s zr8_yG{*;6nF#|2(7Z)6D>7g)y*SW~#O=c$LR!^{K!;HdNfE}m%wgtSwO7M)I3_TxR z^CAYgHLSD)UbjV}d>K@|j}DjUL0!j*B)ghDN7cii>eF^jCR}fB%k5DSDg)?Rpj^`Yjn0($|~TOZmg#!#>A@9#vT#Su^TgDG zatM#>s`w9>UNjDcn|TTM+-B}VX|CgS*W@JvF1-9oI<&E^_GkzzD|85_lW=kHMu-c- zqkMD=WLX?@g7AAaZjgV+(4`PL+VObOj({j_IbfxJ8wn!{c_iOOOFlU}P7^p8Kww_pzB)nRJCrB`U z?KzN9z`*)gZG>?veoXjvYtSFBJC|}__IJC6g;4u~o3a~L7+Z>J;Y+`iXQG`C3#Ubh z;Bmh3=?c-A9@lzfG{J%y=e4Tg`J6-iQK76IrRsqs8^M`k-j{_q(yJK4ugQ*=rv~M^ zH~Omr)y%nxkE6v|Y0d&pYDL&_@N+EnfgW`1r357zaUuUeDOPNkjK!XAZRZ*I&fUeb z9TJ(P{Tf*)JX#aLC#{^XsjtHwDB=X`I!PZ6L=GhUI8S=9-J$Mh(DLR_#Aq>Hs6*0b4tV-f<(XIm@(DbaE@9CCmR73e3_5Z(v!bKUH+i zbF}luR+u@%^JVb~VkWd`v{b8+t=GfdMidCBhC;)oS{5M*MJY=Yh~@T=Z-2wP_RB<) zOjB^cSr`9WuQ=&~*HKX2=7u})H=3Hmk)Er0G=PDW+{4V&?fz7;4581H1sH$25E9dk zh{cT~ZAAyRU_@yd5=m{&&99yMG`GSVi!^8Nf+NQnJ!%8nRHMfPcsg!Yd6}5Qp=cA| zc1=$+P%9~eo1}~MNu;nI5Qs&M%RSsU)(pMDs3X=7AhS_i%m27B=QdWJY|ZbKtmFD&;ZZ23SDyKuXLo ze#~$-QWL4P{S{kugb~QuRGVeYt_|FDLn1a48i1YzT7>>?sv5cg@FFaaAPmI{3%Qes z-;7A2=|553CkP@GX8aeI78#7a8nFkU268UrUCr8c(q*RH1I(bL^#v8pI?YROk!K=l{=4RT}TF4h(n@CF@e z$;=0Z1iT3C5S3dR!xobNbcNWJ7%OQ_djv2?6sex!#$zmyVRbIzMj)^mVlHuBI_wlF zGvMxp7u#p%W4_V$iYPU@@CHD4{c2m%^?sxr1?h!csfcQm7OhAhW`SwT` z6ePbV=K#_0pa<0D9Pv}KtAW`{v}~Fde-{{ez|+7L%~br0oNB<$cZi23WwMjk6d7xf;Q zXG@8PPvqG2G!U7rIEQr1sxOvwQ&)BK7$n*=a|AA3~2@h-OReK;IqP?NWkqyDB)w=vmVW<;bTKUGv|dUJeN9;AHZ+4lCGXi ziw1Gux*`&OyqeF{MtL=s$}YrMChVtvgE8xBfy$pq|B?dRYtuH%C^ou#zwu2IWB6f~ zz;tNUo^>@;>6doi)GQ@OfB)}DqsjS~cj0q&H)@>c%!%6Mokc+QwnQjBdhJ>WW%&y= z3Zn%<@-J%oIf?TAID$Ol;w`qa0l)Rl%GG2)99g$LwFjjt`Q657DuT*2J#$NfV`_3d zL%r=138omVlf$gg?Zl@Y*Yuths+ex69ewD*W!vFFG$>T@mWziLLtE$Ib+gqZO-d}1 zkwJCEa09{{432BfSkM{3%N7w;<5cUQO%{f2KrEs&^n{DIM9sW~n}M+;Cx&0d(%&u?XtI)OS+4SM(2Sne=ZK zBH^qq@=mFRiT!yUTV$k5y8xeZ+ajOm^Zht@t%>9XR<47;Uf}c#^DNI2GvuLY3Adbm zwk{bc!eq$7%TM1_x-a~S~KeZK%IKRxg zpedb2{brGN6AlFK9(y2o4cUL;HDF;8(BNPZ5Fm-1A-v@O!fQ}svx~!FQb7P45V*#_ z&>H^+Yp_YA{GsMF6;m~LKe&PCl7s*?TuU_$ffoOP)PVU9q{c8P)IUtlX_`^2an3yz z!HaMPt<_BE1ibub&g(gW*x^2|kN(8G%yD>y|NRjZD~U+FWQXCWdD(nqn#(kes~vSw ziVa&|6{w?;Zm+-Iq*#z@r3VT=87eHz^-bcPk$77YuEp#?bz|_(=fSu?C!$4lBL3uV zcYr2S6{sr+6Q_({8*`ZHO-tAnWlpt{;+}tijHV%$&n#E|$754dp-e$P#&2Y?OEN#P zi%yJ~r*{&!$;g+1k|wc$0mmL6I%2P!ht`f~7I{;BJ%UA7fvsYrDV4iVWF>x;Q*3;5 zgiC?10>Q#y<@vg_Qx3(jgWBNENq2^5H^DaO&sG~P*A?c^TBGM*5}FslD*$ua%{@4B z&e=}gmaxjUjnvv`X?it_5RQao%EBgy0(rZVDQlmhnK~Jomp*1nsD@i$Duo`o*ro`^ zqX#^@@u;xIUfxkAqsRMF2JYGn5iKdzX8hIqK?#O1&NWn>Sy|zKH;YPd4Jv=JhaB3q zn)Zh(RLajw6>hBlff~D3>)P)=1el<~<$T2)X=R(JKm( z_FLFLP&cCxY)r?)6NGQIXg0z6`P&&K7xCaGtl5jnKTx%P4Q=0p>R5>$5#Bxc@FhG9 z0z`tBL3mM|j&Jn4b!M5*cUPUW0WH&KuI2%h`@b;xw<&l}okL3nwW@gq6`$+3U)28CTT@e4=RLM=sd43k;WD+QeFAo150(aT~x z$#7CtX)n<9Q1H*{ce|WN$R+FNcQrV04C5t_Ng#^$(2z^h^$JfnwZe0zO#U+cI$ob{ zZuja$zl7jCBJVJ zd?b8Gtgz!MNuQ5FHj!Dc$rs892i^`c%Me>_JYInn>}7eM@}ucvQvCmL!xQEoe2Be+GwKpB_oXfQ(naK~2gF(Z; zDc3nC*M`5`yd(iH!LHIG3sC5++7%+c7`;kdnPXTK@f@CHE%GClB??JqQh$&3^Ai;l zKENx_==P%);3Nf$O!&-HuuYTI`xDsjqa^&zg5mvdHF(QM!?UV&MZ~Nbp#yLT$P-4h z5+$T_?&!a=OJB?nRz8k`E@oS4yIff~vMSEcrAZ4>%EBh9e`fvje532ZM+G9{d1fxt zAJ_@B+v94w&-HrHs8!KPeU@7$zvNLEYN*Os_{+aG}Hp+Bn6*r zN!lQ0C!uX+8<(EbMkDxJgY9Zg!q{noTEhilF^#ew#0x?|3^FqkYs7xSIC(%DD}Ye$ z4yQehhT6_6zBz<16z4oaDWSwk?vKW=Gq?SMSP?u+f%s4t%`34z`ysaDl;xhz8u^Jx zi#BRmsog8}a7(`Zpg4c>+vk$<;{;p6?F##7!Wz4Eu~{4FT~_-o3LJ(U~NH&v>1ow2<`sH!jyRwju5Yfb-~CWuviijP_fcU# z%yJrZT0op$4(p1vQtrU{JJ(BcwqGQgkdIWZo5vd&WXp#YYTE)#Sey|EtFDZJSxU4} zW16jYC8$}Q{~;fYM%U<7oM0^5b6NP8-!M3pI|V=dd>T#j!q&K)%UjLZx+!`3eEKk` zB|n-kf*F(Bw@m*qbkjvAhIXZ-^VvKmZ78qWCmkx8D5V-!&B*?aGnbnAFb7GphpR!r zmRL=)_v8VyGYK*+G2ZQdSGWdv8g*)40@EK}W{Bn-ds6BesQ=X)SamN0o8S6&E!NQs zO)Sa{HRcIQB|)V)Ud6$^MuqZU#1CIS)2p@_s&TX6XUHo+)A%(|d$tNRWR}}+2|&Pw z2ot%~;QlUw@|4(N{%=cNvi`O1UC=Zkb|Y+=YdEXxwzdPL)a;MIMKse(k>fmVgeuKc zcA0#m#sy^^3Si*?RzwsoK4=n-UouvC662XEjrMRJ5~k`rd_{p!TB@z|^8C5fGsbxi zo&t&}6V~Ql(tcBeOxGH>lLx8cus~z1UHD;6hw9pBQ3W);|h6H{rpbKBbU2u?vf4l^6g`ObGvc%f6Kp~w$z1GnjSP>QoC zJo9trp$}3gtb<~>J}rILtlbSVrLrH_mPpMy+xkGg-YlBQUIla|W>!S7>n{Cu>y}-X z^DlSMiNWvFBD@~YykZe4+*HwaVjJZMndZh$HPVda_FX62SU3L;GXiw=pRm5ITQEwuR10L z4G^APgw8w)E$K)zzP5wd9g>h8rUX`3-%VB;S0qh6cRy@jv+SW#xGfj6%FwS$P#9r>aMvTQE*Ujy z+=)S4N|@p)N_g=ZY~ZXKIXxaH#^@c0z$A7JK21;P15~h>!M?}IR(0l1KD8jQ_i137H=z50AEJ?5kb(JZ>;+PX%e z{#z()F}_7frQUH)B_7>Lq2yVB8p#t%N=rWTB7Om%D%+;ve(P|OQ+8OFs@Oz_Pvy`> zt8okt;Wj{9cG_dcwfQ=<0*%|kn`X$6qLd{)9L@?qO{bgY?8=uJhr{Z$w-k-`}9cV)u?s@3w z&~AMPhAvM!#2>h$|3F2c)tmpjlA$$m9#!YCu&s+&a9`F4Vj9pI*26iPqb2Dg-NXmh$Ryk35Mf!l?C5`vJD`S8f7;24VZ5^nVzHG_ z9h{n~M>cGci|*%9-^gQtlB%>>MLk)dGvajB@`#(;?)r3;_N4qeOfjVdaTxbBxc&(T zZ*sNn2GDh`z%vx34+jn#8u=bYcCEqp4|AnDD_~7@(eqVW@=P~SrnU8BxmA7fmLDB% zZ6e1VISAorVg01OUosSa61f_75sSHwc2s~I%DBa1r&hw=3S~lnxv{m~$s_NsY{AD% zOjTIoita&1#OQPRS1|+us3)KLJN>n~EjB() zH`{Tm3gAFe>u}XUWzZ_3e2e1axp(j3L<3V7_#Pp#V<2}iCh?V|@?z&95G(d8S?J9m zZ+@Ue+fJ#r9C`or$k|xQeP`1eP znMFo0lb9q&THq1lo%Ga6y|T@zX2rB$Fcf*i-D`82Ndju@lNo9om=U%(R%{4X3PIl} zPAObxUIgG129|X1NfW<`FScwre_P^_W5-?}YIn;zCvLbmw0epBZ&gN3s-3?r{C5I^ ze?L1)`fouL8Zkr>;7#r@9(~89)QyFcKTP4P(uA=~sk7qf>OFPWH-zi|LBW4C{y)+D zf3ZM{iY_fzB-jV;GTpyI01kXmh@b(W<+SlZ9Fd%wR)qTpDx)_pzE2f0Mn74Xc@0iG zr~aa^zOZn-OM3l(Ul6mfeM8M!eE)>CuOinq71zjLGQ^G+Y5@xu)!zk9a1Onkjy#Q$ z=A^Mbp@4OMUwv{4I1r_w&i<-H$KEi%_u6tfmJcqsqs|2>uPv)VK7T`FP&tPLl*i0j zFi)_1!l2%5)wu2q0{jnZcPJDL$u_ja3LparbFoW>ec^|Ehnvm{nVIW%g%Ol_24!hM z=^3Ko*I&81B^BV~Rx=xi5w>_dZW5&y>4q2TTP$bQDNGZc#aD!#H8DzT zQ8;5=t2TA}QjzPSwbW?5dUS+@;bR6)jXO4l*KNNu$lL@L>Z3rkzgnM@Lb01JtP6bt z&ZT<7qE!UUgG`WF9PYm$1bC(5gd4J!;`eH1lbf6epGbC2`+%nY40YQK2v`Gl0QoEz z1u)d%(Z>Q8qJPyA>VW4nXu%8Czh)p|>MlkFuVR@#ZCeHVQUTEVhfsE;dfkC};Sth72bU6E*KDe6g_i5fdx*56Ozqs`x$!a5}SgDvn zUt2{UZW7NesW%XpTMRzi>*##C$%jGSpta7I)c0k z-RM^JB1DAGxrlM`tu19F1?Y1$RQO>?KVN?vc3`D{RudZe@*`k(B^+u!BtV;(vv<0&<|!K4fgX4bl{(qkQ}{%7(r9H;@-tRLze5MCi+1 zkj4J5X!bvAV*fYwt!5OhP2XniU4*_=fVWKCfoCrGnEUPMEvruV&yJOnuWXn5GSdmRQ4>~{AeOIos-bp*GoF1eC!?Wn|Llx z=KJIvK4uJP5x-5pEToXG_?9T^m**zIB_2ztC>E)GDe*ICuyculIv{gqwj<+6YsK5A{$`%;)#0?-C;BrbZwf zaI-aUk)bg&)QXF3)jup6?yS0Dp?_0|4Z{Af1zI8132^&}Zf6~i=%3q%O$TJsy11d` zP9A&l2B#rDKo_h)q9z|Z)rW!HxwhhXra4m0EHpPc4vh2B)e#U6>f=n)1u>l?CtI(;rd&u1L|niVWP z$LgmSlInN=#3pvJ`5!g&a5DR_cN9SRpChNWq=S$#^WhOy^`v8BJf(UT9C^4SmKKZL zQXQG09${>1T(MaTB(1!+`fB&ZqDRvxeqkakN|Dy=F&A#n9rHF$NY<5?K%J>@$>~>? z99M!RXck4FMWxs-XuIWZ&0^kwpX#%?7rkq$R3d`za9#|UpKZ2tWIGHV*@^=-zNEAG zpb#98Qa8?^^Xn;NV^G7JcW6ue9Hso5go||@mCir2>aP~Qlpbv!vUu56DjMPYd8tMK zR{v?{6n48456SG?vEcc>f@|-w9TJ{%q|=xhlP^ zjt=8fqu1{6iQ6LuuM~32y@rp51%>M4TkC4=C!|4k>fE+MT9Sg~G%9nRBoyLpvn%j@ zQZ(}jZh%hS?(OHQw%T*pv%!8`Ld+EzWU>oe-&Ju~v^^_YZ0BJLK1|+sWrPD+C&8w9 zUcTVcf8FS`XmweLNZvur`^Y@-j3GV-65bZSlVXf1p97iiC9%~EZ%$Pr9!164gJC}T zP!>lzSs-c%bOS8>z^0i)vPHv|+n_9uibwkt^=_tOPk# zX9U59ayNI{Z4GhD>;o;M6LdCVDcq@%jskYw9ANN~;<>iO#HhnmaEtU2+@JN79ZtFQ z)8UZ=10|y@x`C319Jfa8;kR;ujaD6$YPT&EWe11V5~=+Yb~dRjBq%?9R@E}XCVMy? z!s=fM;gsJDe-{!$7%Y@SeW>sAcBcF0TQz3ORIWux+1i^l+QhMn(8p(Eir9i_^Q;#$P8zw;IWChtU}|Z`CkHX3A%Vno0y}-k&NY!dC|-ravQRsru&6Qz5Bfcvy2Y)nZQk zl*_hUP3YPgBcD&KPW{Xy|4>j(t)qV=n&wBc6IfiP>qcCUnJxB1Q#N2hNPZ>X zX1C22UBdD*PN#}|D7n{3RB8ie>22rsPMt>|U6%I&7w~Q!uMqVmz{t@a_B1iuZdm11 zqb*$WtI^^;xX}z@-(Qu-c#X?YBah-ZE-5!gN0J)KUyErC1%h##&*MaEUrQ5N@vC&5 zAyHP79{yBa>Yb*~P53B>j7R+0rQoG`Z%YzCnESwX##T8^Px0 z9jEwr3+k4wgS3185=l>+Xh&Q|7C$4RDF!0gz*wGOOD3s_3yxdzG7?aIhGh+X;2bBt zHTMrQ7D+>0>%}UrMO`PJzn@Pvq&!`~p=%e29vWQ*C1e)eDUdzL_AIbz{cF$PfIaBv zh_eQk{HEz~=V;622RuT-EOz@@h9w*NhMcdn$C<&({1tM?P`MbY@XJVqYeGVqOxmsq zI2R)R1S?Wxq*@xcO0xs1(e!7ZHhW$YL zHDkT;sOFl8=C%oSC5AVK7k}@+%-+9Y5oOQpmbi}rJ0p~Of!=4%!p`CP(TG|8(7edW zNb{y!k|>a_^C!jdXX9Y3AQ3d%Xkwq{32$7h48-JbI472lQ^~lXg(1^C{qh4;OyI)Q z23)~y$&BwA{5)mzjq^F-;#T#1ABUb$ADo&SGkZy93-P~@&-%jzz|zD(7Iv|b^?gLg zEbdpNqG;u}wSS=4RUip{zWpk4w>e%X@QtY^Pv5OTy(3;NK~+v!JI;GbP4z54WYd%qJ7+phhquBB9>>ZOTNyY4%f81_E&acZpeY?g?_LO6vX= z`a+cfOdwe;ao2ArBs*0xe>U>4yuB}uj4;be%Gde5EfN4Uuz1E5?xqF-9EdL_%PAGj zLQ%uVHv3wcA{Uu@4rr8DDDHHM++Oj+#2~pvZiOG@5%S@DG`fkNrJn6!co6!T&Fh&d zdy)~{9iXl9CsBEXouBR$JPgF7aUb*kY6j5ER zIU;iNdT_&_4q0+7(gr*QsY(FcF0=+qj-X~p5JCtZk!;@!$o9wdiYPo9tWor$w2>V#rxn?s3GnxXzh_}BuE`{GRchf_Qe)OFF9pkr@1-^6th!Un7cJZy>BYsov* zg-OGGYq%LuxCaFZbgIJ?Z19*U0`q3U*$g`sSBkp_YH1dVDhqOlBp9W?MgP16L%E%Q zQ6aP%xLt&?k$@@j(D0ND;%?_+*VX+Qk}L5YS3P#hs3bKYNm@0r8g<^pbg?k%_uui?Qh{WMnCIO zo22@Up`9dHR>oj^)ukP7<@e`EaE67oYuIdzAcZ97_S0${b;{Vyos=m0MqY^X|LW)2 z2mIuJ1Ah3epP;kq9Bf%XS@C4y4qZ^grj+3q< zy8t{5tY#9XZ3G))4ei)|boqr2lLlDJ1$&Kbio*tVdDJ{q6|B#V=4Q>Bs~gxeSToVE zy$(N6SPE1*sxDooahCsqQV}emuBMm2Hu!%ydTvwkr>(|=Xv5xE0FEgkboqqBJCoF; zY5ZU9epm(N`w1b~CJ)Q7x7OI+Nw-9j*Qea8mT>2&g$HW<9T9#5Ce)N@+T}Fmn*B-g ztfLtj_sjiBvFz?IULNye+KIiIC6Jo(a_pM*`D4=4jxSXtCfGH}MrJ{L5nY)_cC)DT zG?4&sSgI9VV9{Sj0e7R713@#?4O)ri2K=nSgdrJJ*^zigCK_zDrzwcm3VB(UWX7r? z#p^Kf6q8jvte7oC{=VNgTqfnM`OrBHqw!(XDh174s|i;l@7<)qwvBXw$`pOtL?L`^ zqcHXu(raVB2_Y~CpHZvP+@>@b1GSF*A^tL-hA4JSMRFPpZew%*NiOhr_J%=wND>oS z6UA*NwqvQ0;?ESv-K5dE6EC~rZlm!YZX$A}Q(|J1)vGyi1EG{xMeaW(!+q-uSh`tA zTtkLag*6q+!lNwrL@p7nEp*Z%lU$`>7#H1MDh1SxFn^xWApR+5OV7yRt|uU;r(Iol znGw0S)U&Y_5vldgr46+VtK2Wa9^$2!m8I20H`9maNNCYQRZyNiBBzC|kwu6^1Js^f zO3f78YuC^S<0qK$=Hox*6U0v%KcZl3)Y(`r)iH<7=$9w+h7%d@!)xoI&lihX3@2tK zT|eH1dLu2*{Y>FDWu%l-xW77w;P?2Hyc#?PpgDm%j2t7d?A%`p6WVO83O1SBAh#qY zpQqBAm4j=vO};>Q?lLB|T0)8_+M;xEMQTlhy`B|TWKZK&9>n`23p}o~YN@3A-zxPA zBpD~U^6Oapx~LTd)hb@bUyPgJ4y!0^!h3$5CqU5>QI5k{a`VatV{)c9OUfqaRWSF; zf8j}4NY+iMO9{U-6RxxGB9e2QwYAjdP+An5A(E}P%HH<=a-2G7J3sY+3G(0@R}o6) zoRunQHQ5z4gB6YTZP_~SHQv*7Bkfy&E#HYHi?RHpRA$xm#Pn&Gegz}*>;2nx9n%%! zX;CF<{ZzL}fPb2fzKnd8A%vOC?X^734vDv}zi!a(P3&hgn_RIwGs5VBOPOD?WA*~X zSkZWEtyXaQ2g;}Nn{*HDJxA7}++GYX5wUhpc>@;Se0Pzdh6>E9QiIJN*Ia)=?jFmI zT?35j9M3qp#Q6j0r%SoYmlVqK1NII6#O2?^9nNLoGsG1DPe}DM=$6O|Q?PEh`U?wO zwP%wua(b)C9?qn~``&`QBW#VJEZ@WX1Fc8Q+PKir`qwOz6!r3Uz;S`z74r7ajr}Ux zwqh1LrMm2+SzC>CNY|lkQS6p5`X523EuC^IdBV1^VqP^ftC5_S9~Vc|*N|w9><*EmjW1-7V05_$4U|i23AhgNs07`L8Dh@R>w^Vo>3TjS?kdy`Ww322vRa0~K0bHP%M|$ZZ z0+)tG%fTPd&>I@bFg$L}jIw_-CqqLah{BwP?e%seAK=cbAbkz>k@f@(G8PwN8NvOD zJ*5rT5f`HR|AqfY2Em`(>p8G`4v)wVa7Bg9$h7cXY5BrM**A5A5~|}LC^7SfbA_WH zPq#?9w0bI?;1{}ct;oYqbm^m8RE}g@wd^Dq-UYA@2u}-$ZX`{IL|goFsC~7GqxhMd zM+VDLmJdl)J!N~7I5IgM22Nu;zcz)iO9Lt;5P_BKZrCERHLBDP4?>qCSyE%C1u_#M+wM+&NXJP<(s-t#rcI#|10d z${Lg*gBE3nKGWF4t7S$#e2@j#5I?DgPtk5MosBaPFBk$B+cDR7Q&02fwCT!%8Y|5& z=(@tt7zH@g+G5RaApG>ON{pPr^Jw$A0YZN_Y=ce8yE#U6LywAwgmC+^& zrAOp0D?qmCz(Q}--PlJCP1LJX8$pE|9K8QL(|UY-byd$+NKj!2b~;2&*?rv6MTOA< zCIeP_^U#||kO~!^M**sS4pVNZ7L|sPvt6xk^m#LyhX`yoT1j6Tb-aAd4hLm~=6`MU ztN;9#r&P#|_yUQCTi^~0Hcd5TYO+bg0srxP(u7gKnRW{7#cdtS8O5rA{ypD*LE1h5 zLN|4#OwrzeVDgD>EaC)crdxT0 zknLQpR6C&7Jg4z;bK4@9LuuwVy#WC^Q7nJ;ZHm3`(MqS6J7U5nFA+x`(Pn*oOF+}- z)hQR2sY@H4hFo(R*^kMtcFQCzCc6i%aopC~Ex|qJN8{*NUn;~;8dSR5G>Vi!LRof8fZ2pIWs?-u}|{ zJ9*E5*j+SFI-gc9C$#!0*VY=^(nyTrLo0s7rfKNtc*}4smCph{6^v5W(Tw#yWq3Z4 zR?xGd-}?pYVBhd|+_BEjI37zTR~jx^slmx!)PSUGUbK#PYTYjYE)r=|&%{-0c|Hw` zs5UM0f&sGfh5SlCSkRR=|8=AZdu%Hc6FoX?O(ISjHNSbV$ZK@se_Hj~jLSTW#qEKF z?>SK`&VhvY@fVNU)XB!9QwKluhTpC4s)|~XP436vMpXqWE4ei;>zBCAvLVEctUkWP zlAv`L5cr9pGI?KpiL;ZjQLUXxp;t~Q8ds-&84*1clB%TWxUfl@;nKsPAls_f;o;Uo z%j_=I)%{Du+sDibV-T1%^~Nd6EF3-1GmFTEHlXgkkW6*%#Mh0mqkx%L8+Ce9BSJde z9FlO>qjC0VcnAabMF#1Yce7g`40G@IGQ3YtAYPzi$_PVDGFfh9^DeKQu>m5=lU=Bo zRRRPb`?H`V#(fVQ7;3p0lmUC|-#%xtWCcSmo+Rb+0- zd%SIDtg>MVNk;JxR9+I_63x}z%S@`1)V-U{ zO1M4lHXjK4%M_2BY=6~c%*%`uZ9VCv}QK=e9obgl7p8jR=2^i)qDCqh5qOY|u5 z8G~v?G@XJ$Xc6z$ek;zFc+en?5M#l-(4TVzwfUQ1Uq|Wivp(-oNL&I(pLANFg|X&G zM#mJ#b7t=GqaeqvufwdiOyq*0)PObpg~4o z{_@)podo6lnV%=RXbh8Jl4n-C)`L7s{+>sjSJBvlj?T!ta6620=+6n9{JiYhK(WT}{iVNf+1N^Ua}Da2_fdv;Vz^r8VBMH#BY9;2&3VXvd{W*Wz@AFk6KH7l0*_{=&(edj$v$Q>dr%pAt%BDU;=^g} z7QT^-)zNE{d@9Frp>-{7=u8+}ASuZdu{%`a;Hg`A!3~Aml;5QLWfjVkk)e?MLsFj} z&WRD8MQk6TD*09h+jA#0_Kvwtuhnv{kNI5Iq%t9WiH~Gu`GqmtfUUE>7L}~zg_0s6 z7H=YV`%9e-3nwZD^#>|@=Ot*BT)?NoFqOU;F?q8eUG6|SBcp$f6~ox07SO+dw-k6S zR?9Nx3R5LvIjjV;s})9vyx~#P$Uit~9Zt+=W~2NMR3p%BcVnIT)bzop$s>KP!{mOs z2F)kCTz6wgsCLM;Z-cxz;e^9j>8Fi-9P{mE=2~eN?f~zJri&@+D8bge@r%e;nGc?w z@hd>OQxiVvN{H{gO>&ci^DH9K2w$8vJ}tZMmj1Bt4+AUA-xoE9)1gu%i>TJL-@Iz5 zWW^j~N3LBejN$ZvsJ_S+If0~F$IU~zN<+4~+qoIR305(|f*^~#9~QtA3-&tw+h*85 zRkAO{(a&FCGkE3q1|8|Uy8P%d@4cMQlQ0ip5yl4yNVmD_4UsX%2T>8xe7shP(VO;| z?7d482B^MgMk&sfd?SjLiE~`cT#1HRj^P+>q{7q4X?GpJ>;yfeTJnFB+t zGGN1bW_rK39$gx?D4$yKQOx?rSBVLVah>sCs<04G6nZeU#j{dU{BAy{`ko>Hc2zx8 zEY*cgo5w#+>kIJZ;nPQN%WjXAZE9phh`;GcupPm}a&;Y{1aI`Pn+5oeHpvCY!rywX zhu{|sadQ6NKi?#e00H2Ss!OnX@HS`Pei5DU?)+(_Jp5yhyG6J#O1M*Y+=%x2w2EnQ zFc>i^t4-@$j-67)#d7uKTxyLGOAQT~KHAtd^_xgA{sVC?hH4!^vF<1&rPcl1u3I_Xpg?2YV5(iIGx zVFiJ(_C9Pz)Iu!EjA-WUzsoTXx=9KXt-jyon=vCiI&70RVIH_F*1so?dC+wR&fC+C zYWtY$?uxl&hm30h7-9V|xE9%tbratF7~Ox7N`z4Ttc`(cvT&6lN-#tl=WV~gtYqjC zKhSl!K5-WQE=qI@@hsM-NVu#+HuM>bsv)!QtDm^1sP^34KL?6tfrwv6-cEzgiSM{Y zKMGA=RDezbgp`Q+#hZuCdi&nf7-L2A!aqEto7sjVd*t%mNOvOvPbw!6b|TO3$wBVD z5f=s2?l@pVxgW7%&-2;iieVBy8pT3uXDEpvhw=cd6+O~Bj1~On)g@6;7KN<{$1tdl zj$ioS0^nF(xwcvFN)z|P8q!ghc>FT)cvW4bk9aLkF;wFyQ8#oz$NBV4Zt0I2P`(X| zq+TrAZ7iOX&`-;Ds5okvk~8?v%Vx#{7*;zarPbRzj+*4Np;6nssMVcMt7E)%=M)-K zMfvZ{gJTEEzpiJyRJ_2js5G~s#TQapEAF;j4LN4X7gjL$#S?zEtN70=Ib#aE++usm z9%0$rJJ9%*pLwl!Rf_o~kag*Hdb|u5Wg};!*o52s%M!CN-55dZj<~*8e!tJ3bu!!* z_h#K(TPo7o&}4;toU7eF#h5XXjTa*UI?)GhiS=+$6YQ8&uk^ev?m}^<>#F*aR z$?%1_$)+mesyzft6OGMY9EbFlWgjA}a=K9shxCHBKDZ;ZoU7Npnek?UV&xZ|C~Y2J zPdHd?)mi`#&is8SOk3-Dx!XviaCRKq4%YD4Mo zUr#Xfrk~JKoElx7g4Viy$W?x%mPer}zwkCdZe>NYhFVt#KvUkwX&s|=v zZ0D}|e_c2=9vw?MtFT?-812gc15=&@;BvbkKbZKRXjFGhZ%XiB?+vZUz-f_QqB#4n zjW;rkU}vR#gU4~b`RW_-T>LjMipsg;!I}p}V20pmO;vnbM)w_a`V5s<8tA|obmD}Q zBG0!Uzh zeiAs*JNUTsrykg(NE>mCRZ#wgY(dw@0=3OwGp1A5wT#XVNgHKLR^DOQYaxP`W4v-^ zdJ^#`m{5bu-A!#)V^t&r$RtkZobXEN*#Z7*xYKbN4e6+&*b|(-MKgVSg$a?p~!I={7rUc15Z;h#~`|5(uE|hz{9g) zOV>^>mp{8tnl z^GU3dBmdMU{ed|_BbF|6+eg>sAxReAfrlIqHP-P3BW5tjVJpD`fhqea)=KfTtCMU& z4!9ngM0Fy&!rJpK@LUJ4JMTAs#mI0Em-XbrUtSw43uAW;kb=NhbKbwkO@8zqAj;Td z#)ak=m+C@Avn2azz>1->(C6hX#eEsj-14v`?~68*Iqk+*lIreChdp^7UJ`T>dQI?| z83l1hkIYi4hR>SPZ?!KAEI+Wi(_5locZ3{}DF(oi{%elc?(3T`t6W_9+rss$XM6I6 z1UCkXQj9stV-A1Uchh%<>+{5TWN3|Ay_VI#pExia>kc}vZti|Rm(zujg{Gcyz8g%H zyFuUw8lDkVI_;SzK)QXLbf(LcrB`Kq5$Zcp-0 z2Hi!-0WkH@ziDuEdazJ>gk0-#H}1XF`fRjN_n$A4VjAdU(F(O%k+%N=>irk)-a0C- zr)w7^0t654?(R;A5FCO8X`peZX*5`XkU)^&?ry=Mk;b)gXe?L*jazUDF1h`CzxRIk z-kCLPeQW-hxikM%ol~c(PIdJ;r)ux#*?V7}vv``<)zGZvj14uSWZ=-FFz#%&I#Xrr}Y~8NZZMB^2YX*dcPVI$0FA8u=(@_pbb_#_hpzLvxNu4`Rxp^!G$B&YU2J?KT{>e8EOxB?v4* zmF8dKO85&gSYc5Izn<+qJoXnaSX%M=4_dqs_s!i#>cMb0X!fZq@3?%%o(i3km}DGx z_U8@XFs2KOfj(i}MR^<649{02tdk3I#$=qg+a}lrNxrQM8!B$xt9aWxW-&ifyUhm5 z$$BbEx)WD5l9l{^EdY6|12%I*HeTs8CEHsJjm1gnAH6n&)egk6L`Jx~gYae3&z_2s z2&#r6HiR+x=+iY$riVgdj=0McJMa@)-G~%xaGQ~|%`c52^I1(&#=!T>vvA*8S~J+e zdbpOL_7#usZ?^$^B6lItKb_ccI@)|cL`-GVjbU*PpL&7Kh(G5rd$n}31_Ze{#74zo z@IJZ2$DQD7gRFppDRxPCVP&|k z&DRLuPRbOTu1qZqJyYkS1Ky3$Pdrvv{;nqAb(Pu zH4`QtGwb~A*|rtC>peFbYv>p)PG-KU^-6ycP{+vHN33mNP-m8lGqkUt(fSmAGv(F8*!y4u(pn2fT7+o zwZ7!Y={Z3xRs5D|$Q(=3`eCGRd2w2{|SW7I8d)RpDxME1w=X{DXhI%~xF`z*0`zW4OJJhKp( z%Cn9iEmE(oY44QkjY_jGQcndPX?h@4nU1W*Nq-ClBSyl=JQv(OI@TPBZ`x14x&3fI znwo1gR(xJV7SHfbiEiN{T-j0{w%(UId~V?7HI)^cf=2g;v%wKZbaNdIPoG-@zCDxPu{{0B zk==G=AUojY%*IZ@MXv1VRNM!!znR{#F|?JV%0P`Wccl4SQ-x|IpYkbI(!4VK#;_oD zEy!X6SkC8<44I)3>M>M0j@P`LUhyuDtGRJiulROFA8hM=37u3%ep#?*aU5GnCn8y3 z4HsITdk&Zywe(?97%fd7sNF71YWdI?^%h5Nf6^1g_ls-c9)yl>#q(Wcim{2exV;Ag z!^6wA;ray@e##H)sOG`jp>#VWQ+>AR5R{Q({W8WFY_R;QvR>Qk-_LV#L6V08F26x> zSAWV%ma1rs@sAawH#}&=Sii;XOHXc`8>}U(i%RosqP7w~`A`eWzv8|xA3BB*kCUKc z<20*}QiHyuLs~v))WklRoz7q)@eiAq#KUoM+PB2ETJHzk#m1xF%`R z9JdD{kUQ9=_!aQ0TO3h2Vb3ZRgnB>NrHb^Qc5DA!`+w>XU!D5{U`p08p3l=sY_DQ? zQ`~q@Mv2VAt-a-je4_7(u&SVHqCeVx1<)vv#}D&ZN46nvbWD|>NQ5+8`wlZ*bmxM$ zcNY5$%an5`n(3|`OM2jZ^TQ(mug*|@Nn*N7sn-2R%j3vBK>^8bVIn1ko~|~0nu|yD zXm#fcY_KD!OpZjdFRj3M^P>VoFwDtfuKC_R!Ima2A%i*MtNnHyk*)J}kcVyT(r@Ih z^788(Ca<%KIyEN38jv+hgVM&bMS%4q<^<0U3uk+JinVu~I0Fq=|Hl*LFv?r1NBC?) z0M%^6L)ta{TZ>Ks>cvUZclIoSq?(c;jANLMj~C$=CJov*i=DyG<9Q1j4*fBX%CdnG z%&dXjzumx^Lvfa#qgACv-GAuY+}HySR=OU8{jofyZM@!P*Upl1DcOv3ZAt0?oR2Fj zLOwWs)A4t(yEzU?G%79Q$j4<0bAp%tPb~wK9@d*QEn)xBkHfm~DbkprS6@=)YB~m= z*={m&w3)Z1Fmtegqe)$P#bnab1gX(e8=|vsEB3T7RLn7C?hnf`{dnxeI87i;;m$M9 ztWRpL(%-6d+eK&9PhA!+ihl(n6-}<1527ky#n>ZxF9Ju?;=jxUx*Qb z$Kp06J9Mlguw>$5{4H7Zlo)+-4*Hv;96dFl?NA?|xuXhMCcCXrLY9gUN%TfE2_2Qu z53C_JCfE}Aw?Qujes#==PP})@^_W?OsZ0DtOAao9(GazbR5*OExz-!7xiea1ZCq)o z)xeE~LgU=h?!2fv245|3ykeOe~pN8vU@Y3QkTWd;Z7gyzLu1a zT{u$5D1U7NT@;^R;hS1oyfBTkaFmnhriIz2e);5wDC}jbL_j?oI05q^l5d}Vlmp}R z`3qN;gCGgkmuE*eXZAz2@PzdK%Z|W{9|>Z{;UNtTD-N*aXzy*%4pC9c+Q-pWEg4B? zPfGQ14?+?RP8vezcQ0TNuHpfsI2D#ar4joyrFOh}A$fn{Qqp>$Oz|uC=VB#Y;R<0* zdY_EBfUh&#K2)vZWrJbRKYoaum*I^zh=u7QOACsv=XmOP_57rN#-Np@A#ceSM_PBZ zw=4uX$49zT_TQ5YQALhDyeph9PwjuW8>FqTOxyzFHRM`_yvKYuEdTaX8jG3x4?WnL zk$i}PMm@oh$wmd~^b4@W;EM&Ar9 zqPCn0jVATb0S*LgNhh-3kILwDjq=kCY=CvPHDXY6gBfv`l}f;osJU^wA5^+eom|v> z@Wa|zC;mT5gW=v@DFZK1N78qzGN9ld(ug+!jBy5C(XQjv+)3A2!lH@4V;sh3J*@Gnd=TV&W4ZQ3-AQ;iuj-H^imV%x4v8Iu}uvCC)YMQ5|- zjXi78B+=69FYja4Oy2upxW*NZ4qxJoZIGUD~QC%|L$noDBgGh>UXv7ZPnbMJKnL*GjXVYWNi| z?^dXBu%(dw!t@B#jF(!hcV5wEnNSVs^U3ZLi;pT0KQ%JaqsL(*CdMn|s}4_;JbdQi zBeB75U}5KshH;~xFc=lA-k71%W7hIh_F`-M$M9dYizn?Gr}}XaB3a<6`GO+5$!#~3 zlh)GDw4BF@KswmR@>W#_ek4!fC$^CFA)O@7MCN*D2?l+OMNdkLf@#N#1?^Q5VcwTopl zLOq%k1wu62bI%fnvG6V{gIu6uD=jqw8x%LUgQK|(Nan-qsSQoxRW-ret|cVe{coDF z&dEF97uB8OZ1fv9RMZ-l9D=W1t65i_u)ZS?FJm6D`}w3Uy^Q1QfC>Jaeq3rgni{ec z17>>WwIfdDZYa+QDi$^3pv^#;uFF5c+&-K0`@D5y%}NK;)6Wi#znIMm^gKN{3_Af`fIVQtvKlKJVjZ zYYNFzcPcMHKzt-?A(KMJBc>)TJsh4m+mIv7+P1{&cOU4av7U;{SC6%;-fKT9g#4Nx z#?@E?4S7=snv47TqikpcA(r%hF-!+>>6^#PiaijC#6X{uqwhAvc*#Z{hH(#r-8Qe1 zt2z4Hg}!|~!1TAUpp|5_)|v$kMa0hsdoohDX=Tjk~aM+ z^qU?ZESP$H-~MpiEAC(7f=>;m+~^P~Ii^5f0lDo(ykO<{-b|y?+~hx!jyGqcD%{-x zM(OB21kANp^efez-IR4wN+pdA@O^UJz5NL9{J&FKNmDh}k&#Hb@a;r?$7k)E~zxsYNIUQG_sWwNv>%YUS5s zU=d$bFb9=R%>FT7{%0_i9MB%gnEOdz&Ava|csW_UlVY>={~G51lJM32&An{c#A7$S(M;#Qn{Ht9)J{_m86-K(^Z@OXBP5yN! zRFaMUL>E5)t?g3ppP%=iN!M)BPe#c=CTANNZg40fHFX|pe07*h+lYs@#Pt9S&+;AT zu%6bj)o57hne#1k(o0;_$TtMhxA^c z{@X9Qlgt}H1cldemE+f|qk>j}1JN1gm(pkdbvsmI;I%x5YEm;7kEmWkRch3Y>du|}=cu7#4(Y4+PraG`tbe-w z&o+i9qLrW_CZ(eMSQNVTva5pV=7+xq^O>LB3b3{GoQ%aj(ba} z;d@n(GR1250FNwQyPXFD<1_F4cCb+o=JfwlmspOY;0yu~ED*@N9)FHJ^IJaPi_kxW)_I*>xAW|VC_XpUdJQo@fL^Sd6LjDwj43f(>^ z>SmDs$Tmt`bPPz=BcP*MP^1xG$WP6LjQB-*FC?CryL^5iYXgnVc|50(5eX8VA5M$F zFF(1~V^R`;jEDL78?KW}6$a3W($S}uM|Sa$eF$%FB*^li9@5$(R`NWeSB0irWA`hQ z4Q<1ef@A<%7>#?<`Fb)CHy41t6mF^3f62dTo^QIA?wn`CtSoU zl#F4bf6?SpmOD&?*+A}1*6nJJ_#e$be>88q(2Hn#NT;s!pwfxkGD2@c4tDmv4I9;y zo}VU1!^z@op=`1yA>sO?h>d1Feg`wO1}{BuCMELrg<3N$W|dY-l8Ip&%M;GXYqarj zkucX!?%J#+eq@B2wUOUUy)0Ai$WOn->xyfMI*=}Tq7DKQw(*uJo#Ak0Q~xPRVQ_@` z|Ngq_Pev2|dEdi5wK0H6<}h8=S@PW^`&+enulF|M>{dU|NqGi$t2u@rOC1$GDpjFF z@LwzrIkBiW+U@PV<$1x1p4MmJdv^*edYkXH=zQo^Bm^T9D-74pmBanfgYK`rovU)z z?+ri!vZWJUsJ z$zQG}hReH=8#^d1TW)WiKOZJoCPdo*@R}YyuU+ zbtjV(EocFJr^%wGgPJ{Ud247K=wz3B?bV9p(2FfM;>3U6=^_mpqSPsmAqEDJJ=JR4HK zVNXU91f%3B2V>2qnYoZQCRypxhgC}^+t^>de9)yH5nUEeGtlJhG}-gEP9^!0RiKs8 z_RYkZWHuC75k7m=gfyZkzR+({ly{M?64#qh_Iy}#J*(Ul_pbUBz-)BXzTR9#z+o-M z?9CLyF}y*-^S*JVRmD47H<@i2!K};P(NnM}W=Zv8&Z7boTPZJ*>!;#ODfbdskx;6B zOmgH4@1oj~)2NfiuKP-s9_C5$%G6v!mVt9igsIQw5=it0`HAn-)J)qWX-5UYnqJ5L zay6rLO9^B{MS`F8+h(ijn*8!{`&FHip646Ndg$JzXG6VTa6PAZS{1WlOyA$~I_gsZ)$XI{ zWCi>Xg)*mavuRz=$!=ag7KM}6*i$fcf>m(<^^Tg0b(7oBqbgE+1(QjA>OR14gA~N0 zX_+kM1TKWAIAjugIbS}Iy#=GS5-!rp5|wng*Vl{&bu}6?IW?R<$6AFAU_D89Q)scn zvpx@lNVm8oIFC#vEjY$k*2<^+oa+qbb5h0gQS@S?C*1x8oe;Dp6c<8z_{oyl4**@0 z3vgYOSqgM?SVMcD((H>Si!yD;Nmkk9!H=U6iD%$`GE4O8dPHWYRE&s|SjN=;)KRI> z8ap_xZb2B1B4UMj7f0LG9C8gLk80q})MZmL2GlXlPoZ>#}lY+SpN6t67Lj#rPU7}R=gixdwn^62I9y9kkr>YCy5w7&>Alxe{W;>V@y$yhb>hmN1CiJ~xT9&ETR%ItC5 zJhR1mRzN*ER6`_d0rx+UB@G1%feo=Xg`OM*V%+wPQ@%;H9=%B)SoU2jPVGnO%yZ z3*yuarKmxXNI>%j2QhAQjxgY!oA9aGR-bv+H}}v4mT=I>i`0EGh`xJY2JfTO?C}+;*(W(9kEP6p(JU5i-e6m`$N9clO#6<`m%BStmoxzW{`s9H- zrnb+0LIadOsy+xuk;m|@SdU5n`E9Qi^IJwwYM?;I=gqwaAKR#W@rPF-**uK0uF%oF z;!(ZDGVcP{2L_k2v>=DalTPDuEPR%~XolMTy-Q^$?PdX;8n@-N4|jjjW^OU>=*Qz8 z=~MOszWpKfBz!I4?6LA;@r8dbrQ1>AkD7^K@SN_7Vw{iEyA1eg{M!uU#58AhX#WVG zxn;Jt6PKyscseoe7wDpN#*$_h#OYTQi;#P0|Mxg}U98>ktZ3u;Z_ioiF9&y{j&$9D z;o3&!-u~@E%$G{B$eRbg+m~XzqtBE^Ggp${FS{Y~FKu7TwU&kPQDoho0YQ2!&lZhE zZ)OkgI^VfKV7OdeShVCh>^_f(AR9&nxUN#+HsTwvAg}qd*8OvSpqVe?Ajv=SokkZ z+JAj||2L=^t>fb80s4!^KT(}K#RE}I@Z}O6{Ml9 zWcZbP<1B-+*a!9Q8hxr9o{YFR>1||Q>WimJzt+7c*wvrtdv7K{NIQ!34}rA)xUg8} ziZQ}@)IEqy^Dw>$_G{%CI`}f4`>>Hf@UZ^)3HcbD1TW7{IjU5-3g51A+SoXcMm8;_Q zCA*GR{?WfoecmVB;`_~SL$07oy+up%9^K>kH5|AINWaG0n2usxL`64!9e zxn+v73Os68o2w|532Rr`)Iz|SOo5f4$yjx^i9JN)tgQunR^xw+{x4cCSzM#%?0w}j zx1aCb{xU7J94zZ`NWSxszB%tXS8tm2>G-6|LGTy}oPO5nPv@zH0B-Xx2LSE~@{zR> zer3o@ZF+MdhIDU<%odv|1}G&Ku_I$lHbZxAv;+Wt}oAZ%KTEc$D}3T zYqi~q)Q+^c&lYxW9c>rB$lz)@iaH!x=;>OYe8=?9=0c+me4lofnF0klq2Q~AV0cJQ z(Qvpy%#+>Bu<=CTyxl82Xpefz;xPF9547@m1o)EyUt0?vayt9~=Loh4(8k>(*Nw_MVr1v?)rzKQBS5o%vGq+Z-NL z2+#AbQHSQCRB?VGQ1LhjH#P8RWU|CW>68fUbsM~dGI)S=xU*<=O5!#8gVO4UPzLl> z_MUl>UOUgl#3~?tQsa;a&aq}G=HPO?@q|$pn9q=JWY?26`#AvRDL5P+&c{z^#2Qr( zz3mtI{iBbE=s;Hhi|xpRBR@PbzrMyQ6z)KAdjbyHd1Qd3_;`vj0UK4q%(F z@D|w-oP7wK!5~v+4adq~ z9*UphU)X@JC`b0)z)}+XiHIUFN+$$XAdLv_(K5^bSv3uyrrK22XOgd86{|Dt&! zy(&mLIx%t295XG+;PlRTGf52n%r@I2oFA#9N3ikP62>o+1&uONsuPCk`^e8r>JHzI zKCM@Kf>9W}#pv0m7PEqi!h_GMex=NaPT^@-!)`5m;Rkh{8}JZ~YWMz&R{XQy7>c4x zW8%F0l4MT>4qwIKt&K~~urD%mjXhk%k~EuartCNNT9`?e_-Sw{Xe~L%{@$g5o?V`e z)wNY-_(-RJfDfb}pF#2q%KdVL>qTV}26%42!t3hzW@3JDa+`ss&HkiTrJon7DGOJA z&g3p63moeiI4KTdV|OIGbIdNgoT@qG@h+uu^_yUw!2~-hFUMOq9hV}p^bC}rd(Bem zKKa1kwgJJlq;|vQ$;f|kgezAmzEl2W#?7Vaq zfe7ZvPGQ5IRN7?8<3mv+kTx7+_OynsChBUlyO`u)0dQHh>I}a9Xz3coIL@XC0T6kx z6^)&V(U{+_sT^5wUU!dv7Q{Q0?Y^~ZYE*BQNygOXEp!`&4Y06n+226LIv zxsIZ235e!FSZ8Ks-Lo0M!(X(Mzi6`WTqV--eE(Pmp^)4X-I}wuA*aN+&DUuU1YNg8 zG}N)|prT7)i0C>pFra#%W-s*ftxOayESE z=3`M%HB~*U`i1%6q5wJUMez_1OKW&Vh}T&xNP2EAT{FnY51HEH>&#QAd2Hko)l=A| z!zwiU<#q%$*rv;xBg;Lh;sG@)Glel$C#sB&BLn_#zr)P&sW^~1S$uBF*PeQKVm>m< zyh+D?i$k3o0B8NW`L3FM-^D2T(7v?~405j21A>v?ra$j_v%)BA;$P?1d`70#Rr%dk zyDC$*odA++gVpm$X)(XNGoeO+D0g3|ESSr288bE`X1zqsTTy8m8MnimvC&i1z2*}2 z+^xoDLB0N0x#u2*sfc=0!t2bt*}ctdEHh!2A`Q{Ag%xaNfl~^%<2qUJA2tf$cc?wa z)J;d?wVl8=iyPKB8_J~f`8BvS{Dwx>$Ru6hwEWQBV2N*E=h{($eynCH79qFE82Zw| z7RdMBk3F3W%%AA7ytt1Ogjh5iLmXFu{5d^Sr7fR>=$2Gly^a?7C zQZceTzMof+XO4Wo)aGf%tM;W`{C!%tMobiWu#G9*IO_tXtWMPGw`#0duxeKot=(~( z-7B_t85pB*jr=YR>!}KpepbSw>*y9(Sj~@ML_cA;?@o363-+G+44zr*6uwSu zp+fzzz*aFQOlX8p9>NeI);a(_)XX2Ki)OSQMZ}ENiBa>he8tVXayqDBiC|H*#n&^= zh5Kfc{)phc?7~@5rJiLbNWQc9Df*{D2^(w?YcO@$E-y>POc)c?;X9KLtLOFiED9Zl z7!-z|Z4T$ZF*2!A&!#5j;q={7XaEZm%Z!6;+yf^5*|+_P_lnsWB(L`RH;u*m+%gnf zGTugJV+IhiBeR#*5_p_jXJaE?izl+5PxY!YE?RXD#Gl~@Ubc_=|9NHg z?Bz#0fuaX{s^7{#5{N^41{fK|4irG3T#*r%S+S<6q7fd=3gxV9?KjU<;4C#w<8|N+ zQ)a`v-=~7>F4s*HU+*wv7JvJGQ_J@gsVf8H)6yLBf-W?Bhh!*}C=k>yFUOG%S4XSjyChoUf&>sw{4OG_Az3IXND3v>WJa=YgsKg)Fd^a(x6L zZpsJ`uSe6gn}QHV&P!n%+4YkK3qk&fKu)<5N-i;zLwf{Bwgr2_>I8b>$uUN43c$(y*6=LY zN7>4;l82Jm^QVAlegA?ozL;b-LraN~ej<}I*Uj(Ls$t#?z7HO^mFh8mdiGPKP!a`a z|Ez|onV=9q(=?4>+_v2j?m*|BpJ$Fu?eP_NcBj-rx{XHOvuR{ouI^)=;;W4^5hHO3Z|~%fzD(i$?%ViYb6jl)X?4juRtsrmJNzAco6lTQQv+6L%77 z)(5UiJmtG#@-H@l^w$^3Ya7&)(x|vQYO)TPQbpk36(wC3eHnxv&;CVwc1!xelCn=f z)=;?8-%uH#n5}P5e<<1fz>L<5u>>B7%T?Y47!^@iCf;*GorY4a9gF8^O@gPBURgi^ ze!!Z6Djg8ELJl!cMHUhKC!>wUsCUjk-R_FSFI66(OV@CcH+_8+A$=0f`djoWL51+c zWc0cQ(=8s;2uZVeZ2>UcPXP$wXkU!+piMtlsQ8peV<}8EQm_2_>&nEJ)4{C2XmtV4 zcI99hpW{+;D--Ej&OMfO-35zip1Sepg@<*SF-fPj+~HYz5%2Z2>C!>T@5`z8F5?|< zX)W&d377^T_D|Zp{*zyKOUBphVNyZyZ5j{5-P+v3L6SMn)Fb-iU`L3467q9ewSl$| z;pe2mg1Yqg9nFSwmBo{nIZk)`!Vu4W%Ye6c1593z@PEm-_>)q3SK&wb@8Zd|CH_TM z3rRg%|zXOiqrL zhDK~a0dTCp>mzf7PzK>3*7Ik1`MVgJU{ABxuiuRpeP|tElBXCwaw|Ff8UEJxoo*4#Y)D6ie==S#I0rfCAKo(i?k0lm0Cd^MF1L- zjw0?Db@mAY)hz3IJv^Lrycw36HDV)kfC^7yTAmXdxELKMBzniooOHANMv6IAH4#jo zHg;S>PmNE`a%^Q^?BPk}gmb??BJuI*cx|{DZE8@W3sSi;SisxyXDV%N@|v5I{98v{ z@Av*JAs?BPmz?a^rzHFh@$(E$BqUD5m|92KrFCSKHSYApWw9mQM(9ATxVINnz3W9V z^OP45HfU-S~}+^MFyySrT&{aX&~jl+@?Fg}oP+;1t2I*P>#&v0gRmS61|fWTV`Ns1ml zzCIR*3bi*M!dF%l?@%-wbqiNkf42`|RQE4LRLw8rekLfe+h#_No118yeU~s!R~f4{ zABk^Ube)TG?8g?3g^?DI&CyKuTvqQUbsrQ8K242dCeC03Z>Z|W&8qQduDeRIE~LE} z@66+2JT$Iezw5uhLEA4Ca3--U%Q=pU03r@A`G5AbZRLMDS52GY z^V~2-D*PN!{bmr8c`*B`04;xRzeJx|jL1U%*S{i?p@;5si3Fsjlk^L%Q=u!{ z67q)9%JYaVm`7q*5{+bV4u1f`>a`sOK;NOw#_1?iG1yHeKNjIPKFypg z4Ho`+-<+;lUX=h~pla4_Rp@C2Kmd$E$^6__dSO+tLb{tyJ)7Ths>2)%BAV#lp6VGn zj;A1Y$Qk;L!xL9-=yx~rq}M|~v327fuE_Q2TXFbY6itoE;bqC?{X_YAIVq#bXQ{7W zJ{jRXg;SV1ut?6egI;fF5=*_A?&iU;?d7Ud%|^I;=&^2pwJ?$SFyKpyJSov)9*s## z5L`|nFUbXv;}&N2QDofj89WzU%N7y}?!^+0$qG<N7o7u_!K`-@(Gtf{#4+`$mIhA3| zusWF}2|+fQqlCAKuk)^+lwv(5{d=cuoI5h|)8o zt|HBDhbrfls>FRHyX~~OQ$^Og7vVb5xjuT7F4vwOZ?aLE4IYsV_01(&?y<}2tW-wL zpz;9Y(BZZWTo?0GVblh@F)}L+-ZB#=RN-BpRX0Sl@4-8VP$Yh?+3hiCE`MSDIZQNE zL$s%lgrJ^~iKg&!X*$)rX5Nv-&B(sy9nL+RkMv~TPt8tQP6$|Z0lf_&hnb(MVznt) zi)+M@@<~nB$0v_Pw#~vz8nEsMUSI%QX+lv39dfmy1kbH%2e9XXrz=|Hz80T_cpDz} z%<+k_o=2wPPUgX#v&!r)wKQ;{aGDXxA`hQwcv2O@S+CxTih3$WC*@`20QaKy&JWNc4u{*Q6_Nl7nfJYNg}=+ zke4HllWL8|3smXGncVfT8>lYEa8|}1E+bA|a9U!N7YgE2C28`Lmde$T7}-b7yEYAp zi+h32d1czdCr1a01zcHnGiTxtDV#`4#bInneP3F|zQ>A@`do*@c`dnp&Z{Pq!lXuF zh@}G)fFWMh$Z=%6C|t?Bm+O;fc{@mYu;#0C-cPs9cT+18Mn|2xvaBDO<~jDoHU{5c z14X8iMs{$s>&`3aMtJ0K+Ml}0C|A78l%-QH>I2IlCla47y}l`kl*A3jH3!3W8!FHh zWq9M?i({mt2jYr2Au``2kNG+%E33BAImtI#-_ByD=S<(ywjOstDAxnMi(`i;h8s1J zmSq{FBm(^^iHH(O`M#DLfpz&PPr|S{riG2hINo4bU2(jSd+BNIIzPYE$=2_clM1D! zW!GQq9T!x&CRVLlUs|<(SVZ7|-!{iy6g>R7{hrlh_*d{woHmEjR`iE>B|bbcCRekz ziys)FlkUnV~o&-^F;ZA&^$ zS;-Vw{G&(=jf3S2xjbt#t{3Yg$qZikThuqaJTKMO+zyQ;XQ&R=%CH&n&3a zOP$0^PNkETdcHXgN87DO;4#t6g&AHri^BSy-6l)P?;Z3Ru?I7RNt>I!Klasz+of)g zaCAJj*za8V>=Q-D?H%QXFHK=CxQFa77xfO3n#j4m1h{bxs;o1h>wBqx?jR-M4-T{r zzfle8Xxy)Ca|klAZidQ8$|W)4hfbJI{bTXLfcXxj^6S!e)t`og%*skKu3mcY+A`_7 zXJt6d+(Lp*{tsKc5~J=j6s>yIH{cjht-G(wpVfM;sm!z>Z8A6Ev{%|(cJ{pmb9b7u z08q^7^+Me9^S=e1+A(TB$0w>TG9UtKX`49Vm!)nExPwRhc~*m6S9+AG$jBGUn`ct% zg4qrOcH}eLozgM77KKTNY;>+q-iR)Ft*ilF2oe(|@VGfeU%`*|ucu@Aq?9MEWsCA& zKo-jb*#DxnnxO*7YWz8qIfK{Ao3$p0iulSS%D}jtty-x+$BDNoYXO1ihYq#@dab-= zmuD8>LcX0D*W>pufP#`D9ycz_+ud;qs&EEYZLrN`f1e|qbgp@|Pt+Iw$&}jhV zI79i2@5PUQ_6@L%#>J&-gb1Pxou?zngl{kV0Oq5@qL%P_ z8jyC1M>OQuYym&7&_lpy*_hJ;9u{VW%@c_9s7rep(5RzZ&iv$wuzoHbzSst}EOC?b z1zfqIS60Hyakj6An8vdjgEWjUZCFK4I zyI}w0z&@_fS7B6)JX|fCA<~=i(toWuand(HwW8HHRBYvJpJ&!dD(9kF2(7n-NwtEG zQ}``mx7}gyvA_r~H#76TDt1|NvE4w#k#prfUFx4W%idu&3>>2GUYB|}#8g8yu&|U& z3=6WKNls+Sl)-m5yxZ6OCvgvwEP(^kO4PMU(-P`mgDaa``^=X#!m%p1sOMfv_(&a!}E*?>k`){ z`Cg^>i+a&6xuDzzpLY_blsu`mKj*$#LpqJ=Ir`{WA+0iH(Ig5#`HW_T3BrjST{)iE zfzy=4Hvo}LBN3svQ()%gr_Y1hw3W^+Ts3)vcVzBd^TdcanR>X(xx_9@i=s}c6)n0A z&^x7ovdj)tVmu+z&Yi}bLhC`kryS^X#a@wN)_U(s%YtSAsoj|{5d&ds%IYuWvv>l- zTLNcbe!dD2S*ok7queulwA%UQ8keu`@2ExZ)RjXZV^g7>czCk3%XW!SR~Aff5gQ-& zEHTHgLsQF=EOD`6^G8mY9(+^-5~)L9$QsN~R%uS_h`gN}{znT@X0WSJ@9zX@Y9;A1 z5#>{fZ!@n=tEF>byW;rL=Kt2n;G=a1yqI$>dgl|7A0??^_Jy>- zZ(reNHtM{RddEMR`{HM^zh25O2XNgCLRS|ys`72VpjsNFr{}-+Q z|5t2G_V39WBpaOk<{0etkzGVVz7~Dy75anN7JHN4AM=w39c8u#hnaG{O2hxV*|+B? zTX+)#{7EK2-^eReEGW^fAC_443(gSoMqR5G(vkWF(~Oj=f*}^S!l+m&+OEq<>tx9{ zj#2UVzfMWF8#QmjSArFBh5NW){TbywCw&zRb z)fXGklkU0(0U}kEm;e*CfqYiXh6{X0oo}stEWQ~P)JskG9ken%p}lw^EKiweENuGa z&O;|?%)H*>U}+!ka_^_#;9wcOz$*szMjf?ZiWJE=?xs~s=vE7bk$BOnM>jEYcjfC*jq8RN2deNYJhByB!S6tG zoZGQO-Y+B5Uo>kZJS{RBJ}!Pdgiv@hhceh(vGdLY@p>*qLJ317@TfP>e^6{$B3gE~ zKBVPy)zG|&Gu+#iD)WhEq7l*D8vSLnQT+86({RGK;{jj#RGN03TAyhi`U1yARO}Lz zOF(r#$7XWzsX}uew}UbLp)by(+G|SG3R4T3szsxV1W_NNtnKs#j}D6ZoDd043u?1F z9RA3*pB>p_knh0Wc1dC^p|Adi@!T(KLK3Xr)R43N0V-^f>y0$&&Dq?&ZcOoz83m6G z7gS8}Q!_}BQB5NH&x{TtX2Kveob1?!VjU+UEB%S7nHan09z|+JPHTJ$A+>{%p;y7x z+A6$BJOuqoksnCwB|VB;1cn{G?BA=zyc`_kMISNfbW0N+UfVQ^{>}n9}mN`7$cn)R2=zd)M>)C zrsqqy!qu=A`xmqCBGvDZSq<@6YQq>;&~;zw*YDbzs-ivLkO*yKdRM5b3{O$?yPA~qh_C2%P zgOHa~;|5^ewr9;cgB*$#@pV{es}TYIQxoZl#Tr90Ws`ypBs6dL$*4c5@T4EZC!{9b zAsphWvq4(=0%!T3+t}3{lL!ukarO)LdeB}3#o%y#*bN0Saia|g?jQU-3%Atc1Y%0q zGlRCFU%!Dg&$|0bcYchK+aS(8OjS*z_R;^MEw!re2)J;;ef5NZ;)w-8*f$tz?|Jqa zuth1=)q|p)1%m}K0F)C+3}0U$-9(DIaX$c0q+VE$FN@Bz^{!fGk_W80t{euVSDhqNk^<`p$fWp1Wx9;xxwT`HQ4DVFz3BH@$_~4_-Ph12PViqweR2870vzy~^Ccx-+=AD~j}K}+fF(4mCSMe<1|QfQo5b!@ z3iW+P)9Vfm6(7u+c?N#+Qb{c5{B-~{1N0;6GHg|B>i#u$H@qS0y^=0U=SW{^Mr*4y zMVBtTwNVY0tD8S$wqXY`nnt(oe>p7N`g$E;MtecDC_3*1PdQyPJwmx;xP()pL27eU_xF1+OnhqXb>p0U9~S7@r1+6gcUD?E!%>GQ!kMR*Xd304#@MIUMg~x0Z8cLOepDg! zrVvwD?jlWWwC&DTh7a`%c+kR*=IG7kEqE5iw%B<3sO^=3le1;PL<-8g27#SVNkI;> zZdIS6=3j{4zeqm=A!fE&FrgrJwqc;*V4-240KX3?02UROxS85_Ot+AvOUk1DdCo>L zRnxt{3}{2Wp@R9AaQF*gDh`YJKkdAE%97(;-tU|ul$qXB($gt#dmTKRhd0{QNCzTf zg)9x+K&;-w$oiWzM3$5W$kO-w_#3B)ezAtp*A32#R!R6+O6VojNPu+A8(6sITmK6X z8Mmjz5dZ1tckb3POQ4@CK79=xbN9-SB&U<@HjT8!k zEFzN>{@{44$Y^b~_yI%=pC2$$ieFWHiz+(DLP$Any-v|-KmDp!e04d^k(`ngEoUZa zJ-mD381D-QAj_^xs>QupQrozBSPX=Dl%I!rVQ0lGC~(4}D98h8oixfZ;Jmi_+&#oi z45mrKTCmDp)9{Kv`AZRQ9yY z8>oe{bVo4gN!i>$TyJ6iWf`}rZ3NtQQrf%qb0N$3h;=c8PzE$YSrs4GkqOm-y@ZA~d zD2Z)1lyw!AUT3Em5}JklNYLdq`xb-zI{>mCm@jm8-eE?K%6L9x^4Et3d= zUsRG-{}t$xGs;rA+LdH+9Lb4$fb$c2l-)%$)Py4DFFGzHO)TN94{Lk3w7?Dsg&Zwl$*H&M)>tp5OhDb~8rd^o`|&6x ze+7;XH{&x;w#wKHN_K1LXrieQbz1>TRpmON$eX2|R*v-3YtO$=rj|!4@1c4k|C9I)7K)PNi9MDV(xJ3^3ohyp5b9mT zm$^si{Jr9VvKxNwoQEiCSiBNi$-eFibrhV*;{mchN%e4admTf#<=m?K@9o?_e2q=s z;)WBP5%gYmo_KzRb0}?r*5sxAtQ6lc2z8(Z5_z-N?Bg3ljDY4YF;dzJF3Ei*Jw%h@ z(5Kh7$}&)0%b&SP`kh_ms#3RBYUw>pxqbTN0_=_MfyVgGAo4mhls6ukM+y+BDP|R@ zV5L}Tby(HL36d_xrO6|v-XLk(+}h-?9C`NXnA#yLXgk5k{S^3<{z-Ew`P8>)2X$Zg z7`eT9A?Mn$Ssb zXd{tv?3;*%Qa}PPm}jT|2lJ9+DTSTxC?|`ZKg7OeYYg|m!iL3KmU5YY;#ZT%!P^O3 z1Gmn#X3EqtmrYCd)m^UVQQ=JM8cVf}%*yyHVSm z)oo#%Xh3wY2faM2%W23~Rz{r^E;_IxpFTd*mwA3z`xqW0joWNEXuzO^Z9e^Y($fmW zy+tJ7{^Vgl64%Td$hfqU`)%=)%CpeTe|HhHb(#FhTWckloF}hRTA{8{#k7=qs^CTU zZvYC6cP_UAcD@~KyqHE2^AISdV2*>j7P;my2ZVf`F2zuR`14B`#f>m(F?K;lIa~BQ ziuE}|O*)FOR?7BgCur!y+AI3>`S>HGCsRwbk@B}BZO5nmpmj2e-PrONfVmpp()kMk z|NBce{ImXjp$QhHiK1_up4xdB#po8cE&5B6GZo`o#vKb}l+^~T_9A$pMgD=z(cJIy z%;56iS04Btw8uh17miSDPe;XaB1kzTZ2D{aj4<=m%%3sh`b~5%p5~)M?b&L`0=g*% zZP|>Ice{69y;BLd(DR2eIxFMW(8XwvT=m3XI?=Wp$S~Hc+il{-T&nxjS8N$vh-Ure zevVoF#j7Dv_%14d@4>yr4#T#!WGyHGz9zHP>htRnpbXwSLza>yr+dfKx6HNo(*&Fz zr8cXAxr+MQS!}sRfn|6^u`8)_Kgt(tU77m5^nz!9XsPjt6n)=^H)7G>cM6{9k*u$u zJ3IQ0Ui{hfnvTVC`JFPv?3$+gk=oGp*W3KJ@-`WbflxmCoQ(H$C^gkL!A6}m@4`+T zqO_d42|1?})M_)g`5It$ksU7hw(6TMj4r^Wdv2>-+SZLczz;GJUce+$z3TW|JhOPZ zu80V5^%IuJV2;JaacBH&CxLOd6QGa3-Kn6B6}g7Dz77Epd6=20X2f@@gSr#3vUQ)T zhaY1&F<`CHXBZcaCsSNFs?Bz!TON1D**ec8BPm`kIKdGkS69O0#Oe1|^mxXgFq<1! z(tsFHE1yIa-B78u#rmFVEAQNCMw5-*e^ISFTw$g*qzmwP*?}VD+7!Klp3n6)DJK-8 zL<#TBt%lL-bQN)QMXOuy5UjN9EV4LXBuAGPeDEdb{HJa#JkW>apQE-A@=@)Bd7dDS(u z`#L57$(s(wZ#jKO(mgN5FVU8H>-?oPU{Kl9@RQF_o+pN;YkbdedFQiG4OqtQRJ=m5 zz+}Y}_-xHs&&d&RBUQLGb(wy{pKYY-$avHy2X{fDacBwA245r&b>Zn=nO)K7G5czq z_+34VMmSwc5Gp0!sGjrqV%5%=tiZn}6I2$2PcFLMeWjdqvVE=IT!Ye=ulS?ybT_V5 z-!nAIXTgZjWLs?++CCyp@yF1mg}1CGTX;j#TMb3*jI5A4{7{Kl3Q5RY#^{7f6HZ?} zopF_Q9JUBnJEfhpC` zBmrIz;%^tqsyWg?CHdK(_N9DzPig{w908H@=joa5 zIYT2p_MpcD5h|2+QiTfY*~KfnU79*BwQ+lbh`)a*jX#?wG7P?XHM3YT+`v?q@-IKm z|LVj2UvXa1j}LKHg|Tm@%C3e46rZI!8csoawH|2H`5ON_HglGlBXFRm{LNzme{g5s zlh4Uc#rtUcoI~ZzF_PT+qmIG{>Q#Rq_;&?t1QJoOCHwR5oA_)cVJ%GExjx}p|TSO zQW1stF4Z$Rtq>EB=|302A(-`^Ov?6fLI$a0p1iwoPgwlES!?K>n#e&jB=WS>EnN=x z;usNe7T?b_vu88#Kw5)kIFczG{LvWQrE}Fw$LCkF1k|l?4QJ11=ndr~scfR7x|HcN zvufx2ox#+8Fp3HnZ&7uh^iN~Q7uDCVT1?>Jve*2-m*C5-XxtvQXe=zgHkKinpdLYG zVbWvlKoU1z$RtH4T+g%Xgsw{S;7tAyay~hr!BhP67hq8(r4qZaW)6&ZO`dkp^dLS; zs)XO-)Xcgv*odajQo_~BZD2E3rp$&eQNJMQgJ!l^e0xhOMW-=-R@z`8L{m|4k6oi5 zJ^GXf?tndkaSkY{UfGiR8EoRt!P91}Bj;VwRFXLf z7Th}ohG=W8aG_z)t1h*3l#X0Df0{Sn-w^(O$mHq9YA0i&!k|%qh*PS6ZjXBqTFbPY zoEC+d^<>HJtShaFrG#V)c4m4_)MQg0=63M<_!UGwaj}W!G=k()14i!fVpdL{{SFY< z;Wnl#@%?tmKHP|8u4Y-9HacsJ#b=)}6t$xq44*j8iarJwl|LRjt^}Y?S7`iDJm%~$ zRW+!ha`)7c(-z^2^VkL3uDhGgg!ML5yC%+XAGOSd3*4hym0ea~(OwO35zXD1DXA}A zOx+3>L94wDTNyt{JAw+2L--=At8x6c4)G+^=}8JidRi4VQ4`Ku+pEkKrJc6%&irJ< zeqErbwhq0c-~Fx*WV)aYx=C%ZDQfK>jP5q%--$IrkrPYR*yFwOqw@-|NOGAzu7W-K{`|3s?q^2w%5*hw$*-}wo&v3uDH2q z0pZ=Wv0l;F={YnR!@(Uf5Wno*I$PUe7GP8&ZVTB&H)${Wb0$}dBwh)}d6mLxGw%Tj zhjBi#;_HE#?K<+t)hdN2j@_4re*}WAjpJR#}54``Phs_aD8W966#=DQ%FxK!13r zEov=-l~xRtDLA$7Q^k#9>qRb&<_7z?zK<+MSi|f z`LX=0KEedAT;;%u0)dJJmb-bOXrI;A4>{A$X~_AwgjF3ze*}n1+HTLJzV4S|j#tZ3 z4XQ>6SM6B_5A`;3_JGZyb87ajL5*Q$acF{5?VcYU=(xtzoq* zA>to~AD5*Ehlo~>Yc;A{zSu4`&Z?bjATG$7XGZRRyI`?4?Zzo4y=NTicMv^FmgDS!Z)kQPIb1u$6FK|wrv~fh$N^D8!}VCYFqF_(ureg zEIxcPFZoyft8+F=4eZW@IQk=GdfxWmj&YgK&O{$`G~W2?TC!2EjqMA<>x&o4@#t=X zgIv?AW7f&S0$D{N*4i@=FV0xo8+dp$6w7%hInEj?17hVcv{fL z$>;-pkJz?RA9?hm7;f@3o0zZeePrN%Iaa&!V)2`}ifReZMdW?K^o@?y>6PFs{eE%x zHjuqp%ThB!^lVl_@cNl<)94SxQN5c01W^jBm!tNApWsh}7?&?D*!6-^G%5fDBi`(N zvGrIJBxfxeMXXl=#XqUD;kbFd>bc4iwK|>jpG08_Fq(b=JPc-E93V^|g7~l4^hfG} zU!rr%nPVVpc6sYB2YQ8bzcv=uh8~eGJ9TAgwaO7DfV@a*a}a zM26(3{Jh&4%^XA?YZbbgulgHe$Dp_;J#^>Wouj}h@kD)G4pc$WAUXP z4NFu?ecaeR!)P2$ba7e_b^0}!-0NZAEh|@oP14WObkez@2CmZV1U?GQNFh_O8G(u2 z&qbG+psM#8PKdd}pfZ|K0&m7Xs(&cb%Bh6MGj2xxN7Fy>x%5Dfd#QY{9a#+SaNtgO z=C22O_9zPwO9#*Mq=G@C%u9&9jT<^4^*aJ3B46XiuWAEJ)TixfORWhwS$m_LpH0l| zMj}SfN;l{A-<=w$SHbCmAA@smAiF}hz^9aSJs00_^2#r46(^Pw!`;}q{;b82u5|^I zdf_U1_47nkY|HuUJrAk4*D7y-zjIlI64Wk%yPJ;)$4px>E+`OHnFx{R(Mmp5FBeI&Jlpca^ogV09O($R89)Ndt)V3)E-Q4F&>7C3;3h z=>x)LrJqfWoOD!5Ie!|tq6eTKpja>;!8mmUGaA^-ci``@pm`k!%h|`bf~(I~7DvqQ zjfd_}A{XqPyu)3dqQlw^Nk;P27u*`y+iEH_`a=g0i3AHO>A*gk$8*n9(TjJA*Bn(O z#U|Q4NgWu@xDwl|*2pNiNM7IA9Q9FA3`1RgB&f4&B*xv#lzt*OH6tP`?=64 z7NKYGdY6&Qg~i3|MS!jtYHkKkc=@^{+Mi{-KU<<*t5n+3G^L@3d#+%`p^tv3L!J?~ z4=3MQ0>QO8h}XAVpONi$`T&fMci08afD`>9`s&YgzF*WsCX9N@njd1P<55_qtt9xz z(R!~n=R`cxowefAw0A*UVsdrGFu6|l9EF3~l+ICd_9$gE_G5ynUeM93J4$LDEc4@| zz}CcP->oegVzLA{!}rthed9^l3CtT^0X$<>C)H=v-!YHBzyItv+54G?k*9X4awdi3 z0kTsM>av8~COHV-&jAkENZ)xw*%LCc8TF007ke2ea8%rLXfSl%UGyxCaZq$o9sOCA z2wq6EhWEl^sw?7`sQ*fh$nvWs$L>Ymj z*hgvUw6USm!49^^kc{m92pL&5$|59wjECj$Ik`-Ge|1V&FLD~xg27TckIt7xRmnh? zsk$jU&D>c(h-JT7-xA>;9&w^8@Qrnm&B$E~9wJ{}ZF`6uwxn0dY$-{X$@~TQ1O1z4 ztsYANDHJm{-9Tww#2AI0+)wZX-Z;PIyBg%-b&ev?U5l=jvnl;UbM$WNg74t3ASBM2 zymrcuXzbOa|6S4$)?bZnAWEFE`4pllXM{XYUk5(ScG`P*BZ~5FNx3m4u6>k~XU(&q-v~?i!43hd7-!aWPMy{i4-ID!t1Kzf$ojs)igZ{2&xu@STdO#NGI^(s{aIa z6PYWJCq%8rq1%tJ^eS|6*wD;LNpMyImtSclD(-dE40|nSNG#kn??GT-+8%$M6@|b# z-$7h{M;2eEk>64mPdS43~m-P z3yD6as-9lA`a9;S*OLlCr@{SF{#URH1}MLw>=OHC{l9ACaGe}cV6%gt)3$)pPL!iu!YdnhVg~J$gZg^{Di^%r(G1QdSP(f%fm!X zt#<@{I0-!-X zZ8gIyuT+X&#U4~sXIBFiIxQLIk*Hkd@U@(c^F;lFpBf!FzDR;!nmh*F7|KM;Xk@aE;rvoGb!UFNu${2fB(V#9$?31i7!hq` zA0G(eo|J{l-4#t4LM=>`_lIqDzm*vOA(lkVGlby%f@~zdg z%TRi-zlT!tK|Dn{G?cbLg|QwV?qg%4AC=oP(5&neyXsh8FoI%22c_UjjyJN_tn z`C?hhHhs8>@p!seujNdawb5l?*8nmm)L#Jeqz)@^(HRP$rcjRV>?+(>K%-DqVFzR7 zPg4jtmCWHrC8!_@7Oh?AE+n@5?AeXe7N)Rdpdr4x{FCUoYri98!&h}P&*q0*3I?3e zgMHV4TosIzkNsC}uSHcD5XOggA7B69MMY^xr^%jua3ymqa9R!e5~bnbkKvZVd~6a# z_Nrr?k-TER)hWC#;QU{IA9!qz#AxvLR&2zU zW2r8fIl=FpAquXsn@Q3%>DFG%W3H-qDZ9FKnQd~g`jS6~1Upa3i9EnXxExe@_LK9O z2&ZqigPC7qATqt116aO83ak8+o0Swro;~{J8Rw7?Yy6YgA%r+ra7qhK#<)9A*|nH*51IL+S)`ldlt|;C_!S39Ch~DIbSs z*_6Fz;A%hKcTqAw}WrokXwX(u9jI}i*+O(EUFyt;mKWQ#wec5e1v<2cQGgG54d0*hVYKl1ABZq*kh0XkKDjiTZpbiES zO&R1}9ULF>>w)^)*D@C6mp7at4W-!!g1w(kZeIxBhiI05wEMIgou>@)BYY3OwA*{^ z_R0d(tYl7Z!ZA&w9CdWgd&? zxxX$WvOgh_qHK+c)JIx;7h(VT8{VWJ@L0sE8Tv!dXVWjGF5Y4AkL=vDTy9NG#}2Wa z5@U4;C4qFhMt1&Kc5&HSfCgAfC^4P|@wq<9#rL9DX)!(G_@H{Bxwq7Z0u_ryMv{6wG_%gD5(g~Kwuc3J^;}xy+Lg$`fG!y8+AW$P z;U=9R^r-SY3$9verE^3$PbQnoQcE=PM62O1crO+NZpToq5dR#d^nIg+!8B5S_TIR67stAUzECG~{@(p6N4OSAI!)Xi4X$gljh zAnIquGj`p9_3@)kq^LigJyFL6z8Q%R+l24Pe<(Q!W(M{~pYmP}K^>!vN&X0NW(7f*mM~h86$BOAj4=s1-BL9U)xQAM z5G_O4e+Isz)Ru|M@Y*lO)G`~COF_t)bAhw$6e!7_0vXL2$U2jG{OPR1niu_kqlO|a zQ12Unt`8al=Xk02jymL=eCdPX00itT=xBV~{yri*{sd_tH7w-lzZ6U9h}&FOj6PE@YH$(}a|- zwh+lPc%!~f=Ptpc!EJzfWWZO?6i~B~L!NKDgW#di;ALB=yJwGM6pY4AhDaG$ps}F0 z!>EG=j}d;_Q=c(Y9|Dhci?EM{2Z<;cE+`D2AxjDQX;)7k({yWJ1vUO=EPYSj2tC;- z5&-FVgDdoJ+^FbUMVz!wpKmWf)$JqEHCo|Y**Vjpx2UQV@wmA!1wkrivrdy4id70y z*A#YZ6q^nDIdd;xCYu7f82;{2i~6_ed??lfrHCI4ohN+tmc3`^dhDjyeE0>>)bo0J ztqe6!flI#YT_HwOWO`GgCR!o69$C>cC8q$4bKCuBUb!T~F`E9YdHM^$WJFI^zX%lT z^^X~y%NsPa6roEJz?`!^*i$EddCIM0ebtiE96^G`26l-Mi z_3-~xX>&{LMN1H{cw$&PHRtkJ6wg3d0vk&iO-Ix!tbnO!ajdFal^>PF<_zJ>xhBYs z;NWy>u_|;_*-zCf7IfCy0L~~|9>C)I#{myBvs!*i5MlLonu{9`8tO>~y3DBVE7|78 z8laPB*aw62uZb251j=s+>X(b)+@KA2%Q(7wOTF+&^v_DLuBD3%*hvR%^cvtCo!C&7 z_UTu=)xV%|I*`aN@dq`U8`Mg>Xg%fYW_D2oqE&lkhEKV%e+A&R0qaQW+>sNEXEEn& z4)8tK0e|5LWmM9iV+c_FRePXD%SqZR70Q#aQZLtAWNYCXxYP6QF zA>gfC9e`oz>^)FzI5DzemEDg;$`iZzVaG4&M>|v7G+!4OKh>J3NOPmdoCExVQmGG2 zmsvy}R%Lyw*-^IPAxkb-^% z%ZK?Keyu-FnCqlmx3FEJCD_m7nD|4epeFvnMgQDZXvVskq7~6%aXVjzJyqwC==Te< zRk#xJQ1@Seuk%Kd@V8oEi#f;ehMxs?v2^MLQ;1&k=BPVv>>2_I0{s=A3L+tv!VC$( zA!x^w&P`3pZu||OmPp1U{7n|L%>`v=c#g0a1_?f*#T}cDmYN>+#co!M(3vqstpJK! zyTS8MhgG73GRZ|!xLGh`ZlDHh}>71HF5Xm3te)iB|Jy(cDJ)|%eyp$DrpBMDQOllu8@S+)>B!meG#LJa;lde zWO~Ldxba~!3bj=wSR~Wg%FyU?q1iI?1*;+P*u$4Xa3=D3KdIt&F-@@3CZCW+Wk_DB zS#xM|^p`2xYj|t8s>MO$D|5J_krw+qHC5O$Eg$lvOx-Zo#5X07>!@kEI3<0Ab9F{2 zB4PhhjydB?KGWzeCX?ROBD@yi(a@{8vAD9>zbrR7ISJ&~;A83n(Qp|bm@ryobW8tm zD)Us+&@SEnOvhiqh*0~xc2*&2>;&E+GHSguv{8pZmGM_|}e;y64W2HYz8KJ{W&9^sI z9As?^0F^YWY=Za}udL7Gk%>PCaa+@y(eqgFT9$|LbKty3>@_SEOO{&bhPH@9ubiT* z-Hi+$AK#MoQ$GJN_ zF!*O|BrYc&KS*vG6X#L>-zINmEcMqqB>ns=2)l0V{%aep2u)Rat(f0~1W_ z+|c*(4B|N!BYUCbX~z#WZ6MiiJ@H$BHACe(IdtED0XSid5z z_lXXPjshAM%yr#T?+^z>@8Sq}w_j_hh<$t)#xn^TE)nl#?U;aTqR~)jMsHKHF?NBg z+)nt`NBWA&42Sh=qJgL$b@EuMxTzzL>BU)p_Al9q2SX)H(3 z)6&9w%PwfXZ|D+(!V5ztD4me6+1G0|kXGdGNmOhiuM@EK1qQuz8qc3qplsgIu(utk zS#?$t(yhdb^jfe)c6Nq-##Xveh`gqRwaaGJlE1tJC3Bu`kGvH3PBPfC0RXX?xf({$ zQE|Kb0NE!6e-?2vk)xrDdP;wUC(>`XJCN8UR^H5_V$`;X!^;J#SgMP!R4xzoDdRht#k{L?eo~XGx_S^2icY00n%20CBu8)`E#Hb9BlKAP(4ySg~B(7 zvEB#LSU4je!%Hf$+VZUm!_#$LpzS}pAsN;i(r7Gd=IY|}T;`$s^5&mY1B@wP7seKB zG++;jZ9!H%$OYduwCV>zYW1UXq#iPesP_^GWz*(D%{IIwC?4-0YOf6vN_GVj2FgI~)+r&(P0o0Hi71<){Y;OQjRHDz1rmp_ID z^jo2vas*qMmMIIqQ6_f?MO1Iy%&y}6RIOMo{h*tTa;X=sK1xDa5X{atEDUL4cK=5sq@FWrQc2;ay4|y^t$y z4xGDNrA%hrP`!r9TN8}@Gx}St92w+SxRyt&C|^F7hF~*@bp#^fJK&rieeEnDTV|c~ zTy;SRjEWOgA+;X8<8uMrsqe#;ZnmblHbpjsov~@LgCmN_UiRYz*!~YAnWNJnyb9m3jr51{Q?~ zFr}@U)}-yCZ8DLw#0da?zC)AMv^JcRK}?mHGNlgl-TMT%WPA+fODDToMn#{iI?tHu ztzxn6G`9uX)xX+1Ztl862Dc7%l{XqMR3rQs9BOMWkkce^xwp8A3YN<-#NFHRvJ8|#*ebgjNK~;TAIMh;? zLMyS`5D1_Kep;Y~QWA)h_Kg*FD9nfmH&UE#**4(CAG<(3t!37$fq(YQmq41~iJlqf zS6@(5KugFKXj`AC`)G}rt7QgbS`{;mN7BN08Z3P6=5LfevhK?SWhzC5L)m?yE zew*zSuu0#F_Mz3vkYw^4xL9raW(`*!v5x7OsYh~m!yS|IfhZ z*Ij#6Z>Xg=LdWK*0o2dzP2-j{>CZtxG~Rxh78IaUV^jb z>$O^5A`{7+1c}E?^JB-54&28au@w4#i0NA3K_ZUSe>#B%P!)r};}G`MS&H2I(;J;UHz)zQCRf&9$z39W5r zbZ4mhx8YPzNmcjRp=O)mz+{?>bc;n&;Kc6=_BJVj+GZj$99>=~>ibK?xf#{1X*dDW z&ao~2XS+08cxX=ruzZeoXj^A*tJvgEnMW>EyPu@lSHJWX|! z@cAx*PT+u|EKL7Ui?GNdq14Y|MqBDP2IFLPO3AZcl2O%+%`2J3o1S!4wd8Y!q^x*+ zwkhJP3L086o{=Uhn1BfHu81BaiRw#%`%5PE9IfhCo-3NENLn(lU7YHUOxD?NYXi*; zNQKSed({jyMMrDvTPQ10RY--a`M(y{G!R6uvr8^8Z($IvF7qd(`}XnQ~H0+gHc?_?-jha;(Ug5OJ!q&p(qIs_d;Oa{;5xvhLywh zCG9xO*84tkC6-=7s+Zoz^)w~CKwJVAhw^d~WkpV3;A5tyXo7e5_PTLL7%IJW4~gC4 zUv(n)pu)Wvzx~&k%~eG@={4Id&IZMuenU#D_AL+2tN9wWNN;^FJQ;L8u&*{ zP_D6i@WC4!SZ(yC2>jiqTvL9MGSvxk0a$xZ)CI4ec_6d)S~QusC31pPUhS zt`|C&*~6w5`N6MwkfjPaq6xlsgBB0gO^p=8RK;H)ye&c;*wz8M%CKQ)PfT1z@1qSt zeI*mUnz*bxvMaymO>_4sE?0KY5bt%K0orjw&kG^DYUlLXhjV2>fxpA~G8wCJD0Rk_ z?SfjJb=`QgcV1517y`}+o%sEX9_o8U&7lU=J;6>O?ZI-^pId@sF(f4<@WsFO#HRRMsh%bs?l zbduo+YiCX>i^}R%V~9_z>S6D_jIdJh;4dortFrGb7Exa8;e{xjRJAGsP#i9$nvdm> z>tpe4ul{;-DTyCQt`u+zmaF)B`BU z9H0i$qUKwUUO81%t?xO(_kO-w=`$`DtBs7kv7_J>J|!*|*j9fMIJA>E;u=t$82Zt? zBB5RBaKU)~ZkI=E#6D`|KmwXrY|`B{TuVot&ZVVsL4xcYx2Q+`4&+ud@5e#d%s1Rwym`rWaJL0ma-ms8a^*1h2amhr`ZIB>{A$~DVERvT* zS1(4+S`?$m)Z|#wES1$zz6+UO{%QxV-fjTnZK68Tn>CadiF^l02Uwcc+y0#>>JhT( z-*ySw=PWfhPcJ)htH(X%H>c~r>!C7G@0z3+>TDlCa5c47?tPrXa@M>xn&*%OqW|t$ zVisfLqGDtBzEJ%(2oE<1;Z}&@mi=w>v&!uM@1A8G=at`7t^ET0KI_reLT6LYup_da zP0w!LyBtB!9++v{XhHgU5homQy8mZ>STtn#)|zj>9ZN%Jj800%>mt)VXE3XjQw<5Yq;WP|GS8lD z@^AIf>B6ad!qn?Wd=|_$9p~8CJ)3C>?2V)`ax?Dsxc)c7GZhePJl@C3R zWfOgy@rV?RmgbXK=utLRth&0_Aewv?E|Ay^jqDi76`?)kt=Cw7COfzmVBNT?3zG&_ zIjLdj#F?Qfs1GEH>5a$(Eay0JnMAQPzs5LH_!#yr(etwCqiYA+k?80>ua9nIGB@dRph!su zb*g3fUGhyWcRaNn%LdpIK%gPWMekZ;duk{POj^;>X|ab#R-hKniE1|g_leSs~ zNR!@Ol)0@694gGIAuTiH7T)=jV(_ussRq&0a1TcjtRL2B3fb|X(}JMEnGwy=2~05Y zoo`{%@Fpf*u%pReHn;Tj(YMVY*RO*~i6bcrh1U9L_0e7p7RGWhiARD$uRhi`o!auX zRc6iGK6`x5y~^)`9cGL&TIGvcaNW!7GfKA(ub{XU1NB7`ipT2aPLWV@BC=15pZDEP zW6D&!cxSmkM$M07v~TeU^V~#uUPrf0^R^lIY2`PCSZxva_^xjhUoDtIaT&xN)olIrW{Lv>C zl>uKkcKPBlr`9uF!d`4TZZ&olGKv&)`kIbp(jPAt?dAnEd)jEHOnTfWw=7QO3Hf_e zgI~5fA(wiGr=h2+5&q6^RCTbgb@}Q@BTkE;)N8(UbT~@Vs-(j^+omC|HWUWcr4*c$ zWG790`+jFPAfFIf6FKLL;^(Q8y-8*V_81atpr0|Vh|3x95d>ry@3m?birA(aM^ce- zX#B))oD42<&c9#vf{Zi$EW#8@d2L)qgDN>)aSRzvNkS$XdW?t#ez(Cd` z!;)FvIVPhK^SXXPxR&bT{RIFtHKzrAeEc-sT0CFkKwq!%Si8~gyw&A>0l94ZYOV`U zG?y1ZwwTCynY<2Kd++jtWBM1s>haV0C;sP*qhElgpF4LZe?vZf>ztaQ6|1L!(CD)W z*R^czlOb*E^*QkfyfWEQi(BHmJ@6BL!~g7zSM-i<8Iyzvh=;Lv$z;@? zt$AH_alR`6Y@*zL+(#;|aq7Gf$~TXc$$*B|4_o98w8<)@Z0nK9^2hpE518_>$#S&G zNc0d!Su1}Pfd8|ot_Q*Y@q}XkaRC63sD3}G9PICVBmh882qL_@JZ*{qZL;)n4Pth& z_vR7zCG$Xq>#pY0-Y&#g4DxNx@;K~^jV-lV8e*EvV-wdwvOIoK{Hvd zZ~@H%(TB`yo{;>2w+3hLe)hvKa+XqM8;*TW>V!>JBu(0Ph6w(f^S^N7Q`m~k+;6v9 z#{VUmJVeJdO>#f{$4R{7{tuK8cISUM#v2^`E7pH#AnMExnMxM_Kd1RSDbYT6NS^$O z_;2_ekevTF$X20UXq{z=D)p(<<|*~F7N2Byb4dy#)v>~m?bVfz(l6Eh-ubB^(Wde# z+PVfLwc=S@*{nK*!L?EMWC=;Ix&;Sj7d+|_m85~)Z=vvoKMUd}v)Yk@50c*wy23_5 z2h+K1bNm8)n;ZlDDFBeK3CsiFciH&;o?s#dp36@r%TpXA0w&`PoZ2>iyQVF}U;x1R zw~d^ifH??RCgc=jXl@@$D53qffg0bo)}bWyp(=?iD`mQ!Vu!*oSso4E_5e6Tj7g}k ze0-j)bjZI(oAOC>8gwV&eV+6gqxz%R95jlqE5rXbW-~N(rP=T*G#G&(C*g>~u9lK0*@2fr$DK#002`^4M5m zMx6fym#0ttCy3lL_iqS;it<0EApc)QrA=1&t#jw-n~#5c(m#4wvJ3u`7yk*ggb0%j z*i06Oaq3cD_&Y~;R42!Se~|M(P(G4Pkiu%s;`Gx6s*5(6zIShZ!zKKmX7|x1HCTJh{diqD734wFl8PEW|27W6d`}uF@~re_%+&rQ^>-{})@1$zZkf4a z@us}{_B7nhu*?=|$(oCK4&W;?dP0lD&M|laQ;2}SiGMqiw^e@+|BC>9vK6h<21bWu z;Rm(0Xs*!he%QaI@V~^8!-fFkFIoQ|_TD-wjxXF2ZJ=>?cPF^JTjTETPH+u@Mglah z4FvZf!JXh5++7o(aRPx50vUc+X70W7=FR>8ypC13v?{Qm^|#~=+3h}l<+ zA?06<|GyqnP#JQ$DgL|dl`?|2;$HX&1Ei|VOQtN6#4bexlorB}CdQB^OPAL1%^Otq zL-t$3KeY#gV1is8pUtK)zI=Oum+xIk_&eXx(5Eo@D`TN_bM7Y_ZkIb3G-kWk`gd;5 z;NffMAl~p{-sx%L{lt{7u|~P0d$yS~!@Q8P?3b=c4Ta^``EwzS0n!gsmu^P@h_2){tr?52Sf9p9{itu1SJM13J0DXgYyLI=9EMd zNTSFbBlT=AahJs7-+Bi|yzy7R0soW%BQ6>j5FMc_!{a3S>LEieRO^RQq{c?6Gy#qG zzeFPgl_8b&-^2Z%LHx_De?mw7^O}wS$ATgK+AIU`{wMhV|MCBU97uiq_tNrzO)PfV zKSk)j^Ht_t`0v2}!7zg-{(Ck-49Q;qU1a*JLLUb%-0R5V_4AKHAKX7<0XSU1t3sc) z^{YM~PH`j6!2C7$zLwRE^xL9;(dc_cN#Xnj9MbU-#08uUI1o1$ma?2)ofJ`#-@P5z8+6q(m+^7L_(s(TWa^rnx7 z>evCfz)F!GY~=G4=u|!{bsWjB!$acx(x3s2@3T9sg;S7arH2}sJ_E9Re9i6CwdoI) z;yQRdc8&>7?>8`Gf5raMg6BjP5DojY2v_xj{;pGWfi7hL%Te>yvsFK%GB~iS9!X2lBgaond6tAHk=hVqIyj(JR>gaCtx|* zD3X$K8wkn>9XeUk&axE{4!H!&<$PNYFb?MMx?h+5Fq=|5!?08lzpIVPBW-R&_S%&t@S2PO&U#Bh-f=h(! zf|T#G?l4-><(K8Xqhgl<^!F#$e6)k=#X_=|9z&ua)YCzZTQ}W^kqwGiv)@In#?A&9p6*+Uy*pLys zO28|Y>-idar|{0)ifMNs_#u5;=6kCVA7MmhM#5AaZh2~7QDm*f)v9on^v1-3$}^q1 zUtB$*YuWRy5$01+h`~cJ-YQDeR?fXTDJxDV4J>6pYVx{kCu>!W)5x~p4_u46x>*3~ z^lndEX$6uD20%raGoN&LSf{yO*yoW z{!#Zs$w%oD_8zH|UqQyrX}=QpvM!8WcaYc|1YJ2nN|v79 zFwnI6`pw0iee!3=kjEDUos}k}jdJ=Vz{VppER7j@&%vXG>?VDUqwJOZr-PyTf0a1${KUf{To_XZwtHQEQj4-Jr zKnvfP36%Te^Hlz%0``wh){x5GNV)&mzvg?GDFoa6{eIZS||_uQ9gg8KJd}*1Z&u zE^mTNVv)P(PHjXyP1c?steq;Tma&KUdqSwLYvh+o6=ijC*IgCBrdDYB&!ps9t_V$m z3Ucs`tz|*Lv~x%u+fL#>tB1fJq47ghe)ioGf!&@FkEBrM97_@gY{UKc|96Y4%UlV zc!enc0>1E-qjNI1eDbH}b$ZYBCDicTt8zCOky!4DzYVE7)G~R!22+6^%yo4aR;hB| z7qGbTyjzS;EAs{`9T_Y^L%35(VcU9!asTU&_FL;-V{nTEMcA~P7F_eKY#|{o1@7jR zuGPk_kswCDDf_p+URE8KrpJ<*v!}&3I?TBq`KcGdqjrUU?(0>wLQ0*<@g7-bb3;tG zy8>wfRYQ%Gjk;c&RQQ)mCL#ql)C4SXWn-bsP*FV!{w=Jwpo;TeR@1^-+$g|ESIS~^ z=5m=Wo!k!it0QNbyo`-HX*1@j9Szf_Wg{f81kO0{m~WMMIzGb%K=P%eG%OBEO>^zQrw?tT$t!5Ze_s=(fR+zc2lDv#4 zhVP&6`>p|5Q3hE{@gJo6QMk^@YKmNzyPK4=aR%+X7)Y%qL zNcKqzR#(7cIv`kaLXkD7(X^{mzXQ89k_%#VR@*{xG!WC?59-&-^CBN zvlW$=YuaOxt+sR-{RH6lDl29!jn-qMb|!N_128E9nd#jpJ0lPRRMo=Uio6V&{FI-pO=D z^e7Bhp|E%b_7M~aOPMG9(-JCSUViM4Vnua3+%4#hiMaJ=_;ZQ19s`r%N}szbhCSGJ*}#JImDLiN6iw>`A0sU1&YbG8b)oZ(GSi#9LnZIe zhBRUl{%k1=LH**U+dzEDn1i#JdIyQS%ZaRKKqcps?2~QizyRWFB?G!YpDr|3MN22;= zV%QXbSUNT9(&jdN6L(AKEg^cBU-`1w-7@8`d%nQSc6>3dxwTV96OrfbSlztdu`n7X z_?6_8%S}`;=94TK0PJ@YBtY%X0u#st+7(c`%+VwV|YO-wvOaa#-lhaqg54@a>uvoGQS}nE~`!I7CizRUUV8N=-ff{p= z9GGyPYS!AgiQn_C>P;_WQYW)RhQZBA)(uf2p8zs@^99k`u77yf-XdswM{xMaOQqT= z3l(%UlMeD3EJ$MiRjp(tOF?p@J(4!sZqEcx;ZXtLP&Zw6^*OPmaimX4(W;R)%&+}n zbi}1FfKZ)0K`)dWV>lMZTB~F*l^;m&A1ZuHXac@(Kao)$#Q^s4%QVSmNvc%vq{)9; z$`pQjLf!zEW{tra{cvwEa5(vZf&Rnnkqy@QLA}pYI<3|CjOe}ui%EGgu1^28#=*oS zm*`AKuumtFD(%PM^ivdEGJim(ZEO=JKY}NCQ=mp*t)Ba%aeV5xHfh1Z|+3ewz zV_ke&RG@6p0szrd<}e2a>&SGlJxD)c!UAb zHW0V5qIOttzK}cDj)Cr57Sc?cyRp-6<>j_RM+Haz)4v8o#Rif4Fq)B zfPTWXj`y4ke;j@?E*3ctQAZoRG3-LP;fJDGp~B)W+*uj0Ue~?4!gxX!rg`mB^818~ z%w5HXot_W!xAku|4Hz|7B7>EkWj$@ISFT!VR&9hku>)Jpg@Ba&X9TpHlBdtG|6qX@ zlTD;vKQq9nk(sHyYPw&()01~3VT~Qf&VoWJzX0YKT${Bs?c&e7PY^CdROnMx73B(tO7cM<&=JsHS$L|LhXkXD+qe`C2mEW z!-cs{U-EvpQF{g-FH@mD{Ki(J*0)lV3Pz#LxXWyws0H1;bo!q>3Eh`3%j!=SgqzNU zWu&pH_!V2{JsqX!eo<%p*3yQFU353_lOPATqw8AoXRN%&D+8*osZ~;>3@P=ApXV?%N(2z*~pMEm)A8x!t~No2-1{+PXo=<2sYDPHK=(@MqxVu*a`951i-H~i!0R%oj< zwk9gP_cc_G-BjSAqDSTv2@ODhT<@GapAr%$OqM{I*h{o)z&sd3(nPCUE9pR2 zS1{NB6Nx^jk$fhD5$}6R28oZ-hdPcNz)3~u!e2lX8L|A2Z~T0@uzW|)!W_*H`cH&R8GI;X@gO+TDBBTKoI{3RvsF!SUkRcPGd8E0qR_n< zI5nc?Gn@-Ve~Yk;oZH*bnOVF_B|yp(&UI9ZNBoSY3NryRrxnGh|25=-bD zE<;!Yjx}~ZD=RxLrGGJ*ymg%y>zqq2_8R~TX+jDq*7fiPv2q&n($`#Epn7VvtO0|) zJ`b;u8%<5-cRiUotP=w{I?ckGS7W0`j_ZuV`-?x-=UyF7?p$ zECR38*lXqy*6~|qP%5UfOnc>7liT9bQRE4}(qclBiU|_oO{}G-Ou4hud889nQ^26! zqsv~ly91Gy7g*zo(7xhc>?I>y;hhSM_WE5(PCEO>dBF}#*^apCefvQg5kq1L#!E|$ zSB52ZU+#lmEJ6FaZc*%9m-<(SMMeBgLRop<4(phr+Zc+aBTunmIfETH!RP1syFs)D z4sUW6Ug6=(owyQQ0a{DKuqY)1`isM4O+S#ch3JM@T0l`$n?(4Jui7Eb)`)|cknf~y z*uvJ+8Vp32@-i?P62cIxnpUT6Cbm3`Nr{brq+vNiN9;d^ zu7;m7C*wUj@*^R`mVa1VD9cl{5z~qv(Vx>Ig+Bu4*Dx}RGenQr2JaoLcaSoZMLPkv zo|0>Htz40KrNy+DFq?R7Nm9_(=YyGl9kCP}k;AZ1lx>gh*Xsd{1XouD;divI#eFfs z6RKq;um(*`8vV2qEBzhD;vHVJlwmYn>%2XaNOnhdLZz6 zDm+JDe7_Ugxmhoba|xd#=F38efh~LU>f%q+1#-ov@E}&cc#tF`SwoOi0Tk{ldjsq?wQn&(U*{U`znC z8UV4r8=Rl>A}{CsWDL?8MZE0JRKnbL$9m*sfh->nkOWGknoZEbvbpdchMREHz{o9! zi*PDImL8n`zFfmE&d(PADz;mWb@;O{cH^A}7n1t)!1|=eJA@Rn353Ki6RIu0iQXA{ z_XWS~e9Veg_12Y{yjQ5&ulKFlzyvD0&HsHSW&!e4@8! zqKIPrAv85Y&9V#(p0Hb>Yjm;btWWj5u>1Gia8SCG$1=MDedbYY zzeGynJ9HpT^z=M!e>^@P&&kJ#Z}E9=aUV(I@P@P%r)^9rK-@p9zg39VOUJnZFVps& zI=mZn;Ht~vivQ{NOj9Q#v#tj2vS`o`@@Y~TDAJ(;Eb72E^4d*xCN5&ZULFD=AabacpXoWdL~N)$>$!~ynC!Dg5W*A=mNwd60~i!dArE%X{kcV%FaXWZ8zS?k{x zmh3KZwv}f=IR{{H1Wp?v_hh?zJcwbV|5W;$vS^})vM#R@4$VH90`;3?Bh=r1GTddJ zX#C|UEfIMO zh?f$ij^_#OGK_JY5DsWvKeEZz$xd!HbHK7Fj~Skszn|G1mXkkTXiy10g~n3;q86Ib zcA16z)CnPHJS_{2PARLo!Fut>0gTe*Fs_XV#61ATo}827>VB^iW|p2-YEe&3Kx7?7 zl>|?a%b;GTuyr((%_Kv<)`g)Q*(Yxt`v%r%by*C%885+Z1q-jTlyru8Q1u-&ok)*# zd(t%+Dw@iJ4w9uZ4IL)rx}?F_#t=q2RH`>iX~HcrvulwxVfoCGCeUoXVE0fqD;rEh zery&{x7W1uiwj4W$ty$z9$hhIvf;kE*{dyI)LHRPHtsa~!E(=t3DPaO+Y4)$=alj` z5Xd$7@W5dw^e5PQ-4O2hcbyWWOJ>CLv{6nS(%PeQqj1T{!%MFNItW7#%*l4M{8EGF zY>aHe-Sb>y>|Bm0LQa4~{=ypi3FnzBX5(b&>WM0f4g*Id$u2rtoOX~zf#-Y#R z)`_#yQ81kkew5&s$HZ;}B9ooi;jkU!uPO*pPcQciG5F;(ElAonL5Jo(3hXq%bI-4S z--}*_{o)!o>#R4EMD;%cxwMkw!&{|1f3M=jCJEN$Mc$9x*5Tn!P}XFRKX8w-psM@) z1+Zb^Skbnn@=(Y-6T+!kCACfO2$w`voZ*Th4)k6wLBYobX%8ccY4~uCoHKUxOV+tK zxPpokL%ND=7cMLzH z7>-aqt}T+>qW`QCQ`X;&d~D}KRuBj0rN(CwqdS7UUI_m${ue;eLSpVk0f z^=Lqvu86sLg~%<*l!k1?--=reBm_EnUi+a<>r83t0{Tf)Dl8lNwtvj5liED(yH>Y8 z*j0cyS}!~NNJSAzeUxM4I;I0Q+#rjNvKg2iz;+ zo1mYM{#oXiL2^_OU^U}3U>VauJx#{snAPyp_HW+$EG#!VKd7h{1R=OPqCUHh4nF7Z zv>G^519RI=^sj(9Ft^qNE1#@G1Kc`95vwP6gS zEhamB=jmIlxE%2*54k}#jjl<*k_F2Ysmra|XFH-`)Qu{n;jz3%k!6-HnZ8Qmhq;c7 z_bcJ@Bunw{6me|P%!OV6Iz$TB&erfpoT*s#siblYIrHQy1Wa&QMoD;7Ni&JgPRu?M zfVkB^mRH`Z4WY{kbMeSD*g?Mo<~?5>FuUj+Y7crG8p@gA@b8OmYj$obmzT(#=ukvb zGO`e>+L3{9m*eF0szrvaml91Tbv6i#)^OIjd>3XYEGASQ->^C=YAjhCaV(T&$J~}G z%iDR82kJoZBuFB=1wwe{aZU9+sWJ0kj=qXhBki2t+uzHOSZQ9I-OC}v=NM$0{(FA`B9p%WYp=cd zLBUGBkb`1SudSYgSKJQ8QW}%BuRc4h0Ih?Rd#fpQqp>sk+#!6zILOK)*hsYp*iq7t zZgga0DU$ff*7B(HV3|}DekzBmJ!f{5n!7jN;Iv4|Sx0PY_*P*&j))@HGfp_(7R@a* z`a9m0_NI$DAw*PYuNJv!FGp<5>2)Rv2l{gQa|+va?<_0khaboB;ngY1M`{5L^vthF zav|F#s;%k5nQ`Zu&LzRQ6e-AMk4iVT{F9FQZ7$n~FZ|7v#5=`y9naR4$@6 z{iHX4J_sFhStEn)ra+&uE3v+5p1zT%MH|THa%@ej8&P6)?22WMGBfh zIi!Nb*Y-FPQ9ucrkO6GE3pua13Da7pAl z;=CyMQDuDLs*BJ+bV$(JGokzDci3~6L<`#X|FZ7FwCZxIq0EWW?x7j`)j1?#XENkh z*T^}fmSzguwmHG^Q{g=d2OwCn695388sPj4`bnMf*iK3fwS+XS-lq7IRLT$u*fLcQ7V9O zS+^48XTy^hi=pM;g2JzReN(0O|J}~Nnfu>PK{%=0Z}ms6$|)NBojYl?W7g-F=9hNg z7qu&1I-%fDC10aN_|&Q=+JmQ55eUg6L<&;5G2v@o=>9_-|2L!mdXM2#zVz7FYpJgl z4oq)mB&r&`$9ma$w5^8jUocnXtxuJO6+0W~!Mxo$>*6|x>Jer(RuRGD$4jq`W)aEp zH`ajMw^kd&8g%$Jjy($@a%mfYyLTBE(p!C1Bdw({HB?AL@%v||1b*)9?=&JtRcN*h zzF$1%rwJH^jN;}w)h`0dma-{)KYVfV#fe213GjI%S)sP@3E)u&ES4;FeP|BfQkikb*y47emU-L{yOXg zAfa%>i^0coEu12?o1LYWHyiw9g8M8wWt*X-y1?>iDmpl}Xy1%i=FZZnMPb89?dyZD zTE!W3hV7=UqwkyW7D#0egUQ-3+N+X*gafl{)HN{}0y*=wj+dGil*HaGdoA=s z=)ei@QNM?cA{m{GbM9zqW$`g10!A7bJmoKlQET3IO}6Eu3aZWk~+Eu8?7Rv^)-J6|UIWTVJ*ju{EY{-JWR_@bq` z? zPi%7ux6MKZ3a2y#xcmi7omXlRk1Vm)$Ot;x>GpH*2$E=An~4@r6WSY+jG;^%V6~KC z8-HD3f2bU2!4pG03*gU{BuYwad@*J051FRM2q-+3P{^c8%0dXHf`2=^VpNe8XgZc$ z_7+f^CbD}TC8mHiQi|_j&K7FuE#>k$a*NVa*&`B&d3%ekbXL>d9V*S+!0IDQYt)4{ zk(~XRQckwk<3%H=i464mCYp72i*6PWTJh zi{M9PKU<03<`n%4c)z=9=NpxHf0KVY9+qmrCB-I>j$c2JeeRt>kBj{6H@4ykxeShl z4X&s~xC}?MuLGBR!cM6r3dGlR=a~FVt7E1?=Al}FlM|o(`^@F9vK5Lpf?x0&Em3hm z#Ri1@t5&CeZxMiRcd@SQRC@XI1{1HgkzWACyYBf4bJPPHag&lm#>A+gR<&Z9B2qu! zUWf&sHe!QWC(x60BNo{Q?L6nyhTK~Iqv%}{n+CTg3!zWo9*3zDzGGvEX;3fsiL0uR zTb_0WK;Ps(JY73bq$UH1*5=}{Aef-GU`0a4z)HG-cSjLNUVHmyX4<^`wJ$7uf9Vbb zIccFq$fH6S1m5Ve2z6nDYfB&z2+dsPXi!scQ`=(qqlY4Gp+7E1E0puW859}!i*mm3 z1v2fPSU0!H{0FZtb=<4R7K$WF|glUqZ68wr!-bI6V4OsU-U&a$B5Y3Pe? zfSqX|P7NFfe#)b*02u}rIUa98_OHst_MjuZ)EMd(9U0@`RLwxDW)*R6KO5c5`*>Mk z%7e~oH6l=9gO``WRXx1O%+8RW=fc32+mggwRF^QDXsA^9C}t+rU6kocGIDxC6@dP+ znNK>L&H&V4oYV5@(U~F+H3QEPr{kgGkdHQnp=IXx5{ZoUfcI30j_xqc1Uq`5tqw}1 zTThj*Y8P~(S^EU_gVG<;qhhV3CQ#;9Z1$+EUc(9w=4^RccY4=?$EC zTY8v1hXFd1=z)uAhsLz15v~}YGJ7;y*zd*z+Y<@0C}Mcr5I%h23#~D;IH9d!b+aS7 zAs$J-)8?Xqg_{-TUg0)~06{dFl^;wd^3$3v!)?+^5fcu*^4vO)=$Q)ptnXC9b%Fvs zl-Nd+^(Ib2_d=ml)9*n+y*<0+Nc6Z)&8nY1R4st<%Sr_&%1m=1nXL;>4Qr@E+i~!K z4T3%jGK!^RowN)h-CM+b228f6=PD^aLlYFcR3udo)iCGRCC35cCZ-9GA>(3eK+?wY zs5KG2+mV-hOTEtc0X=osT|D9lai=5G5|sxY34^@qGz?=6PP!rDt7u?pG&~OP&iPF% zk6t{^9)wpu-$k47O%zGvT(y`rHp9dMnL=yqnN+2VIO05!fp zA16V}toZJrcD$HkYs@5TaymAT!D0O~)W^6@K5k@W_0!q9_8vGtCXB~s^Nw%3 zixc#N!9*#EM)oaBGO1ItPe%yB89%hdni{;n(0x`d+cmH~yedBVK*Zn(=EIJE)w0LL zn9>j~xGTYFfAv7Gx#R4Kzz!HT;+ekEO2ZhH)rWjWZ^az{!Xo7C{Lz5lNzt0JwAa0f zj?bY-!R|ISVmg%~PE&~1hSO!gS00pzc1C~C;!Xz-nvVO3e*&k~ts-sq{!)URP&A*F z`8n&ZUXF9((cQeLN5B6-uC`It-pcfsa|7-j6;$$*R?}{tk&pAj(zUtO>ug~K2ky~R zsGkbir+%mp*{@ZdmV1HLxum*ia06ln7Z1+?o2)Kj6tTj&c_q;`25F47u+0+P&|Bz% zO=x9q14Plj_OSO&QKS7a&kF0z9sFoyUx;5N-38C?^-@&YSPYXGbf*I3;`GO_PNKba zLlnlNW93xvz{0Q)5iEG`wuF(>;4--D)Eu+`Hb}AFpOR25h!~&2FB<%5{6V^fLwF*N z(n?dg-CUq3`mH#$ojz9^(`zp8u9*PrPZH=$AE16$tZIdiWot=xs$H-~Rm*58pM?Eb8&*}S-4+=1%Lg}vvaotMuy6}QsN8nne znHd{$Z^?wqy+(b5G~KMG1-ICK4oj>N^b0Wa*&X=#xJ>RN>nMeL%9@u1S*MA1FQBzV zuG4R|6BuVB+i12fw#+e>dCjw|pkm%CV)7!qmHRzMG)c1+e1Tu1qq0Jgj$Xj-Y`~u4 zi}TbHy-GOGw}rY>1+FN@88Ox_`da5QymyG8~9god5#Ga&S z;l;fFE*pk)l!SE7FUq#Tl>B)p8b8< zTnLrHLtOmat9z+GV^1DVjfeo7yiZ>>PW5M>yu4L72XYU}pj9?ukxTnlT9<2gsdIYJ zcKOG<@mH;&$S7>R^o@vd@>R*`Io{uiw!#HI?vI-b_~FAzwhY0noTGKhrvR_EknL5uH4U#cw!ZIdf-$n6SA zf9f?*D>jFYHmV?L8STwl78y2VZWxJQjcFI5tQ+mMUOIhz(_Etd1SZg3B=%yM{Hcae-RSI{MAo- zBCUa}ZUmDM5=Sw?g#1u}pL-wWr(nr*WfhZWb5>OLGB1vuDT|GxIFvXubI6uBC2K4^ zzE6d4DO&^OfW@cDGnc^Ac0iyBtoBBgf$Oc&K;l)OOFJ$TqAT7sWQLVoR_zwO8E>fI zjh+Vs`mWRLxn2>31ZeY1^82lUtD3Kk7l^7*tWJyZM}3p6rsDcYK3f^mv7 z1e+-C9ur6`OdPm4HQE{6A za1ZLXcux)_{{nX2VVwI7!(rwzR!ki&oqO=~PX^78@TEDU14@qOX9b%2i_r#6HQKB7 zhyihf{Hm7IsCJVK2w;i{CZCgVN5}`=hY=YRFJX953pV|sKGm!*5w`KonF$DHHc+fc+uo}cj{h(D_Oq!IcmnQ=24i9IHj zM_!c(@IVCRqH_(?&$sG-0fCTt6!8!~<*$}SKcfK%LM9IW(0rzkwLVy($*n5sf%)ps zdLn?R6N@oWtMIfpZrk}~szc-mMTIUF9d@A7J7@XJlpiEw&aJFw+!}uYP?$P+kVuF< zM6Zlkxj;ZT-+5X!p8G&G6u87-Nn1W1no})@L+1<()k))^dYpOuRf0id9AtxfqEm>= z3Fqtq5L*|h9?oH08iI`RBT6&m@i@#Z*#pc(L{js1>+mv{W*+n(oV7s99=@$=3^;Eh zrlcL-D71wriickQmOm&OGWwXbq`{ceWKG9TRBRsyY)1wpzhh}gMJ4(;j1*LnL`$=t zpq7-?q8poPLn~KYEQ8ZfCZ6-5)H{wQws_|%6NN+e6!*s9XpdGDV+|%)Z)ZHWSs9$r z`k73%Q1r^I$?@S~r)QC3iSCQMygU8|1RicLo-D!PFhf?&i+{Ve4E_pbH;whjoGKkh zmQpJKheU%NbGqYJhFo5QnUjCI<4(zj(E|!Sa8b*dJR1&`ILct5OJ!8xTmMKrqU1!9 zujsiv&Cg1`>*7C5XJB+c)3tPM$;>6Tw~1Me(U5tGK$+w;NrOregUm>W&MjIs{mk(? zb9Q)hf{P`cx`;>l;({ClRKh3@**e}d zWt=;ABJT`UAR>v9&hh@<=hLUtae2Rug{oN^3A8hlKmvFf@WDt!dLNi$WsAJ zurVH3ZHT^(B2gv4n(D&5(x`T7#x}o8x7r^Y7;J6!Y0I?!fx@mx$4$%HO2CACZp?(M zV~7WTv%T6|^AB=qOr>r)^KTfKeReW@V{E-lRZ4$uj;hJ_svH{oe!gy z=C~Smy->N4?}orpy)5yKw3?xST-%`SrrK=Ca1M6IL9adXNg%lK+^2lBW_dUh)1Q@AqDxG-9cEsZl&2UHY_#8sEU@6^+pK#nG2^Kj?4UW~ zP=4l9->2zrLr!z>?d>e;AH+b~=NN^5nsy*5>9?iLTWgQUvt?2!#}&S6r9^NuyQhN) zD?>OomPhb&X!{5zez0{xA!2&vAk=Uke@Zu*gQBRC#~Axz()^OF1y&~c40>l?!IJK9 z6sfTauV5m`cG!hbLlz{iuzg|5qzJikNDIwR3=jEwAJA$U5;bMd9srY z_KJ(5b71mp1xPa~18lkLepS%Xn0=*DALIeapi)M>C#cFnp49r)b~hT&KtyLJ5A`^i z&J^M4@wDYTg3il>ht*=!F}F$C0RC-n^5|o} zS@3EHeA;_uqFhSb{RL17wWe)?9v>?mmARJxgz#Iv-^=xMNmK;Cu78`M$ULn+T&XHu zSw5sfeb5hI=Djjz+gwG{CD(6=%k2I16RlYn!x~JhT*|RM%x=HEpWzIRCnf7GpT#&5ebSC4zZ?ge3iQwe-)sNyBEFN{Bg z1dL5fySVGZ!wj)XslBekj$cSW9MARZ!q5e~&OKhf7CWTMokg%Wxej_r&bRhAezgMH~Z7~yvjLz2zy5o>Lx*Xy43|SGk3B- zS(@rJiNxBy<&f|o24^2WhjIsdMBz$CPOfu zmKZ}S^^~t93hHn%2GB2q`fSV1zRLe8?3-iK2-B0ixsql$)i|nam`iD5P8grPif0@0LX4_1fps0*D0lJ5({~6a2#BSBxcG| z!_f8Ad%Ch9H_w^8!Nohr-){c`q6gpi$*|C{e{rwTwB;!@#ZS%-CA~`1_dih;|6wZr!&UrGRK;tW#({b6{TnN-f2S(K|0`ATCPO*j zNl|8O)#JKx_v8yDnlH)I+#5txA-pKO;C6(1Hk~~D&G+8nP-@eFjDlqhjZ6l9!KYHg7g_jSxo*GePW|{f5uR6bmr+m$;igp=s4>XSiRxR#Vr;&;u zm`}!>i61h&4+}g{AuQ3<#@G_G%|A#S5)_qoEgJt>Hf|b1^eEs#WaKWkX}tVPhRL&i zHoF?%MrOKaMLKi{Wtf!2cegcx14hmLbd^0gY!v}&LWHqmaOl2>Hlo1k=jar3FAa`>?@N#$; zi_Lc-`&>^Z*H1Mf*na`!7iC>A(YLRBIQB6Nu#!z^+$poHV;6E4A)l=ngxGAyuzFBs-rPIt^XB|@V^hQ40ewkIH2D~kcHMp?o}W{BlS zXs_VLS*dk9^i)c&QB{5IVn*O+YCF%)c_0~lE2+S5LVD^g|6X9EropDRtpUG?@6Qgw z`n1>G$Bq>>zW#AH%(-2|$gN|A_kGR7_|(@c5X$%MV-H4N0n)3D{-4LRgy4V;Q9iX( zzv&{lGB0w?15F%V>lbU)Tn$3%`w%TNMufv9j?eERQBm>6hb84F$X(SkWl`FSvRj9U zQcO)CG{#!}CSqT^)gf-AK8S-fohU7eElFdP`;BCD^V7g#UPp|F+!OS5>Zy?-4(s*{YFWJIvL@ZBT*pDqJm3wo4d0WD)*I)zVy9>n~bzQs1A zp=(?WUkYa8<@najceL@U&*q3tq5 z>u`okc45|qaoi{N7qE8hgEgSryZhn|%$!<^K*MGDEj#s$S^q3u#G*t^|BE>5X9ZWK z(LspC%e3PhVI)6&ay0<~faHAy(q4(U=5?C}d=K24!{shpX4enf7g45X0fOud}j)k2Yf(>RID9xNN*F?27S8hiSFN_}B#Bp-T)u*jQ8FEfPw4CJZC= z;bT5}ZYzq!KGjBCqNIEGN?>SwGm^h^;EJ-vaK(yERHBgX(H8>+Pa>@1Hy;x00r57$ z-p??1y!!;#`WDp|N}Dki=bd-Z-&*KZbaq&JTbiXz5?@^s%)l3t`++m^>??T_o zs-z=PuWiTOyc-OrTn*v-#pWrl3N#~UWE=(CX`83l>Y~NUlkoku%1r|#qMvIx5nHel*W|l0L zEX%Uw%)Lo=H#fTvIVX9`sfX^a>gukk?&K^gH|K)oLl15ms`CZtsT`i zf4R*X#nNxv@e5>jn6jF~haZ>IJojI{3&q{Ovb`1n2A?-#`ZI?)#No+7@t9O4`YFNA z)?fF*8Om}8D2%6>|qO#_X4r+iDV+4XQ|aK?;N znB##Ndt?I{8h8LTr_4f3wf)Wr2bh(46qDyxvhgs7s3dNcz(`&utw_F&-~*ZY5)Ep) zyAEqd^(eb^Tw3F_A_!K=FILp2grKxt=?+I4+D(>44zfTy_0Ji{CXpY(GZPGrEMkZv-zbH)6zCoK9z3s=s7-+&BSIHjNy{c+!>`w_8zh zBY4m6XH}CqRI9{w22v(qg|-wm^C2GORqtPlRgOr;H@m=E)dd$|yau zjBY7I$J-=yceQK>c{#j2ei}kMm|ZZrft9VU$%5<&q^oS@BJs8{OIO;`o(+c-!09@k z`PtfDB-wl#EdU>vv*o@AJ9&Yxw?xj9gAJkDAXx>uMD=HNXCj_`vPtRX-e;8kwikcnOxQ`I*hx~X z18n-7i#@r>$rN>VGEwJRul6~1i;942z5*EYsg8r4MksGPX=%20-OWg2qctOeE5p*q zEsmbAgw(EPSDbUxK3&E}SG2pde{g#fse9;3_aD)sU8gs$`0_LE2r$wKN1pQSM>$+4 z+%Y50J!qGtg+2bJwB4zbUPRX+@&HWS_y2izWY={}wRh6rNlU1ydN1ebWMMme#5~r0aPz)uUR-=Fi8|f6QCr~%gg+$H~bSDe(X_oG5p$!}2 z&G;@yDPdD#kQMo;MB4oL!p#wkJ#p9WeS?7L4w=caQ=BC^Gjbu=f^EQW-IzN+S^lsR z#4OVimN;8lUigxu494j;vSD6G9YjgpbB|4r?8*u*Q3VCBG0h5VO?#$OaV?1ouXYg< zZ{=S$#l3G-7qpy}jV*oIZhOEm&rHE2y0X3XGY8Qgg0}2sH8q?R3l&&>qO%4*TjkY0t%kG}AA^dh7nCn`c~KH(tGyTG z^{^K!P2?IQqdxD3N@MsI7~HXS&1?`bQl-+nBl-VRsmgIZP2#HCZIBBs;k1frB~P9lkyMq-rik)jO*FxD@q5 zTU(iPa@3Qp%@ONV)Ko$O)L$7QS7CjAr@Yy>X*s;)B;R^#RSl?Xgqt4m98oIRn&i{O z+pmdt({!cjs#brNm};Cla*@z&E-6y%bP z(C{g~GyY}G4&AWjD1S}pFi1j@e-L+8S`{RG*bzvidf$$<8v}qi42>|&n|KJ`!&Yff zWi9&6wq=J*jjL&3nj866i>cM?8CO))@V8wUSESu-I)X`z9IKdi>mRfVsb zJpa(tSyx0x4UXy1C!ff5b1b)WTQA7Dz&{>0wt-|1EpvV=Q8$a!}G&O?4Ve4*1&Y7Di$ypW4;H(qe zuh3#5dyE^p_ybclaH*Pj>TyxC24Rss{j>cBOpX2!9F>e_^>u}2o&0^gwHA!CD!qrp zlWVb&6)Q46@^QsHA=hiQo6_<=_Vm3s==|%-UqBo2J46Q9{_RCJcWr|IQFW=JA5SfO zSVJl`Hyu!226O-ODM=^wZhWD535M1bBZgl8-o2DfktafU5jU*Osx_YWv9|IGG@1Bj z3zm_lfLih)Cr6r*GODJ89aNKR^ zQE{r<_Q33VeHd~QJ=CApyP>o$OyEhjgCWn7wgP9{SSN*A{{B8OHnLnJMEQ19dhXB^ zg8PMG$a7po1Xg)pr}blbQjq0qKTEG6{H#Fik~Q%nyF-4FAm(%Qx9ld2)j%zX^);?~ z=Uj-Yj2AIq^u&sKN~uyMl%HkO0wtK_6>fv8#S9~9ZyMz1l<#)?vaZwC+3csG@Y2f} zD^~d2y4VX_(Qn7Yhs%oSyK_xC()kz)|TmM4t;IH!D~R8ZJm zzM1Kj(XegWPlQbdz1Z+mQPgI< zt=uBs1UVhBOv&D16HlkgnGDrwQww#x9@-!CX-Mk2%}DeKk~1urNw4V)CrL>pk9Z zFz~}X+(gYwEOffmI!P)s_esO}T()|pS3UW;yP%1KIrQ&^M$EqFJmFiQa`BQwBWiqkw^br&|NY>Te99Uu_@)UsH>0e zJAHeh0J+LYuD05q-v$%nrV5`-?{C9mS7?|iG|FTc> zU4xj;-Sy0<)8Kt}eMHbj(?^I6okcYJPZvNGRcfw8D8C@hXef(D@Z+ zPH=f4G?x8YY<6NFv#vDa?5t-Qi)StnEwYfai!47k06?QcNX5MYD34BcHGo$9^JX1Z z3wMSM<4N$F+m}E@i7!-S{;hm*BV;@aUz%jwi{_q%k@|tG!#PWObGLs527rw%W~+$a z4~DkoegIu#y@gE0G}#>o&N!$b+o%T~!K~-D*6SUOE{SMd7FM zH~40Ddsu!zRJHoZ$W}>d-@LSiTZ}xg+ok!{V}^b>h$X3E%3o@hkh^vzcFLN()MXCs zG7bH6$+SdevLN?P*s-A;N(Dy;MDhc}ENqFFsdsb44x=($w)I)cMR?lw2jhBDM$@Cv zo2yb|yXh=|3d_@U%MJ-(uXGC#oDat&6Es_+*kt%o;Yk+(tNg7k99nXfkaD`~pTv$s2Y@OQpGhSwdwp)`< zxA`35^S(M`Xuv%c-4y=#tEOVsUt=aKuywpdnL(a=N7ntYRTi8bfJ?<)qN%p6nRxWD z8l>JzP>`OYN%hD>jCRFc!nPO>%Fo+Ps^<PB2QDia!3wZp`_Y^K22Vaw#E3&PWyVC#9^^=8(U%-VVV3qY(#*R#Pd$B9l`YEd`p1 zx=aV7EsP9`UGfm(Art3Nm(UAiZgIXi8$Pc2?m)mPm~?B%hMWc7)s{`qiRFjuZ>Y^W zMw=@{sFYEW5N;pSeWpHTfDp8Jub>{0MeF;Vvw)7V@IHgHC-C}FjgKbxf|H)0u7sd@ zLi0H5Jj+I{&xqk;$&DW))3{hNTczU%x6V*+Fyb)A33M!pD;H5AdMXGD1Ps$aqiZee z@)pK?!`{hS;QI>*pi^X#$$VPze%gx0_XbL+oSg5?oB#eD_jtjBA%(|?^l3@u@mWG( zX#oSYkq?P-P9VZZpTt5guYaj~p6T&KL2(ReV1|{WcXstpVr<;GBGQtyprJJ}^Qt_Z zg$gdc{GG=ccBanpgkc`gj@Z53|3aSqzfx)c<4CA)clH6Tmw9mQ?52H9=`Y}G1-YEK zFsxvzp;Yq^`@ew6e*S$2lV2bAPd~@6uNYNfB${9BqCy>B5HRnF}pWVke2%w z(0w+`NX`hM@%Ht6g5C8mAiaC*N+(=c;Po&GEH-GU=fC*Bj(_Mg5+^(@R7cs;w{x@=_CCWW8lYw>;#Cvaan+gfA`?|*(+ zMn?AyTQ6fJXDZ~P(_mXo#Z0SRb~XY>UKAWHm0r!#E6c{%`y1r)6$Y`dzXe=*fo-?` z*x^Zqq)YmF(K)rhMQCvKYgNv0JXvFxS->{t;jrHd%&kUV(#{#82p95S2w`HHO|YY) zC_jTTFYt1!#pF|2NXK5Xf$R#Z9+b|jj@n_D772Eku*-x5bc!tmN~@+nKj49VPel>T z;qWMd4ELWs3WfaGaMpSJrHz= zCXI543Yr{&kOTc)5orkANzD2HD2y~POCWW}56_NROQkqiLO}KwL*GWJW72w>R9K4{ zo0OLX@n_n#Ue=oQCA$RL;@7&5rLQ-nM%ck*INzV2{rb+BCgWT3E~5^&Q*Ab}H<@j? zdRCe$O4Fda9FTW&LZ(sAoAT082|HWpz|TKFhsFlFR3}ZzF0(UvqYUPRpm*t^+%9%+ z7AWehy(Qxm*2kra)8@?+dzp+z4h(6gW1R?G0SQmD0hE6=1zWH>93@v#OKJA$5r|Fc)8k=$V`B%*!s#Brg0ed@t0aKc*e{gJ9 z-=iCHL`N1A-#KGyfm?dFQCGNSnO%D|0`W;s^a!$@r}CdGol_ZN5*o59IyM??G+an$5S+xLtke*uSz-+q>2b6DDl zgN2GTcvZc6$bd_r>B0+T7}V=L`cnU+|Lcr0jyNQl^9}*4s@TJepQGJ8Rl1n)VwK@M zBO=#_bH2b|zJ$@|J#}Wqr<;!EFNs$GC}s20keN&)fV< zum67Ue^wF?IVt*}?Y$zc_N8FW=Go0(CIseJb?b^8*&!(}@92pIfv_Z}@X&7Gt4HgL z3E%c^y~=$A0ecO+$>vOyNTB z{4#)A3J!$`F8AX(N3QaBG?ndz^A1=lEMls6-a#Cqn{B;MxCG08+SD)YyB!~jN3To? z$8}=K|J-HZC9?;s#}$^zKh}t@--=Fsw_Z#bKB_@4?~6YYe`qZBbNno^OuvXu`wQR# zDxrBl-PYv{ZNVw@!2TRLhU9WO4l0d(}E)V8ew4a@O&MWSHdsPl16;vf@ z^-+Ny1Q>Uk$#POF*a$Z8$zoc$>Fl~W%G$tW%S|p3o+ki2LA(XW(y}Es*SN{ax3JMS z0x-|(d;lCNvJrZp@WnGS3(LqE7(_~1Ywl?ATbxy1{@T_hey@ewWI)GlTy&S;SG6^} zj!y4Z1nyiY&v8d%MCokaH_QRqXOSp=!o2mFjI z4D_I3tFR7=J_8v_kd=k)sozSAOdeanEzp!8c_vbRH#G~OgMbOkZnfwt?StSR0(|ZX zc2J>!SSRMRYWQx{tfP&tZ>i13rpRw&$txdPqj+MlY^}G9k)cvIt3el?JA_k&8WMj8 z4+T-RS748ts;USGU!Oe>@szf(<__7TW{=O7nmNpcZIB|Cw%@FAlYio}x&Fwo-~PB{ zhrx-uvMykRO*=s7I6DKQ7g+qr^WM$uVyr=-S_eG>arg66x(yo!aaSm2u{vAzm6IK} zEF9Lby#!>C-Rm!FxB%5r$$SkvyS*{jDjs%fS;0nf&Uflb%U5$R*qE`+HP*b_)2qbT zY^66oE7ev}`9m2#YCXTQRz0M7Awr8eMl>tUfi|#!A0}Dk%;cxyDxHg9pR?i+?j(|< zHJ2R~I^lLIO%a{Z$20%D>_Qb`{1dAr?J+f~lR>Sl?7Z3XOz09m#CAz_pXr7A&d5H? z%VM&dGD~gZ+afd{rpmKON~cm{&5OTDZ3XncQGi5*=z*VBV{t#~gH-8hQ&Xmo^jGi0 zpdMPW-seLWDQQ49v_&(=$fx!l)_N1?bo-6-E))bWC%D}N@+^X1UrN(SA0L?HCQrr# z?HmZdl5l*#^GcYnkgM=xsMM7whZA~=h>h5pvWz>Ws+1kZ91$4Fb;V9q-BSTj6e`iF zotQ0i9GOU?Owv<}^8WlSO<=>v3pSUr%NpgZrX$Zizm^oKTrxQigz)BjjkG25tk%kp zD$he2=2|p@7RYcAV5YXLmiQ^Fb5T&?dU*^=`-E|Ez+Zof=aT2g!VIGM>@b);tgg== zi^+QE94|s4d4Sbn?h8cc{7y=o%YcxAa~>}wBftWBF_}?`pME*&y|j&03$S`EHH6NP z_MBSTlV?Eiu-Hhd&^Tv=qvpA&a?63n6`~6(n0TEOZkM})&$zdI`Urc7Cg@Z|0 zxUTIaI6aQu%cj?b)o!8`Y0~=wZpF!y^l-&_XL6QkCj8ZI@{!mq3*<}8ZY*w=LviKf zo5lDG4BpfQaZ!?5^xg8J#SMYs$YJSJ|5UX!Aa{3al$L=rKWFsqVLy#v1p1N_;Ci%yrS(v4$P1{Fk>?Hu z8}l@S>o005=H?|ZUCOQuA_o=l((}8WJMk+f+9 z&Y@>Dh7mPS+^<`iYmFecGJkg+q!d9zs_i27aOw@VuVGxR$gAvXDFF;Y_tJ5D8 z_*&*IG#rPJ3EQEpO~fms^yBit$QBpoO7NGMY8w}tQ-H>X#0ysxSh1h9<-b6>cKU6q z^K-fM%0dZI@2j|lf+~he)+G%wa8dmW#q*PwP_g9k^4ji3X({oC!?UBpT7DeUY;*|SD&cYp(Ju6i z4z%J7(=rcrJo=p)tWF>tDEa|A2oeR9gsEjNWM~&^Cm8e21;|U}*@JU8#D9pq(;iU` z!h0oRMMMc8J)^D9uur4)i%tet(Bl;HX}g{-NuS3d&OD5b*4Ex@P`I>4by(CETZiny zau0he>u1z8WHl-}BLqYQ(1e?c+IE*>`atZW_ijXU_6;-$29?G91>ny|9~CKURcYyd zu;^&t>aaZ+s!kh}3EN&+vLd^x9FO+a`M5qQ0qz|(9yEej#Wpn(LCMp`JFKsTTA8Jv z4Eyh_u1pl7&eV8pJmvc5R*Y&$?^~g#3iIf#z(A5Pre$2r2#y;Gg4ZTgwMNUD{q|sG zE~7e1AFgDk|4Nos(@X!;3@$`Sd^o`3qqB>6$y|TItHRt)A)#2V43UH&q!+m_$;t5N zj*Av)pPQIdUI-^)00UeSokBt{3Vd$aGxg0AQ0Y=|baKM}c27+K6L&nI7b8R;9Bf1c zd1dE@^ieMkalSc2!L60b3WX=rT)q&_BZ6#M=(CjyDVbgP8y`^enF@o_PX&rST0}`q zmmX5PU`TlD7qgz-%$&6A)aErgLVYpB%J2MER4M3tE|S_D!c_;&!{8KKy4h2e01b@Z z?u?B4ry?4QWjvhx0yliRsElie{k3!DqEotzZ0Ffcu&3d)LS(0RO1X^FC zjn2Ti!drSyCO_MzFPGR60Wv! zDPTD+u6H^VO7N-PA<+Mv)W*$#t+B5t*)!P9kUtAGK$dCQ(<@?5q=w22&o+_L4`gqD z=r`-UV5da5&x;9ViVS3sl+a!smRTK9UqGOwnbTyY zBEsednkr^=VKk%%deCv2x#73~Pr+i3ZjR{F=U}<6*tq1pXLHK)o@svH`)KiX z@-$~R*G3V2Mb1BVzZ`c#ScquM+A(QBtp+-F7YtB=QiJRjD8kK1$b6ogd;(Wvp!Q@p zWsH$Ko6-U|R2nWNYnRfwq2IStp3}?rtCLB~{3)a+I@m!~u3`;FeZCsF0HrwE3{U6< z!Lqo1%HD?Gy}a5D8>uH!G_Zpn*?O%E63p zXDsYg!mV7>k=^8uVvdh}loif5^-Rp^QyDzwO4|sg@)Llm&;RnP53P%c1X+4EbgH=}cV# zb(`rr-##cJY^1E{m2e;IW&7o_@>>lt-QpHYR~;b@t-Xw9U| zEJE#gv?{I6;AN!qyFwFdjpcAGtoD`-dyNuy#mjFs;}H3>!5pRAR?qAZhruGZ%cs-Lf3ESu$8uQYkU@ zZMlFD`33a$sgeytaQvCKSds@?gM3~&YYDo9fZrVdQm=wC6HYHIAgcOIYDnj1WvQuY ztd8cYR=+W9IZ4Ekrd(eGCK#&*pEOH71u4beZ?n^n(q}i(9rw-6~Tj5L7l`kR% zyd!2*KVL6<5Qm_u8I@p00lX9|EXU!-Q|BYX<`$TQ03S^u7UV7ZrUHq_0EfKcE4~>9 z2Pq2uXOY?l&Gfy>014macD1NP{FG#2&0y%{P9L)&ifCyV2PVof{!E6T~u2yKHtK-OWxKggI zwjd%)d^4TzH&H4wnyNSMSzKctQEg0U)n}z4IeM0WV=$_d^oZ`kZ;?&XP(&iMT{!&a zX4!L|lfmC=YC23eyKHxhEzmo=@sELcA(equ`PYJ|uGh~?1Orqq!oilSb2yX|>?E<8 zcoY=5YTDCce+Wf589ycPN=0>D1*E_omI~i;)U$Vg$W3rhJ0fR4Rslv5wtJv*J z(V_s~X2zPlMPj@8y=($+Cw`-x78s)^;k9vUkp-MGEHQe_zOuuFiFN^IygUg23T-=3 zPh_;Eaz-g}=(p!9?6dKYiuv%~4G=^A{nhCDh6q6J*JZF8P zM$de0SEbqNvi#1X#k-1=!aG~DZ}n$c78f&igyA;P*T zUJEU^)w;gYL$P;o#r~w@$u2c6ZDlkW^L@mM$3hxQO8EUCKHdc{KxVIH*f0 zCZ>SpueIu#U~R|#LWcEL(@a77d=fcwHfNog-r$qJ_YWEHDq4n)QEh2x*&S0)IXbRf zN!n2pGfXK>eW&6z0uI9zd-cA(5VI1)MyvD@ueahjS%vbv36Jm_&I6Sp+Nhu)j67_r zd2Il)9vQsZsSRMDp+OvVlQ%mK))q9a3~m1Au6f{}Tgc|Jl^{)#T%0U=gag?KwE#{{ z>+ecKiic*Wm`{xIaNwEZb0h zxLQ`edz6HK$h}WXcSm}ej8Y?6QXFfp*VyQB&2n&Y*}N~J{_&c7&FW%z5%iB)u$?d- zv zMNjkBI(C!m`~{R9EzZQduhHbpMSoz}L?o2eeMz~$0q$CygK%MyH)zd`5n*;*Zvt}W zCvYu!=B&Z2OR0V-2e%qttR%~cJdR^=h?V#SjSWMD{PCK)88rdYqla9pY=#K( z3?vR^r|biL`lLGO`#Z6z`YRr&y0f$+>5jgZ(tL4FkT$Ud8BwdtaC%ZTZ0oiRZlYfU zBy~%$nEe_>=b~2#%FYrFDfdUxHms5y{T=!(>EIE`6Ef$H);!Fsx=?{RQn*3 z66vLu)?Fj{^{j=E4qIa_)~!sS2t`*D)^LJLKJkNxXS%F}UZUibPle7AiSI)uhTRYD zhsF?R;b&8_qOg$@$UEvTL}eSHmBvQeFY@QwythZ*D$6cMIIni207{q9w1oVA>o@zH_Wo2(2f$@HF!T96eXjhD1M!&0iTcpVNjjj?N%IRdxv-&hi9qrq0 z%|Ufd11)Km?#U*YzS4WH!jo~VVC5xON+L!ZYRNg?@*$+)& z(Yl_^W`!ihCo|sWv?jZvfOtZMjj$!VC_26p3xld7Yd$S(U?)wYpgF3U#}5dxtZr`R z!j7)O@z%ba=~#C+av=#H$*TAM4xI+Ip3tNND`-w@LsXHX+O2==u!WOpK?h`inFD;< z*oVuXxUV(Rwhln?4ez9~lZR%4N*)|bNYN%e6dnBao1~cQ#bmq{9S?8){mbdMWuu(9 z2hHetR2K}Qgj*hW*PrwVhgd~iucXU2ls+ST8|4jzc3k|&p{=@p`Lw|?;&;t2S(N9p zpA{k=Z@V3BYRtt2)83S)CJC`I_eBi+4};hsjc0xSY?d$*m1pnT%!|X(Iun|9Q#(qa z-@-Mx*AWh|Szbj8rUtj;lTO69-zvr-i9X12@xN)JuJ>k;dH)5#XLzo1zk)f%U`CVI z(>ZEhEPrp;^o<1%zBg6tF?XJ&=h7K{Ua6qA4L?4=d(aL^oiJMw;P&un0h_)-PEu=U z`7#>x5O516flyw0?_m4)$su9>8;mKQAJIpjcq)T!R6JQmb(zKX-beqCMFVb8^5x`W z&JE&E(A(NJN%LQTG14#h<@Z0UK`~>?GZudN{kcHbU=pLitNZgm-{KRRjn~1H2;kf^ zCUkJV86*TG3^XK|ClAb$_dj#b(9IU^?!i=fgXD$mrsBZHgMX6bfjRZW(6JnUY`xU{ zT)Pr!{XOUSwRy|r{~!E6oP)`)A%6j~pRUR!gjUC=B1`@PDu5G(lO^c(s63e8J50G$ z|MX|ap_ib*8717|@F*8cRjGC$27S7SpU;xC%+{B5I*b<_?8G<_)=&c_*kpNJ?pXNi zwM1WO!QPfpu8>V|2rG9MFqe8bLmV$#>_nJ`%DAxvl}#1+OI9*uc~;>~_Ok(Mc#jiX zp?qS=_Ch<;SWD8={mGupPn=rC!=$Cpu)gnjqOXJ9b%&ua5l<4nxKzT^6oj6ok~yi0 zUhWCML&Xu-lnwd34EqHVn53EM)13xD+K78|!6|0sCF*n#QL%U(0ya2R@Cwo@?1xqyBvxrh^|6P} z23seUvx~j(_lx@v2%!sbT<%P6u!@V;)1eAh(vAUiI)WOX+`8(TyPT0K@0z$bh9z|WGFj+hBot^=D!0*QcKj(H04(P>v|66Aj8Kq|Zoizy2vuYHl6P+@RVD0MJ#{M!& zf31gy0uJx?on$9bTUU2a8=)dNFbS8}YC-Gch=n4#eho=_PE*2$4tMM8xSMy_V}p)Y zQ^%Z{|Ki#aj3j+F79y!QNsdG8Z-4xLGqg5IsOaB13MZVYHXVkGWA9>@Ukz6BSuW?& zok-nOU>(WO|SJ^KKW$ka(UH0^7$o3e|VG&RJdm5aSM}?FvC_Y5tBG|NV zNM^e^wWYrIRT{xa6o!#Bbd1HbgLt-a!b@nT&Ir^Fp8E_KdOv*_Jwlq%#c2A}mxQki*9|Q-&I(dFV0|f^b1I!m$fz@4;jy-l4a1T3A9c=mGGY!) zHZ2Lu9{bf{6?FrH)_`9lV1}J<$!;)rsu-V$mNGLR$hb0bq5WgqO`R(q_8PGQvFEry zWNXZmQJ4-WR`pCiShDRov-Z*nW)mC46CAFf(s@t+3uq7q^cf_#P`RxrjSF~JjtSZX zO~>MJpDa)xB);*};2e;K7uq`1-2aG*@D+ta7iUu^?{HHwqk;iA%Q{LVBHWYXOz9yE zx~_fmkU2ky!-YIfLe4(iSFLV{9z>w1n64^8keqNC5!Ue0huOW5FNrWqXY`hxN_-?5IG|E)BhQeFNllyT0#R-~Zq zVQ&8hjx3|NyMfyfFxfojf<6y$?GxM~|AO_05{1YI{HGP{-zdEe{~dGrpLoXX0-;y= zBLAla94-?0N6hsY93b|w^%sEl_W6(4-d_N_Er7 zKL+%e3j;VyC!iAfEV)6B(%FP!qx&Ukt6hucS8jKy@9bjyWjeTnQLkbNKlkvY(Ps>W zoS&n)+O>P~?`Aw|5ISL#wwA{9K0Qzz54+T9!e16d0*L$M7!KZ>2NWUf*y|-bjn6hG z-q%VWcJYcG$J8%1q4(?L)L#;genpIydnKL7-KSwbTQWiU2Zp}$Xn(DI! zpgo-zLtk-h*e9cLDJ-i{(uIqQW_0Py6*x{t>KlcNTU}y}F#0T{TMrt$3$myYd#ZZz z?Mpl)(EUY>PMNULFQ`GNZF5NeufETCG*C8hUgrb)Jx%_kJ0qPYpZN zgfB?DO}G=*)jtV6&E@1_nZD??_)aOfGAH4k$YFy?yKANL^GogL{ zN?5gSm+F_w^=-dWQ@tCw#pQS|TeDuv(aV5{jud_FE_Cha&DT zpy``BKnX)+O3^JIY>AvPx>_?RTR0KKtkqVJrRN(<13B}MSm-*1!L$JABh?X8jw=3A z)efA$aeJ0+I;Lo0Xd{e#<^~Y{2(`fU*r#*O2}G+X}9#BN)=+%~nZkiJ^jN z4DrzvP!<}Do~X%_XnBhQK|umVGE!q*08h<5AAgsZgo+KDj0vwq6_SNGv0$?5`QWta zEKy}=@5MVjZ(_Jacmvxbwk3=kR24!~HX>D_XxQND*LTqE?98;rA3tFWSBtGu`fXSc zfLc4D?%`0{{?et8i)=H?*t+ShatuqHmc-9(-_5uq<0X-$)Ve} zo;K>#+W{GbKC|f@;9`ia*NY=Xm*RLeprkTD30-YSazn-y4j;U-$-K|XVhJz)RywHc z3y-=vZeR(?KxZ7au&NCgc`ptFAyjehN(iHZx_&q+j|zE@^=3J?hM*|lHQ1nxiJYRf zQ2B|V3{q@@12SMqS1eNMCg(720)r(bBH_!}g&j$&bzhk8U%>TRLf^|-x3`bq+~?t! zUmUGb8|;WJj%e(WK|k5O zT~AUP=z5B$sYp&EP*JR(>#AX!rnNgd%&8n|L>g{|lvCIl@_6Dm{petC=)qx(vK(~* zAqxY>B9a#hX-3cHGBSOOwxS@Td47XVLL$h5TpfrIb=!nbiDyveGulaY z8r@Dn^zY)y3$XKebrjxOFal=v2k-48;yNmjFp-#0TA%#t>^0k>y&MhAD!z>_GB?#!_-%RzYq-+rlqg0NM@5~&Ta0=zF zUOdiUT&1)5kjWyROxjG<>v>#p4@sI|l(bqxO*w zis^2{7qF((w`LH+&Zak~$;X8Nw*xCJ2mN zyg!h`9*9qa8Ir4lk!A)QyL(58*1FE8pG*Dxo$G|)A@)_)zgSfLKdrD5`LEml>)x2Kxoe}Ioa|B=&tUfj`&upn zgmF$@Xv|tN$-SmUEsf-lA>dlqf88HRvEyU{Vx7eZXHZ5u8pL=^32#^WZ$lN}de(c~ zc-)xaE9l1o%cFRmsajGC7a?GvSpy6$IZ?m=ScuCT=AO77Dj*B9G{{jkxLFcz|6}t% z_Svqivot)<3!xSy&O4@?cz&Wi{_NXD$p~?)h9WqUtqZxXGxC^5Dtstf#zny$vr>w& zQ7r9=RW-b#kN*@@7fi#m(c_M0z5~;^vfM}z@3N(^QZ(Fl5J~&aL(kfBNk4O%OZtPv zbZh4jg-A^# zfL#c7a~a_15ip?w3j8?0Ak@wib9-tWCEkYtB!*0YPO_&#zDa;?QHZgv^ zsN*M^!l+Y?fwHaM!(=#U9HUT}m1hGMWQPgRhgEOt<>JTBz;hoPt;*n3A~8%@w}o3E zSjq7*AFsmOy+kCUD<|?!NBvO5(K_WN`T9!|lbNdG8>v%YhG8Oe#PdY01GWV^KAtHg zf}9=H5pv z_keI^c$>H&*2QgUE<-2S$v{m5yLJ(q-!&;0%ko9A@7$ag`X$ufx!eWG%2dj4ew%%> zIU*?q_<>Gapoa=If|q3I@Eyi%Km&p?UNCd~1&Ql#$q43J*7DE>_HyEf!4fqvy&NMT zs^;=2@AA>1Psw(1hah&vhOF-rDxJ%A6n{W4lFY8C+@SfEBF+9hBvJ|ycm}2_N~#D^ zo3&pet+`SlIHpOp6YKylmvICIR8|+o69D55r2O5wZ=wy(Zg>P5dv6Iec+N=WA@#` z6!>c$$2n?NUuQBOAdz?mxi0^sfNd#yE7FLZ)DI|U4ULg5L24?C!tRa$tz8HD;=Byc z>~!as!QGlFe!WEZY4`SUcDt24v)%`$9AZufxE0ao%_SR$)3Weoyz8!ouadnDd(=9* zHw1*3R+Y#_AOtxMXpGqP9dcx>yo)xe*E6?7KukhARvVIweK4XL9; z2Ky5tHjDKqV}bwH-d6|3^?ZBI41?R??gV$&Ft`&ScyI}>K@$jrOMu`IB)GeKGB||b z?(PsYNPuJ~@B7~Gy{)%Z`^RqWUpqBbcWzJLo-^HjZ=XKrb3P~i+fadrJq&`)c1Hf` zP^n$g54GY8B$BVHd?QTJElEn`yQ%>994S(mf8wifV}yKo}QLFm3G{8s*Mq_`jOU=+NZj@QM$K8>7MD1eU){#AXkXz`#-Gy3;z6>GPv6(z&eW zl6$X>4d;smy1V_j0~=Y6M*p;NQ}1slV`K()e9);miXhTr71(}`Wc`3by;!p@|_zR*&WJb7ayOzm~<<6@|Z+*3#6( z)s_qo^A(Cn;dMs+HPTDF6990-J%HnQpnYwhYA0J6r+Rj$YcHb3W^##ss2>TP(`K_j zOWDJ~P9TEzIG;t8w>SU~j&#-`2j5eR%x=2g+Oq@pv zIek%~UyTEpv-N+cqn8k`!3ko-$MpM{ybN#p@wTAqzRa1 zRt{%H7dCwjjj9T&)DFE%rE*{u6%P3>jw`FZnyZH*xp&PAGB!vKqeQ`bmSD<+DyOvK zFzP9fD@}{u37lp-63ga9KD4l8ppVs0!%)3%Kf`83rlL1zbyPu)YPQ2DJtk99ozZ$s zFP~{Dd7H>lTg|{{HX#~c&%zq3aG)+qfzQlDgYFs@<^pUJY!DCouHBb^if&!M?;(Jj ziwb}sL4gH~GF#YM-Yf%jB{3REj&^j38JKN6yaW=3c}uNYjd%L8@%gL1y#l3!bwjOG z%{Ht@>I<|G{g9{xL1Tj-*FbZ!0fj&jzQbn+D_Q$)ZP5nFCV3pG#y=E-t^pe$fE_@< zBL0#H4NKuA2qO37h@8pw@**Q9)Cdoo>v}@E;CZsX%e?wpyYf6cCH_zMqVUzmblxup zq50+VLME2P0D&zX0VE-c&W@B>W<{$?_kjyOW7=nkHA!snFs|Y|VaK{|)6ylt|3_VI zWPwsN!(s{J!1$6k zigej}Nu7?FX%vtxRJL0heut^1cj&wI|$*MZKz2RoJ?0uwJN%wE5@xE^FBsM(C&8{&g*39(jj4nSjg+{$R`d_~z zeWSmSh6fgB)>P?8A(drxhqE=Zg_;0y+(VNEGB}axLCk)*mQEe)iUbFA!9hSOfjK_F zqEu5rlt-tL{Q}-+zbW+fV7NrjXH1c@@&wV=3g_dj7aEjJgqQ`zVjeYM{GlTA3ML{$ zJtgKO1%0d;_HSSE%$m_Fl?$*fK@>g&@SaQ0)uwE8F^VR~Tm188%!+A)~Z_Cf~~X;)c)QfVJjB_YJU#REzuE zTpcB_xS7?L4JVTFy0%+z8m~2T+%jK_yg(;q2M61&U09|aagY$S1v+L^nZ5^=F}n}H z4hZd8HvhR)Bs#Zh+NP3b*i)Xp{6(a16Gl=AZ zIiygKS+&Gojk;Y(>2_OIA{ccVc|cB`Jh3iHYz0rTE;5r=*tdu!3`5q>Yxd_TVx~J8 z{rfjtiFxq({T$UVL8+J)_ZfNXr(xKM)@p3-n%ziPl{W86&=#|8EC~*gr`!nu8 zdmDsglTsv2BXmz;BaIHpEZC7g!4EGwofHf`;aHeLq=C+gE? zRAU%xhX5f~(X{1{L9HA}rz4``Vq<3~ms`~ayQotD&vChzk65)x-i;Z4u9C_lV z$qFYL7ZZuNSV0r^pg@|54L2k1ThalA2=i{Qs}J&oJy>$PT`1DqY*5w!S1)^e=de4* z1``(c`_4nD2i}$kBDfc{H#)pDplahGJ{)MX$w*AZt)g8fggFklCXb%$Ks=tV!9)9zmQ^1XGWA zzR?GhThmhs(8d zAt^KDbxPc7ZN%m|7teJpFtXJmoDfz34!7WS4U@DiSp`E^^L!!q%E@d`y*wY_`!=2{ z(p(5*GKbbx4;zvvaXO%MdF;|ze36RnLjx7nTXS>rt$b`5gQ#mOti}ZvPyT>6a=7>D zgx=Wd>5LFyzEy25H73m)5UOH(_HXoIAuc`W%o2k-)?7s1HFgO8=F!p87x!U_44O9- z^|Pp44_JM`_^NvL@@R9?kutl)c-uEjx)3_0aXu>h@F_L0?#x1X0AJyX8yY?3w1KtS zyAY4)fh{0@(GU4^O%Nq1v&<;T*T!E5SzE_J=++W5kzr8!HntJiC+d+W&0xK}T7zulby}x+E62zPN z?OeMAPWMW18hL1LiL1uhzGy$j&sUzzB}Zx5Vo++(O$JNTwJof!G`<%pFVeu3ByO05 z#T5^Rl7CK8KXN-JM`7hSJpuxiLUD9QBrnyg6&=5Fe^HT8Z;ETz`vWknNHBm=+iW8; z8xZ|~UWr}$R3}_YWkA@cBL4g0=@6pMye9QUc<%YemMyWq~T zQVtNj)uWXtTRWU7*D*7PZ~T+(_d31g?8h7__s#9+Hh)WOB*%Zo#qteaPd#;#q8!dJ zoWD4Od-Npd%2v|<_LqG6OluBcRqy+?Eqg$#jj@Q(r3U{1o>ZsGTwA~1Z;5zN>L(v? zl+}`BZ+FKQ#|r9by7iYN1(_^>O_%xi{!JeL5z1=tCjo$2>t(N6uBy!>`6U5hCLBYz z?XR@{Bmc-@`@P{ms{cnp-M0j9&!UN%^o4T2H0LI2g|8%`~4DR z@)~<6Me0M27#cGILm0L8`7cQ?49+&6!X>VDc#s-p?Hu9%l8wAC0T5hd^Uiwg(i5Qg zjODq$At48X2%=<$$~{ir>NV={hr90Tte8i@AAp~=G3rWB!AGM~kmTJTK+F?G62pyK zelRO{z0-pgRF3;Tx@4zkcWe>;n&&%`r|h9nKSbf%`GMAZ-rOn=nxcQY!~jxRqgY2# z4Cox)U{bGL3~4LM!>PXdXR!FAmyb+X=Cnh2HLdWB{w3vq463EXH>!&9n#e zuI9($<&`HrlJ@)Cc5O-NMA)_iX$BpkeT)!{Xw!B7pP@Iu&dX>b`T5U;MGQ(nW?Qpi z-8y-)R?5H1{>$7slN&)u!wpaYM(V|C9qfx#$knO?ByTT@q39(GL5PT~Rigz!CN6F5 z5M^<`H?*hgp?DfdBQE?S z1|^D}byJ#;&z3pj5kKL`gtgtayk zI{k%r;qr60H|{on4c|^<)@T1Xl|9Io&PKQf_{04!SFc zaOW|S9pASilCaAW&gZPj_%ev$SjQ8? zEAq87R=u_arQtMF6lkV{#l&Z=R$GJN9r3A^;@e)u1DZ9=qDsK!*(NB3F0y-Q9o1M-#yi`J*J1+obKElXIu7qNeUsv6q^od2 zpo2j&SI3z_LKRd;tm1CXAsO3Ad3+v=pqSDnk-vC5Dm)ZoY0s>fHP0XMVx2K~Gl-$L z!9==O#}=7BTm33CUZ_ltI%$MLxAv&{OF1YPKic{{=A@oit^jMB$>k}Kfg z)E25H^>8v3eyk=~0YUgT@VsLLP|Y?4D^-}W%)q`#>(Y}iVm!eV{(y_5J+;>7rIGc| z)b`J0NZ$BW9VfP{3%Jw52+t4V*{b)c07!(?LZ z?t6ugI`!3ffjR`&N_EDEO45C;wV$f>-3D-C_&Y1pEIA5*Zh%5pI$_7T%b<;(-1mo8 zU7lt)FBs{Q5vok9X$*u96x^%(ytRQnLr^K6X>IlGh8>o&l@ha%&)}96y=Xhn21@D0 zMt*`7VdS;klz@g?&v!?dzICs2&!u&y&V-EJ%r@%xT+RxUn#k~K0YT2poBVSQUY|}} zr~ph3j3+1OmK>qHko&_4aZje)-B({Y+fT6D~Va|9S49m-t!%q`1c+pTqAicgj%4g zytzDGpeg69kEngW^FG$V0F(aa24b*c*7 z-pCId3;@s&Ha5q@R{1=?T@!3O5p3-ORLIQQ#Y}UeAj?DL28-7jibmo zw#y)>mnIjQ*C)$+8q3Y(lt|7ugeA*Fx<+67NS@i7Qa|nUkHF0xSg4nCe`ZH`m~+K9 zlvUx>y>g@1uZvy@Eu_YqK3d_ z#4{ulfga-jo&Ucfra@55AiGa|*778L+;1oQ5pns;t>xPP>($`^ZNlO|i^w_O%r)qD z4Jrc0?mPAjf-VX4e77%|^YXvHfplu1@!JTpYY)`C{nZEZipcKWB8~p1?H|8(^(A_; z8Ga7S((1k}Udy?zbm5b!43hbPdKlxvr&k%TT_$_@_%2vhX5N{kt<(P!@Y8Qm%>Dg> zuS|8+iW@B(GTx4w?|4IsBn{X2;)>a?t^qek^B)-eqz{FWU8Nm^A*t9E2J@+mMCG+C zM4i-l;iXgP3{LCwe&G{OD+p3n_mLs1s6lmalvFW z^Req6hmPl4e&74D>&(rc-%b;hwW(|+%lWP0!q0)s9wWgOtudUOU&LM2fQ0HOa2b)9yJ95gw!obdSZ7xfK zit_3WG5{u#wLhGZ<5%r3aS}5ODYb;TKim;Y(wZoui=iZS@{>OC3?I(L1BlDYcY21^ z`Z4$f7?iytgX!(5(>b}@MR$Ikw4uJ~V-B`gX`d9AqqCoNZ_AONVcTXAmo*44)OjN- zm+WTV=wpau`D&NvO>-D74*}BW`&2y&I^TJk4Se|GLLiNz0)qawm;aMe(eT4BCf(ju zmW0urCzhS}I|y9B&v#Lw8#GJ?k-IK99rLv|07arJ$z;ap*)P=d%LC7SNNj~7j>WfG zC4!`1)3&@qQjH6O7J27a7jdou+(ub8gbdiI{6J_2;+CU zlz5>qT`eGFW*%_ww9d>PU3 zuPfQBLg~4-UG@ekbAa=%6r9?JBA61TZrWqUUOQM2AukjG#;9P>)>R=x<|otV--%RC z-DBg`EsR5xa$N;)Nb6sy5VJSYjhVRByXmiVDmm7Ce4cAbj_Qq*QAbvK0|Nge80sFPd60-;2n@fPIn;D z{3ExC?p{~tBZmR$^dA7iXL+AxVJ~|mdd9tm0%0ktc=!WA5AvA#g#<#=EdIJjeqVkp z3k6jS{QZEW6d5&sWo~^Sl~d7RR<-@b}puNSk+>6LiTJoUt*C6X0)tN7fy z&uLd)zXOr&2w~gz4TceUqBuTtP(z00aTa9n2doT_NHu?yXuyZ*P$N=Ya!oP5cpWc! z81uE(>uy)MkDKZ%g54#!JW-Qqfd@L5c*{G z2*tv`^e%%v!{>RV9q@Eid0GxMFm79tejX75*RKM;;47x2)ZUNJef+|~bFj=9=v=Zc zIr3}V<9EZ#*UDS&<9|+S^FbkI?)JCm7TYe`GzK+4H$?B~o%H7pH+Ov027!1TdzLgI z;Ak)0Q4Msdji4K;=u#34=S21=YbB3}&&ywrA%w}H`Bju@4 zwOAajdzjWr`FJG-+ou7Y@-n<4YHK+`U!`AiANz#P0VwpPu9ZDCYcgSiP~}jRYN^z06c=OU!B%@+b+FqK^WL6|xaG{J5zw*aGiGD=GG!oJ|ntm8hnEx(z)eW!@C$Tsws+O;(1 z77^c#u&5vWwR~5-088ZnOJ4fwdfb& zH*#Yxb|!Vw(#dgtd>!#|cCoA#Ixv>gqW27paP4V4h{dU>*&qX(Wu&Qdwf&a1lTM@k zMy`JL8|CnL^6Kpqzt6&=5NChj6=5eCp4ImwzdJ-%Y}{g0tA_$as);S|WH+*lV4kwo zpbryaN^7$2Ry+=q4&t)!hA!Es2{$V~DtY2aMRa+e6Ed?QIP}cKgTNt_3eJsN7W+FI zt@2m~8OJcR!$lCLrk7@?NU0U^md?Hjt$IsljnQ@+}nh5QbsaB*hef&ws zDq&6I)qq_74xtEHFlTeFlUhQfw&%j`ix*VD8K%r^UY9EJEZ)!UAevvMYls`2=ej#Vp}mzBhHHc-gNLhAjpkjjmB#<@r{T{4RTnAomBne+egDsZ;*p!|bJ3S-JG#1-Z`T+~q=u*QW9+(}j z2$K<4u6z^b1hi~okUq>R=6r2JucPVXH|ESF?MePF-ET5O)}NfRtDfy!5VHDv1Hk4< zX~f&82Q?{(x|%+bbkIKDHzK@>_c34ngZOk<7$PXY=RywNOMjz`knXlBO}?CNCjSxM zli-;mA~;_yO@N(_fCbro6mfWU!baF66ml9rViGO`dEn`iO=)p}dI;FcthoaScJy-q zY>vk)GGkWiE~;hiix?n)Qx}pj$l*;i5Y>3z*vy2#72ss|+}Wn3l?ERWPPbQ+3V^Te z86Xk1U#~H1Ln$pV2zAw)&0nj|Zxa2y_v`15KfWB?AHfLU1x0!i+=^s~b8y=g?6jYc zr3X3%&y<&AYw#>20fuwgI@*mqR;A_V9D-p-f}R1bhr^a=NjQYKkbG&rN{i!y=*gckYGAjY z2zS1fgXt|yB>7ZS1*UnCypWRtI&GJTG#EYnvPeqK7}TxC&+&ho(r6?;al)Ba_}8t? z0iyLb^dNJyin1V_)n|&Prp0BP@Yoli0K2efRI) z&ATGym?2DkVlE@7T}v*&w-ze#l$^An1{qU#yF79%a+-s#aVoYBJfc~-)}QI6Io@C~ zfm@sUaN-giJBFZ0fehX^?kxtvg!bmlnyuOJEj|QDnnf^^*jY$U4p_CnF)OP@6=(da z^Tg1E=y~mr>=o7r=`RZwtob@^ps?qmXw|#)SOhfQ6CRuGOJgmh4nIWgKm!(-rfzPG zJO^o7oCl@b^Uv1glqfP!#w$#7)DF8Ue3`iOfN=>Q@{w?uh`VQhLICk7pW|LOA?RQY zg0mcwn}b%T&1iktFu_Lih-g#s+> zK<8*Oj_Xljrf%tUDWkPv(gRgm#d-X-8HFnyI(wsFxE=*8ia;(J-#xR$K{^i^Ri@+9 z{erzGH?Q7xdI^$uo|hJvU@`Kd`?VnPc2)P?@Jjy=wa-7x`R}ur&kQM zdbQhrKc%B(@9YDyY~veRFY;{mNVn)JBE7jW0H&E^?`)lD0rYhtg6>V4o@c?tsOnpk zJR)jYXOf%P`IeM&%gk=Z zJE8SSBOPCg!fud%m~;&%yGKV&a|3{c&luSh5)0k&`b6|dzFk@?#{a-QgHF}Bv$y|q zj+9*7FG_6FAfyq=0#7l(t5d5}b0(4~S_LU03#VRabb_ZLg~>@MQ$`blANc}Vw9y9E zJ%@A}5mnY#{&GSevCv^6pb-cR24X!BFZJBPBny>O@OW$c?Q=R?GPBf+8>+SW?gQ%&3e~5d?g1P zq`utYt2sqCVxG6u51&pJZ|t&@Ft!;eCqe|E)d}j7br`*;*4i5RT-BUTdIHwdMxS1j zr1A3*&_N80`H}nRzd3wl$hZb>WctYuFaw)gkU8^0soMv6d_N~JZi&f? ziS~ZsL-An|L)8aa*HkxT0tw<4Dw>!9FP z&(1=zR)zjtG_9gClnP3R;&Y7wop0%pAIZRZjI{#l+g%vJVJsHPoLk#Pw;4l>*>Wbv z_4EtGbIy2n7I7|@30JfbFGW`zd(v}y=E?nnPF*;5_ZQ2LbqR6&kyk(sNTkAz%}HQtkX;iF|^j!}MKfoX-0S%r*79kF?`ogR!u ziVfv%2$)II{Ab^rO~jIr2Vqc*TCjf(Z@xLaCP7wyJt0oMkf!&#F-n&rPid~WmQt(T z7K)OJrf!`$oSFZuF_+b8WJo^BQ{UR_eYFO|EgLkG9i^>7XrEVi<81PscN)WfA9GS< zJBow0Rvlg8fVL-$Jl_1dkqWFZM%<3X;uDJM%@P6dMvm$S_e?J)>=b z=BfA}K&}e{XSB0R$H}esPIa1+Ch=svW?WHnvroH>rsXc3BOZ!O48VcY>{L6X!r&g7 zR?EGnVd_E3h1eys5}^x0_3RjpnxJYv!k5h&KZUvxR1)WW7i?`?RaR)o*^E!7J<+jI@F@xH?g zQr5+wm6m?VMJEkRW{}$`^1W&wY7}ZJauX5*J+;LA2t@-BspjJ70|r=*>*F!6OLG41 zOK5CQ#!tV_x7Z~z!jG%5coj{ge^FB_ex~bDo`(6~OE!Jj5A!SidVlwKeWcArHUVY~ z8zlkFSyb)z{nmZ>JjPE8#yR?OiBP|UOtXR+B_l$W@+Nwhu%7#AVt%`***tmbd_KOI zvm99CLJ@3<0^=q#Z{*=K;QOAxve!s|FUwNzWLK7b@ZkH-%oR^FMuR}OWW#krXXMxF zrXUPucKiw7gphr2ZyPW@lkF4xChOpiBY=)_r7xHw z8XCXB#s#Dt)RB=tX|}(%I_5G!RubBW4)|afXu`vzum$<4xb(v;Uj|;tsJ0u*d`RD( z=$2}o=22*SOSeq!Y`F+>Oy@YDk!i#Y*mOB~$)cr*P}qYb=87EX2*~{6_<|zYHp)>~ z2g(of+fbSc{kb(c60A(DmgyN9iMo_C=fwQgL=|`pB=PW$o|D%;r!UoH3)396_0pA= zCyg7hc$L6mN{Ge(g2kK|ja@7ded$5p0Ht>aL1G>y^Gx96@NptY0if&FA7lA0|4|Aa zcf& zltC*jL1wjjt}%rEuxASK=w!V)%lI2|_2>E{_kTAY-#LwGjD6Q%o!lgOpCI;1& zYfm3-B}9sc?zq-l&DNsg>YY4q6%(7yPDG847^nVOnI2v1Q6SXfo95+&eLa5;tYiad z(O67nviWW3$L55`D^hrMmsewGaaO0w+XZ+?t-Gr80OYN|7-%Q0q`QH!?WVCD{X%8g z7D=ffb8@^nx&D@55@SnU#Tz_1Iw(#7cN)03Um6u4<~%?Gn3vuI5EWsY9|U!hPOj?w zG@d=iH$}If0<<2B<%F!u<|cU>7i7#hapCPlD?SR}SkWfn#Ad}PM0(%}LhzW@s7Nih zt*lJ2djz2Fa=M-6I9ooB2N#X1M?jfaij?$dYg%>Wa&~7k33?BoN=4y$aWD_zFqAGv z^z+`eqbZgr-HKnZ?viSih*4uq9|ryuQtoT`mEOIjij%s?!uTl@YwoUsv&#jEl1HYU zI#jw2?p?b`4&D?vHsQ|<-cXf$!x#5G7IbMpi08yI7k-sjDJ~X1MU8oxDJtmFjBbU(d;Kz9q_og^9 z6)ZP{<4*cYaEDUHDEsx8XNe*a`Dd1whY3Yo_VH&YZyZx`+Vx~?4PsRJeXD0HoDKwq zS_~YbEH%Z^=*orXafs6*UN0$AfAFto^G<%4F|$o{<#d>F3_B4A2Q>hnAJw^5SbSKG z+2&>gYvCg)hTC}!bldQi!Pm*a^{ZN^k)`;@gcLZ*BbC_TxkqBYbD3Uo z#Yq*OP`sdTYWZ~2FVwnll;`jV1{nsZ-SC)@Xa9Nfxgwr?CG%~l zESlP7=^I|Jbg}7wuo)L(C@yJDdA6Kw@eOSZgqKo&st6C|c||@chl8U#mV*_XjMq39 zjh=08Km-Tkd6B_YBGNczq3TN=`Btdb8qL4aS=6$ZVI28r6EA6e;ufb8)B&wCd1;@X zYfX_5UF7boRS-9C`ywoFaw_H1xZyQ>3m9{@pr^&|36WjCHXPeHfk)gG)=0Lzf`~3Q>GtJY(im zk*8!0a>jW|?0e#Df*j#uKwbq3s+po;fx%g)03TZ<;evFHKbs0?l}Z-zP~X>;wc%tF z_?ZAzEAywOxAm)(=ieyZAp5>|GHpYEG5~!<63j9~phGiMiL!Yskb%1N-})r&S@~~k z_`lVY?X!2}$(TR08he(1(Dq{f{DMdC|a&tS)&-rqGRg4I$`-5OFS-NlZrn5fO6Y%El~&7 z{5tU?t~BR}H#3NzJMU}7??C&7t2I+E`iweGB8qqynsH3FtT6KG;){*?owJRT_kH5X z{*ay1cyJhMn25Lqljn0+()LvmN}G_%BnvyJR=e1sBmU&D9q-hudCL9oq-7A5FrZT( zGx7=C9s&x|XqzX``UX|#d%IoR(`)Y1V>vsCvC~U5%Z9LxO%5qAz1mwPv=@k!bpHbw zTS+$RO@-~FNdohcJq5bsRP4^e;UDpigGNhjwj=SjOoda06Rc6OAen+(OjRVaxaLUj zIM1uDA!BeB5gA$EKHv2V8EO35xKBkC4)v!>QC_abRn0Z!$?CaXYGQ%RM>W#PII zYwYMOFWp^#97lCD9Z@u{B;zgPOiLbCsvA$<^g3*p^2#@X|3J|ZT7+vd zI!hf7bi%aqHVvr6_lGJw~TQ_7?I{sJCJ-0v8Tc z#sb&R;=kO<+9GDVNm4a9EuYyY?bMn!ETNl?;M=?)h?F-|h$|750PoI1cltLG&b9wk z)&0Ptcta;sUc%9&6#Xv+x;@hcCby;?#DeKpw^Qzq67<{eL{jiF#aPTcTqAAB@(J3) zb0cU4wt+9qWurNCb#71L*d3J&Fe+qe)oyg4jhh9dN_JYa>6`ai3GP*L2CXWYxzr9J zqcsxwLowsh=V%|&jMCp{l9)yf51oy?2fGu&z9F_z3agf*Lj7LswPr%i?Djo9tWyOo zRCmf+r4wjVi_EA2($)uvBsa8C3O0Pv=&?j1f-B1u?9qmJhx+JjWRy8dEHHx12w6EM z`__1Uv@@)aGfD|m^qz1l(O_B>n3z?d2!l{qqpCExL=-9jY%fBY%W7)I#awR z!a|HJw0V>eNeNTpY!e+UHyxKtDzCAZxSU=|I^gsQg`dh|}^zF~D7o-!s9VG?+cAsqFwO1p%U6ahnj41Z8$HQ4isql-nZ5 z4h9DzzpC-u?Nst9MOS{a$6h0a>v+0d*1VkcU2@O-@m0lKBPl*CDwmCDVn-Ct*pECJ z>L>H$@rFnn)i?}Kl)#sMD>T&!n!!H**Q$itdSiRL4(1zr8wRH3dbf>&AD{55997+% zRIdt*ia9zDUYbFcK49|_J>IYz&?o_mA=4PYZlhFic~k1=1wS?dWujnXbHBv3$3li- z7^HIX!U`;3Uc`jF0@%)Ttw;k!WH+4~J$r#|{dlZU)!}0EPN| zm_wa_GPMjAyFE_^og9p(wiO!WQ z&%RkYKTsSN)&#_PMt5F7cpcKG^9hA#^=#~H>;Bni zA=h4uNPBaCN?a||RNf38OV<4wcS3OX z{l7V>B_)b^%Pl+dn;8*fgUGYc4K<8Vla$(Sa+c6Sr-&Y1v3{k4CX3!&Y*IaFC0``` z4v7n?szoO##sroz6f2@I6)p7Ra@4`3_OvtJy3@rb$${F0$8o|>e2$CqfYJ1m7U|64 zh65dPkI$tfzxH=S%3!;#@*#OS{6HvDA&wj*`smT1lzp(`^ihzA=UFr*su70xoYb&M zo;p~jYV+AZMw$Eb^pb43b)N0;W!@4`N_wwB|GE6;5ETf$NDrlVXli1Wm_`Yy@p28J zyvCV64O}E6A|_iTD#Kt$Y!Zn<$G4;`&>#569E|~fy4W!K!fNv zGw@_KM)KMnksio+eOX6x+mwnD{_qEI_VLC5bE%3=G&SxMw}eQO{=PU~t}DJwcf1iU z*mar0j-}K?hQ?nZ?9Et-+P?7?ChhrA#X`i2xq7>UU!s%~in8i<2iBlwzYJyM`K~Fa zDicI!?$-u|=G|vhA^+Md9u)(_lI%An*mjP9m(T*y9wa{s&CqPUV!#1L2Yly6Ds=)& zM1#VT=b45x4o@1NkY>XdU7v;JNr5A(&kHvNPstUK>3Mcn{)wYt8h=J8B7;!6X23%r z{Pzb`YfBuI#aH>KpYi&tRnQ{zqdq=IAwO;xrx;z(p&0x=4D>|&KQsI$6d^@{U7S!$ zCaDVnt+w4NCe~K7bHs*~{5%axHv#%3*cOrX5<}PkY{yM8NP7sh)MS(R)TQ1e&$0+p zD>?zRItZC+y^_zRuWH5HD0u?_v(R>)Z_# zt`(y$83@}B2!Ya%@bUT#h*+Fh2M0Bvwu^wnQrH7EwJ9B5d5_B2?6yuuzlmV@Y zM?vI-D(jMpcC8o3lX$W|7)b|Vtu)^mrF^|K5zJK}b}kcmK!7aosGNF($d76}1aOAB zsLmhDl?VL@Rui_dD+@lxe8pmz+CXKpBMs4-)FafmIly41ALev?^_x8YlUA$Jgl}t` z*9rYKw47_KvX_KTesT=O)VPI@n89-fg+9Yk!6`Svu0q_yeynvc7h>?(pR5=}6~mc@ zkHB~;YVC=c3w3=6MKz0fAw_dii$|gP+c^%CVSstJbHhMd)`=&?FlP((fNe6iUlQNF20#^MEJV> z@z^5oID$v5-8~~Tu_u#UKI+K;!tSznG-B+D_M0$53(1eOzy%wL5RDS8!3wffG!&jt zV@7wF5f^3-)y(~or>GrQ7@@D+c;{O1Jhj#7& literal 0 HcmV?d00001 diff --git a/docs/_assets/images/contribution/create-config.jpg b/docs/_assets/images/contribution/create-config.jpg new file mode 100644 index 0000000000000000000000000000000000000000..60479233ee70fba759eb065f07c42d7c73176a3f GIT binary patch literal 140865 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 z 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

      @->9=2vkN>6@E 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 literal 0 HcmV?d00001 diff --git a/docs/_assets/images/contribution/debug-console.jpg b/docs/_assets/images/contribution/debug-console.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c9a669019d65f4222c92ebb201bf4fc997823371 GIT binary patch literal 157192 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 literal 0 HcmV?d00001 diff --git a/docs/_assets/images/contribution/import-build.jpg b/docs/_assets/images/contribution/import-build.jpg new file mode 100644 index 0000000000000000000000000000000000000000..79be8450cd4ad27f3364d13d47322ed6eedc6b0b GIT binary patch literal 64305 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

    • YZN9Gj~b`T(n_6^vj}il%WyW2hZ@o+c&;L0 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�Tq-EjzXbnm7tLm;-<{(`T& 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 GuKzz@9W{gi literal 0 HcmV?d00001 diff --git a/docs/_docs/contributing/architecture/context.md b/docs/_docs/contributing/architecture/context.md new file mode 100644 index 000000000000..cd38ee437867 --- /dev/null +++ b/docs/_docs/contributing/architecture/context.md @@ -0,0 +1,53 @@ +--- +layout: doc-page +title: Contexts +--- + +`dotc` has almost no global state (with the exception of the name table, +which hashes strings into unique names). Instead, all +essential bits of information that can vary over a compiler [run](./lifecycle.md) are collected +in a `Context` (defined in [Contexts]). + +Most methods in the compiler depend on an implicit anonymous `Context` parameter, +and a typical definition looks like the following: +```scala +import dotty.tools.dotc.Contexts.{Context, ctx} + +def doFoo(using Context): Unit = + val current = ctx.run // access the Context parameter with `ctx` +``` + +## Memory Leaks +> **Careful:** Contexts can be heavy so beware of memory leaks + +It is good practice to ensure that implicit contexts are not +captured in closures or other long-lived objects, in order to avoid space leaks +in the case where a closure can survive several compiler runs (e.g. a +lazy completer for a library class that is never required). In that case, the +convention is that the `Context` be an explicit parameter, to track its usage. + +## Context Properties + +| Context property | description | +|-------------------|----------------------------------------| +| `compilationUnit` | current compilation unit | +| `phase` | current phase | +| `run` | current run | +| `period` | current period | +| `settings` | the config passed to the compiler | +| `reporter` | operations for logging errors/warnings | +| `definitions` | the standard built in definitions | +| `platform` | operations for the underlying platform | +| `tree` | current tree | +| `scope` | current scope | +| `typer` | current typer | +| `owner` | current owner symbol | +| `outer` | outer Context | +| `mode` | type checking mode | +| `typerState` | | +| `searchHistory` | | +| `implicits` | | +| ... | and so on | + + +[Contexts]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Contexts.scala diff --git a/docs/_docs/contributing/architecture/index.md b/docs/_docs/contributing/architecture/index.md new file mode 100644 index 000000000000..9b976cc643cd --- /dev/null +++ b/docs/_docs/contributing/architecture/index.md @@ -0,0 +1,14 @@ +--- +layout: index +title: High Level Architecture +--- + +This chapter of the guide describes the architecture and concepts of `dotc`, +the Scala 3 compiler, including answers to questions such as: +- "What are the transformations that happen to my code?" +- "How do I run a compiler programatically?" +- "What are symbols, denotations, names and types?" +- "What is a compiler phase?" +- "What is the compiler Context?" + +and many more. diff --git a/docs/_docs/contributing/architecture/lifecycle.md b/docs/_docs/contributing/architecture/lifecycle.md new file mode 100644 index 000000000000..2cf58f477da3 --- /dev/null +++ b/docs/_docs/contributing/architecture/lifecycle.md @@ -0,0 +1,90 @@ +--- +layout: doc-page +title: Compiler Overview +--- + +At a high level, `dotc` is an interactive compiler (see [what is a compiler?](../index.md#what-is-a-compiler)), +and can be invoked frequently, for example to answer questions for an IDE, provide REPL completions, +or to manage incremental builds and more. Each of these use cases requires a customised +workflow, but sharing a common core. + +## Introducing the Compiler's Lifecycle + +#### Core +Customisation is provided by extending the [Compiler] class, which maintains an ordered +list of [phases][Phases], and how to [run][Run] them. Each interaction with a compiler +creates a new run, which is a complete iteration of the compiler's phases over a list +of input sources. Each run has the capability to create new definitions or +invalidate older ones, and `dotc` can [track these changes over time](../architecture/time.md). + +#### Runs +During a run, the input sources are converted to [compilation units][CompilationUnit] (i.e. the abstraction of +compiler state associated with each input source); then iteratively: a single phase is applied to +every compilation unit before progressing to the next phase. + +#### Phases +A phase is an abstract transformation over a compilation unit, it is usually responsible +for transforming the trees and types representing the code of a source file. Some phases of +the compiler are: +- `parser`, which converts text that matches Scala's + [syntax] into abstract syntax trees, ASTs +- `typer`, which checks that trees conform to expected types +- `erasure`, which retypes a more simplified program into one that has the same types as the JVM. +- `genBCode`, the JVM backend, which converts erased compiler trees into Java bytecode format. + +[You can read more about phases here](../architecture/phases.md#phase-categories). + +#### Drivers + +The core compiler also requires a lot of state to be initialised before use, such as [settings][ScalaSettings] +and the [Context](../architecture/context.md). For convenience, the [Driver] class contains high level functions for +configuring the compiler and invoking it programatically. The object [Main] inherits from `Driver` +and is invoked by the `scalac` script. + +## Code Structure + +The code of the compiler is found in the package [dotty.tools], +containing the following sub-packages: +```scala +tools // contains helpers and the `scala` generic runner +├── backend // Compiler backends (currently JVM and JS) +├── dotc // The main compiler, with subpackages: +│ ├── ast // Abstract syntax trees +│   ├── classpath +│   ├── config // Compiler configuration, settings, platform specific definitions. +│   ├── core // Core data structures and operations, with specific subpackages for: +│   │   ├── classfile // Reading of Java classfiles into core data structures +│   │   ├── tasty // Reading and writing of TASTY files to/from core data structures +│   │   └── unpickleScala2 // Reading of Scala2 symbol information into core data structures +│   ├── decompiler // pretty printing TASTY as code +│   ├── fromtasty // driver for recompilation from TASTY +│   ├── interactive // presentation compiler and code completions +│   ├── parsing // Scanner and parser +│   ├── plugins // compile plugin definitions +│   ├── printing // Pretty-printing trees, types and other data +│   ├── profile // internals for profiling the compiler +│   ├── quoted // internals for quoted reflection +│   ├── reporting // Reporting of error messages, warnings and other info. +│   ├── rewrites // Helpers for rewriting Scala 2's constructs into Scala 3's. +│   ├── sbt // Helpers for communicating with the Zinc compiler. +│   ├── semanticdb // Helpers for exporting semanticdb from trees. +│   ├── transform // Miniphases and helpers for tree transformations. +│   ├── typer // Type-checking +│   └── util // General purpose utility classes and modules. +├── io // Helper modules for file access and classpath handling. +├── repl // REPL driver and interaction with the terminal +├── runner // helpers for the `scala` generic runner script +└── scripting // scala runner for the -script argument +``` + + +[Phases]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Phases.scala +[CompilationUnit]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/CompilationUnit.scala + +[dotty.tools]: https://github.com/lampepfl/dotty/tree/master/compiler/src/dotty/tools +[ScalaSettings]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +[syntax]: https://docs.scala-lang.org/scala3/reference/syntax.html +[Main]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Main.scala +[Driver]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Driver.scala +[Compiler]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Compiler.scala +[Run]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Run.scala \ No newline at end of file diff --git a/docs/_docs/contributing/architecture/phases.md b/docs/_docs/contributing/architecture/phases.md new file mode 100644 index 000000000000..15690dfe4e4e --- /dev/null +++ b/docs/_docs/contributing/architecture/phases.md @@ -0,0 +1,108 @@ +--- +layout: doc-page +title: Compiler Phases +--- + +As described in the [compiler overview](lifecycle.md#phases), `dotc` is divided into a list of [phases][Phase], +specified in the [Compiler] class. + +#### Printing the phases of the Compiler + +a flattened list of all the phases can be displayed by invoking +the compiler with the `-Xshow-phases` flag: +``` +$ scalac -Xshow-phases +``` + +## Phase Groups + +In class [Compiler] you can access the list of phases with the method `phases`: + +```scala +def phases: List[List[Phase]] = + frontendPhases ::: picklerPhases ::: transformPhases ::: backendPhases +``` + +You can see that phases are actually grouped into sublists, given by the signature +`List[List[Phase]]`; that is, each sublist forms a phase group that is then *fused* into a +single tree traversal when a [Run] is executed. + +Phase fusion allows each phase of a group to be small and modular, +(each performing a single function), while reducing the number of tree traversals +and increasing performance. + +Phases are able to be grouped together if they inherit from [MiniPhase]. + +## Phase Categories + +Phases fall into four categories, allowing customisation by sub-classes of [Compiler]: + +### `frontendPhases` +In the main compiler these include [parser], [typer], [posttyper], +[prepjsinterop] and phases for producing SemanticDB and communicating with the +incremental compiler Zinc. +The [parser] reads source programs and generates untyped abstract syntax trees, which +in [typer] are then typechecked and transformed into typed abstract syntax trees. +Following is [posttyper], performing checks and cleanups that require a fully typed program. +In particular, it +- creates super accessors representing `super` calls in traits +- creates implementations of compiler-implemented methods, +such as `equals` and `hashCode` for case classes. +- marks [compilation units][CompilationUnit] that require inline expansion, or quote pickling +- simplifies trees of erased definitions +- checks variance of type parameters +- mark parameters passed unchanged from subclass to superclass for later pruning. + +### `picklerPhases` +These phases start with [pickler], which serializes typed trees +produced by the `frontendPhases` into TASTy format. Following is [inlining], +which expand calls to inline methods, and [postInlining] providing implementations +of the [Mirror] framework for inlined calls. +Finally are [staging], which ensures that quotes conform to the +[Phase Consistency Principle (PCP)][PCP], and [pickleQuotes] which converts quoted +trees to embedded TASTy strings. + +### `transformPhases` +These phases are concerned with tranformation into lower-level forms +suitable for the runtime system, with two sub-groupings: +- High-level transformations: All phases from [firstTransform] to [erasure]. + Most of these phases transform syntax trees, expanding high-level constructs + to more primitive ones. + - An important transform phase is [patternMatcher], which converts match + trees and patterns into lower level forms, as well as checking the + exhaustivity of sealed types, and unreachability of pattern cases. + - Some phases perform further checks on more primitive trees, + e.g. [refchecks] verifies that no abstract methods exist in concrete classes, + and [initChecker] checks that fields are not used before initialisation. + - The last phase in the group, [erasure] translates all + types into types supported directly by the JVM. To do this, it performs + another type checking pass, but using the rules of the JVM's type system + instead of Scala's. +- Low-level transformations: All phases from `ElimErasedValueType` to + `CollectSuperCalls`. These further transform trees until they are essentially a + structured version of Java bytecode. + +### `backendPhases` +These map the transformed trees to Java classfiles or SJSIR files. + +[CompilationUnit]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/CompilationUnit.scala +[Compiler]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Compiler.scala +[Phase]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Phases.scala +[MiniPhase]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +[Run]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/Run.scala +[parser]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/parsing/ParserPhase.scala +[typer]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/TyperPhase.scala +[posttyper]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +[prepjsinterop]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala +[pickler]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Pickler.scala +[inlining]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Inlining.scala +[postInlining]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/PostInlining.scala +[staging]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Staging.scala +[pickleQuotes]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +[refchecks]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +[initChecker]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/init/Checker.scala +[firstTransform]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +[patternMatcher]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +[erasure]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Erasure.scala +[Mirror]: https://github.com/lampepfl/dotty/blob/master/library/src/scala/deriving/Mirror.scala +[PCP]: {{ site.scala3ref }}/metaprogramming/macros.html#the-phase-consistency-principle diff --git a/docs/_docs/contributing/architecture/symbols.md b/docs/_docs/contributing/architecture/symbols.md new file mode 100644 index 000000000000..c19588a4ff12 --- /dev/null +++ b/docs/_docs/contributing/architecture/symbols.md @@ -0,0 +1,70 @@ +--- +layout: doc-page +title: Symbols +--- + +As discussed previously, `dotc` [maintains time-indexed views](time.md) of various +compiler artifacts. The following sections discuss how they are managed in the compiler. + +## Symbols + +Defined in [Symbols], a `Symbol` is a unique identifier for a definition (e.g. a method, +type, or field). A `ClassSymbol` extends `Symbol` and represents either a +`class`, or a `trait`, or an `object`. A `Symbol` can even refer to non-Scala entities, +such as from the Java standard library. + +## Definitions are Dynamic + +Traditionally, compilers store context-dependent data in a _symbol table_. +Where a symbol then is the central reference to address context-dependent data. +`dotc` instead uses a phase-indexed function (known as +a [Denotation][Denotations]) to compute views of definitions across phases, +as many of attributes associated with definitions are phase-dependent. For example: +- types are gradually simplified by several phases, +- owners change in [lambdaLift] (local methods are lifted to an enclosing class) + and [flatten] (when inner classes are moved to the top level) +- Names are changed when private members need to be accessed from outside + their class (for instance from a nested class or a class implementing + a trait). + +Additionally, symbols are not suitable to be used as a reference to +a definition in another [compilation unit][CompilationUnit]. +In the context of incremental compilation, a symbol from +an external compilation unit may be deleted or changed, making the reference +stale. To counter this, `dotc` types trees of cross-module references with either +a `TermRef` or `TypeRef`. A reference type contains a prefix type and a name. +The denotation that the type refers to is established dynamically based on +these fields. + +## Denotations + +On its own a `Symbol` has no structure. Its semantic meaning is given by being associated +with a [Denotation][Denotations]. + +A denotation is the result of resolving a name during a given period, containing the information +describing some entity (either a term or type), indexed by phase. Denotations usually have a +reference to a selected symbol, but not always, for example if the denotation is overloaded, +i.e. a `MultiDenotation`. + +### SymDenotations +All definition symbols will contain a `SymDenotation`. The denotation, in turn, contains: +- a reverse link to the source symbol +- a reference to the enclosing symbol that defined the source symbol: + - for a local variable, the enclosing method + - for a field or class, the enclosing class +- a set of [flags], describing the definition (e.g. whether it's a trait or mutable). +- the type of the definition (through the `info` method) +- a [signature][Signature1], which uniquely identifies overloaded methods (or else `NotAMethod`). +- and more. + +A class symbol will instead be associated with a `ClassDenotation`, which extends `SymDenotation` +with some additional fields specific for classes. + +[Signature1]: https://github.com/lampepfl/dotty/blob/a527f3b1e49c0d48148ccfb2eb52e3302fc4a349/compiler/src/dotty/tools/dotc/core/Signature.scala#L9-L33 +[Symbols]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Symbols.scala +[flatten]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Flatten.scala +[lambdaLift]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/LambdaLift.scala +[CompilationUnit]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/CompilationUnit.scala +[Denotations]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Denotations.scala +[SymDenotations]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +[flags]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Flags.scala diff --git a/docs/_docs/contributing/architecture/time.md b/docs/_docs/contributing/architecture/time.md new file mode 100644 index 000000000000..588b1ce40bb2 --- /dev/null +++ b/docs/_docs/contributing/architecture/time.md @@ -0,0 +1,68 @@ +--- +layout: doc-page +title: Time in the Compiler +--- + +In the [compiler overview](lifecycle.md) section, we saw that `dotc` is an interactive compiler, +and so can answer questions about entities as they come into existance and change throughout time, +for example: +- which new definitions were added in a REPL session? +- which definitions were replaced in an incremental build? +- how are definitions simplified as they are adapted to the runtime system? + +## Hours, Minutes, and Periods + +For the compiler to be able to resolve the above temporal questions, and more, it maintains +a concept of time. Additionally, because interactions are frequent, it is important to +persist knowledge of entities between interactions, allowing the compiler to remain performant. +Knowing about time allows the compiler to efficiently mark entities as being outdated. + +Conceptually, `dotc` works like a clock, where its minutes are represented by [phases](phases.md), +and its hours by [runs]. Like a clock, each run passes once each of its phases have completed +sequentially, and then a new run can begin. Phases are further grouped into [periods], where +during a period certain entities of the compiler remain stable. + +## Time Travel + +During a run, each phase can rewrite the world as the compiler sees it, for example: +- to transform trees, +- to gradually simplify type from Scala types to JVM types, +- to move definitions out of inner scopes to outer ones, fitting the JVM's model, +- and so on. + +Because definitions can [change over time](symbols.md#definitions-are-dynamic), various artifacts associated with them +are stored non-destructively, and views of the definition created earlier, or later +in the compiler can be accessed by using the `atPhase` method, defined in [Contexts]. + +As an example, assume the following definitions are available in a [Context](context.md): +```scala +class Box { type X } + +def foo(b: Box)(x: b.X): List[b.X] = List(x) +``` + +You can compare the type of definition `foo` after the [typer] phase and after the [erasure] phase +by using `atPhase`: +```scala +import dotty.tools.dotc.core.Contexts.{Context, atPhase} +import dotty.tools.dotc.core.Phases.{typerPhase, erasurePhase} +import dotty.tools.dotc.core.Decorators.i + +given Context = … + +val fooDef: Symbol = … // `def foo(b: Box)(x: b.X): List[b.X]` + +println(i"$fooDef after typer => ${atPhase(typerPhase.next)(fooDef.info)}") +println(i"$fooDef after erasure => ${atPhase(erasurePhase.next)(fooDef.info)}") +``` +and see the following output: +``` +method foo after typer => (b: Box)(x: b.X): scala.collection.immutable.List[b.X] +method foo after erasure => (b: Box, x: Object): scala.collection.immutable.List +``` + +[runs]: https://github.com/lampepfl/dotty/blob/a527f3b1e49c0d48148ccfb2eb52e3302fc4a349/compiler/src/dotty/tools/dotc/Run.scala +[periods]: https://github.com/lampepfl/dotty/blob/a527f3b1e49c0d48148ccfb2eb52e3302fc4a349/compiler/src/dotty/tools/dotc/core/Periods.scala +[Contexts]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Contexts.scala +[typer]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/TyperPhase.scala +[erasure]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Erasure.scala diff --git a/docs/_docs/contributing/architecture/types.md b/docs/_docs/contributing/architecture/types.md new file mode 100644 index 000000000000..64543e555e69 --- /dev/null +++ b/docs/_docs/contributing/architecture/types.md @@ -0,0 +1,147 @@ +--- +layout: doc-page +title: Compiler Types +--- + +## Common Types and their Representation + +Type representations in `dotc` derive from the class `dotty.tools.dotc.core.Types.Type`, +defined in [Types.scala]. The `toString` method on `Type` will display types in a +format corresponding to the backing data structure, e.g. `ExprType(...)` +corresponds to `class ExprType`, defined in [Types.scala]. + +> You can inspect the representation of any type using the [dotty.tools.printTypes][DottyTypeStealer] +> script, its usage and integration into your debugging workflow is [described here](../issues/inspection.md). + +### Types of Definitions + +The following table describes definitions in Scala 3, followed by the `dotc` representation +of two types - a reference to the definition, and then its underlying type. + +**Note**: in the following types, `p` refers to the self-type of the enclosing scope of +the definition, or `NoPrefix` for local definitions and parameters. + +Definition | Reference | Underlying Type +------------------------|-----------------|------------------------- +`type Z >: A <: B` | `TypeRef(p, Z)` | `RealTypeBounds(A, B)` +`type Z = A` | `TypeRef(p, Z)` | `TypeAlias(A)` +`type F[T] = T match …` | `TypeRef(p, F)` | `MatchAlias([T] =>> T match …)` +`class C` | `TypeRef(p, C)` | `ClassInfo(p, C, …)` +`trait T` | `TypeRef(p, T)` | `ClassInfo(p, T, …)` +`object o` | `TermRef(p, o)` | `TypeRef(p, o$)` where `o$` is a class +`def f(x: A): x.type` | `TermRef(p, f)` | `MethodType(x, A, TermParamRef(x))` +`def f[T <: A]: T` | `TermRef(p, f)` | `PolyType(T, <: A, TypeParamRef(T))` +`def f: A` | `TermRef(p, f)` | `ExprType(A)` +`(x: => A)` | `TermRef(p, x)` | `ExprType(A)` where `x` is a parameter +`val x: A` | `TermRef(p, x)` | `A` + +### Types of Values + +The following types may appear in part of the type of an expression: + +Type | Representation +--------------------------|------------------------------ +`x.y.type` | `TermRef(x, y)` +`X#T` | `TypeRef(X, T)` +`x.y.T` and `x.y.type#T` | `TypeRef(TermRef(x, y), T)` +`this.type` | `ThisType(C)` where `C` is the enclosing class +`"hello"` | `ConstantType(Constant("hello"))` +`A & B` | `AndType(A, B)` +`A | B` | `OrType(A, B)` +`A @foo` | `AnnotatedType(A, @foo)` +`[T <: A] =>> T` | `HKTypeLambda(T, <: A, TypeParamRef(T))` +`x.C[A, B]` | `AppliedType(x.C, List(A, B))` +`C { type A = T }` | `RefinedType(C, A, T)`
      when `T` is not a member of `C` +`C { type X = Y }` | `RecType(RefinedType(C, X, z.Y))`
      when `X` and `Y` are members of `C`
      and `z` is a `RecThis` over the enclosing `RecType` +`super.x.type` | `TermRef(SuperType(…), x)` + +## Constructing Types + +### Method Definition Types + +You can see above that method definitions can have an underlying type of +either `PolyType`, `MethodType`, or `ExprType`. `PolyType` and `MethodType` +may be mixed recursively however, and either can appear as the result type of the other. + +Take this example as given: + +```scala +def f[A, B <: Seq[A]](x: A, y: B): Unit +``` +it can be constructed by the following code: + +```scala +import dotty.tools.dotc.core.Types.* +import dotty.tools.dotc.core.Symbols.* +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Decorators.* + +given Context = … // contains the definitions of the compiler + +val f: Symbol = … // def f[A, B <: Seq[A]](x: A, y: B): Unit + +f.info = PolyType( + List("A".toTypeName, "B".toTypeName))( + pt => List( + TypeBounds(defn.NothingType, defn.AnyType), + TypeBounds(defn.NothingType, AppliedType(defn.SeqType, List(pt.newParamRef(0)))) + ), + pt => MethodType( + List("x".toTermName, "y".toTermName))( + mt => List(pt.newParamRef(0), pt.newParamRef(1)), + mt => defn.UnitType + ) +) +``` + +Note that `pt.newParamRef(0)` and `pt.newParamRef(1)` refers to the +type parameters `A` and `B` respectively. + +## Proxy Types and Ground Types +Types in `dotc` are divided into two semantic kinds: +- Ground Types (inheriting from either `CachedGroundType` or `UncachedGroundType`) +- Proxy Types (inheriting from `TypeProxy` via either `CachedProxyType` or `UncachedProxyType`) + +A Proxy Type is anything that can be considered to be an abstraction of another type, +which can be accessed by the `underlying` method of the `TypeProxy` class. It's dual, the +Ground Type has no meaningful underlying type, typically it is the type of method and class +definitions, but also union types and intersection types, along with utility types of the +compiler. + +Here's a diagram, serving as the mental model of the most important and distinct types available after the `typer` phase, derived from [dotty/tools/dotc/core/Types.scala][1]: + +``` +Type -+- proxy_type --+- NamedType --------+- TypeRef + | | \ + | +- SingletonType ----+- TermRef + | | +- ThisType + | | +- SuperType + | | +- ConstantType + | | +- TermParamRef + | | +- RecThis + | | +- SkolemType + | +- TypeParamRef + | +- RefinedOrRecType -+-- RefinedType + | | -+-- RecType + | +- AppliedType + | +- TypeBounds + | +- ExprType + | +- AnnotatedType + | +- TypeVar + | +- HKTypeLambda + | +- MatchType + | + +- ground_type -+- AndType + +- OrType + +- MethodOrPoly -----+-- PolyType + | +-- MethodType + +- ClassInfo + +- NoType + +- NoPrefix + +- ErrorType + +- WildcardType + +``` + +[Types.scala]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Types.scala +[DottyTypeStealer]: https://github.com/lampepfl/dotty/blob/master/compiler/test/dotty/tools/DottyTypeStealer.scala diff --git a/docs/_docs/contributing/contribute-knowledge.md b/docs/_docs/contributing/contribute-knowledge.md deleted file mode 100644 index 7164774ac1df..000000000000 --- a/docs/_docs/contributing/contribute-knowledge.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -layout: doc-page -title: Contributing Knowledge ---- - -# Contribute Internals-related Knowledge -If you know anything useful at all about Dotty, feel free to log this knowledge: - -- [📜Log the Knowledge](https://github.com/lampepfl/dotty-knowledge/issues/new) -- [🎓More about Logging the Knowledge](https://github.com/lampepfl/dotty-knowledge/blob/master/README.md) - -In short, no need to make it pretty, particularly human-readable or give it a particular structure. Just dump the knowledge you have and we'll take it from there. \ No newline at end of file diff --git a/docs/_docs/contributing/getting-started.md b/docs/_docs/contributing/getting-started.md index c842fd0a49d0..239e738bbfbb 100644 --- a/docs/_docs/contributing/getting-started.md +++ b/docs/_docs/contributing/getting-started.md @@ -3,15 +3,41 @@ layout: doc-page title: Getting Started --- +## Scala CLA +Sometime before submitting your pull request you'll want to make sure you have +signed the [Scala CLA][scala-cla]. You can read more about why we require a CLA +and what exactly is included in it [here][scala-cla]. -Requirements ------------- -Make sure that you are using macOS or Linux (or WSL on Windows) with Java 8 or newer. You can determine which version of the JDK is the -default by typing `java -version` in a Terminal window. +## Making sure the team is aware + +Before digging into an issue or starting on a new feature it's a good idea to +make sure an [issue][dotty-issue] or a [discussion][dotty-discussion] has been +created outlining what you plan to work on. This is both for your and the team's +benefit. It ensures you get the help you need, and also gives the compiler team +a heads-up that someone is working on an issue. + +For some small changes like documentation, this isn't always necessary, but it's +never a bad idea to check. + +## Requirements + +- [git] is essential for managing the Scala 3 code, and contributing to GitHub, + where the code is hosted. +- A Java Virtual Machine (JDK 8 or higher), required for running the build tool. + - download Java from [Oracle Java 8][java8], [Oracle Java 11][java11], + or [AdoptOpenJDK 8/11][adopt]. Refer to [JDK Compatibility][compat] for Scala/Java compatibility detail. + - Verify that the JVM is installed by running the following command in a terminal: `java -version`. +- [sbt][sbt-download], the build tool required to build the Scala 3 compiler and libraries. + +## Nice To Have + +An IDE, such as [Metals] will help you develop in Scala 3 with features such as autocompletion or goto-definition, +and with the [VS Code][vs-code] text editor you can even use the Scala debugger, or create interactive worksheets for an +iterative workflow. + +## Compiling and Running -Compiling and Running ---------------------- Start by cloning the repository: ```bash @@ -48,8 +74,8 @@ $ scala HelloWorld ``` -Starting a REPL ---------------- +## Starting a REPL + ```bash $ sbt > repl @@ -64,8 +90,9 @@ or via bash: ```bash $ scala ``` -Publish to local repository ---------------------------------- + +## Publish to local repository + To test our cloned compiler on local projects: ```bash @@ -79,8 +106,8 @@ ThisBuild / scalaVersion := "-bin-SNAPSHOT" where `dotty-version` can be found in the file `project/Build.scala`, like `3.0.0-M2` -Generating Documentation -------------------------- +## Generating Documentation + To generate this page and other static page docs, run ```bash $ sbt @@ -92,9 +119,22 @@ Before contributing to Dotty, we invite you to consult the [Dotty Developer Guidelines](https://github.com/lampepfl/dotty/blob/main/CONTRIBUTING.md). -Community -------------- +## Community + The main development discussion channels are: - [github.com/lampepfl/dotty/discussions](https://github.com/lampepfl/dotty/discussions) - [contributors.scala-lang.org](https://contributors.scala-lang.org) - [gitter.im/scala/contributors](https://gitter.im/scala/contributors) + +[git]: https://git-scm.com +[Metals]: https://scalameta.org/metals/ +[vs-code]: https://code.visualstudio.com +[lampepfl/dotty]: https://github.com/lampepfl/dotty +[sbt-download]: https://www.scala-sbt.org/download.html +[java8]: https://www.oracle.com/java/technologies/javase-jdk8-downloads.html +[java11]: https://www.oracle.com/java/technologies/javase-jdk11-downloads.html +[adopt]: https://adoptopenjdk.net/ +[compat]: /overviews/jdk-compatibility/overview.html +[scala-cla]: https://www.lightbend.com/contribute/cla/scala +[dotty-issue]: https://github.com/lampepfl/dotty/issues +[dotty-discussion]: https://github.com/lampepfl/dotty/discussions diff --git a/docs/_docs/contributing/index.md b/docs/_docs/contributing/index.md index 6cf0def2d5e2..27954aefd7a1 100644 --- a/docs/_docs/contributing/index.md +++ b/docs/_docs/contributing/index.md @@ -2,3 +2,48 @@ layout: index title: Contributing --- + +This guide is intended to give new contributors the knowledge they need to +become productive and fix issues or implement new features in Scala 3. It +also documents the inner workings of the Scala 3 compiler, `dotc`. + +### This is a living document + +Keep in mind that the code for `dotc` is continually changing, so the ideas +discussed in this guide may fall out of date. This is a living document, so +please consider contributing to it on +[GitHub](https://github.com/scala/docs.scala-lang/tree/main/_overviews/scala3-contribution) +if you notice anything out of date, or report any issues +[here](https://github.com/scala/docs.scala-lang/issues). + +### Get the Most from This Guide + +`dotc` is built with Scala 3, fully utilising its [new +features](/scala3/new-in-scala3.html). It is recommended that you first have +some familiarity with Scala 3 to get the most out of this guide. You can learn +more in the [language reference]({{ site.scala3ref }}). + +Many code snippets in this guide make use of shell commands (a line beginning +with `$`), and in this case a `bash` compatible shell is assumed. You may have +to look up how to translate commands to your shell. + +### What is a Compiler? + +Let's start at the beginning and first look at the question of "what is a +compiler?". A compiler is a program that takes as input text, representing a +program in one language and produces as output the same program, written in +another programming language. + +#### The Scala Compiler + +As an example, `dotc` takes text input, verifies that it is a valid Scala program +and then produces as output the same program, but written in Java bytecode, and optionally +in SJSIR when producing Scala.js output. + +### Contribute Internals-related Knowledge +If you know anything useful at all about Dotty, feel free to log this knowledge: + +- [📜Log the Knowledge](https://github.com/lampepfl/dotty-knowledge/issues/new) +- [🎓More about Logging the Knowledge](https://github.com/lampepfl/dotty-knowledge/blob/master/README.md) + +In short, no need to make it pretty, particularly human-readable or give it a particular structure. Just dump the knowledge you have and we'll take it from there. \ No newline at end of file diff --git a/docs/_docs/contributing/issues/areas.md b/docs/_docs/contributing/issues/areas.md new file mode 100644 index 000000000000..4f9adf79ba77 --- /dev/null +++ b/docs/_docs/contributing/issues/areas.md @@ -0,0 +1,70 @@ +--- +layout: doc-page +title: Common Issue Locations +--- + +Many issues are localised to small domains of the compiler and are self-contained, +here is a non-exhaustive list of such domains, and the files associated with them: + +### Pretty Printing of Types and Trees + +Objects in the compiler that inherit from [Showable] can be pretty printed. +The pretty-printing of objects is used in many places, from debug output, +to user-facing error messages and printing of trees after each phase. + +Look in [RefinedPrinter] (or its parent class [PlainPrinter]) for the implementation of pretty printing. + +### Content of Error Messages + +You can find the definitions of most error messages in [messages] (with IDs +defined in [ErrorMessageID]). If the message is not defined there, try the +`-Ydebug-error` compiler flag, which will print a stack trace leading to the +production of the error, and the contents of the message. + +### Compiler Generated Given Instances + +If the issue lies in given instances provided by the compiler, such as `scala.reflect.ClassTag`, +`scala.deriving.Mirror`, `scala.reflect.TypeTest`, `scala.CanEqual`, `scala.ValueOf`, +`scala.reflect.Manifest`, etc, look in [Synthesizer], which provides factories for +given instances. + +### Compiler Generated Methods + +Members can be generated for many classes, such as `equals` and `hashCode` +for case classes and value classes, and `ordinal` and `fromProduct` for Mirrors. +To change the implementation, see [SyntheticMembers]. + +### Code Completions +For suggestions to auto-complete method selections, see [Completion]. + +### Enum Desugaring +See [Desugar] and [DesugarEnums]. + +### Pattern Match Exhaustivity +See [Space]. + +### Metaprogramming + +#### Quotes Reflection +See the [quoted runtime package][quotes-impl]. + +#### Inline match +See [Inliner]. + +#### Compiletime Ops Types +See `tryCompiletimeConstantFold` in [Types]. + +[Showable]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/printing/Showable.scala +[PlainPrinter]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +[RefinedPrinter]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +[ErrorMessageID]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +[messages]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/reporting/messages.scala +[Synthesizer]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +[SyntheticMembers]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +[quotes-impl]: https://github.com/lampepfl/dotty/tree/master/compiler/src/scala/quoted/runtime/impl +[Inliner]: https://github.com/lampepfl/dotty/blob/main/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +[Types]: https://github.com/lampepfl/dotty/tree/master/compiler/src/dotty/tools/dotc/core/Types.scala +[Completion]: https://github.com/lampepfl/dotty/tree/master/compiler/src/dotty/tools/dotc/interactive/Completion.scala +[DesugarEnums]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +[Desugar]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/ast/Desugar.scala +[Space]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala diff --git a/docs/_docs/contributing/issues/cause.md b/docs/_docs/contributing/issues/cause.md new file mode 100644 index 000000000000..5bb04e894f70 --- /dev/null +++ b/docs/_docs/contributing/issues/cause.md @@ -0,0 +1,115 @@ +--- +layout: doc-page +title: Finding the Cause of an Issue +--- + +In this section, you will be able to answer questions such as: +- where does an error happen in a codebase? +- when during compilation was a particular tree introduced? +- where is a particular object created? +- where is a particular value assigned to a variable? + +> You may be able to quickly find the source responsible for an issue by consulting [common issue locations](../issues/areas.md) + +## What phase generated a particular tree? + +As described in the [compiler lifecycle](../architecture/lifecycle.md#phases), each phase transforms the trees +and types that represent your code in a certain way. + +To print the code as it is transformed through the compiler, use the compiler flag `-Xprint:all`. +After each phase group is completed, you will see the resulting trees representing the code. + +> It is recommended to test `-Xprint:all` on a single, small file, otherwise a lot of unnecessary +> output will be generated. + +### Trace a Tree Creation Site + +When you see a problematic tree appear after a certain phase group, you know to isolate the rest of +your search to the code of that phase. For example if you found a problematic tree after phase +`posttyper`, the problem most likely appears in the code of [PostTyper]. We can trace the exact point +the tree was generated by looking for its unique ID, and then generating a stack trace at its creation: + +1. Run the compiler with `-Xprint:posttyper` and `-Yshow-tree-ids` flags. + This will only print the trees of the `posttyper` phase. This time you should see the tree + in question be printed alongside its ID. You'll see something like `println#223("Hello World"#37)`. +2. Copy the ID of the desired tree. +3. Run the compiler with `-Ydebug-tree-with-id ` flag. The compiler will print a stack trace + pointing to the creation site of the tree with the provided ID. + +### Enhanced Tree Printing + +As seen above `-Xprint:` can be enhanced with further configuration flags, found in +[ScalaSettings]. For example, you can additionally print the type of a tree with `-Xprint-types`. + +## Increasing Logging Output +Once you have identified the phase that generated a certain tree, you can then increase +logging in that phase, to try and detect erroneous states: + +- general logging within a phase can be enabled with the `-Ylog` compiler flag, such as + - `-Ylog:,,...` for individual phases + - `-Ylog:all` for all phases. +- Additionally, various parts of the compiler have specialised logging objects, defined in [Printers]. + Change any of the printers of interest from `noPrinter` to `default` and increase output specialised + to that domain. + +## Navigating to Where an Error is Generated + +The compiler issues user facing errors for code that is not valid, such as the type mismatch +of assigning an `Int` to a `Boolean` value. Sometimes these errors do not match what is expected, which could be a bug. + +To discover why such a *spurious* error is generated, you can trace the code that generated the error by +adding the `-Ydebug-error` compiler flag, e.g. `scala3/scalac -Ydebug-error Test.scala`. +This flag forces a stack trace to be printed each time an error happens, from the site where it occurred. + +Analysing the trace will give you a clue about the objects involved in producing the error. +For example, you can add some debug statements before the error is issued to discover +the state of the compiler. [See some useful ways to debug values.](./inspection.md) + +### Where was a particular object created? + +If you navigate to the site of the error, and discover a problematic object, you will want to know +why it exists in such a state, as it could be the cause of the error. You can discover the +creation site of that object to understand the logic that created it. + +You can do this by injecting a *tracer* into the class of an instance in question. +A tracer is the following variable: +```scala +val tracer = Thread.currentThread.getStackTrace.mkString("\n") +``` +When placed as a member definition at a class, it will contain a stack trace pointing at where exactly +its particular instance was created. + +Once you've injected a tracer into a class, you can `println` that tracer from the error site or +other site you've found the object in question. + +#### Procedure + +1. Determine the type of the object in question. You can use one of the following techniques to do so: + - Use an IDE to get the type of an expression, or save the expression to a `val` + and see its inferred type. + - Use `println` to print the object or use `getClass` on that object. +2. Locate the type definition for the type of that object. +3. Add a field `val tracer = Thread.currentThread.getStackTrace.mkString("\n")` to that type definition. +4. `println(x.tracer)` (where `x` is the name of the object in question) from the original site where you + encountered the object. This will give you the stack trace pointing to the place where the + constructor of that object was invoked. + +### Where was a particular value assigned to a variable? + +Say you have a certain [type](../architecture/types.md) assigned to a [Denotation] and you would like to know why it has that +specific type. The type of a denotation is defined by `var myInfo: Type`, and can be assigned multiple times. +In this case, knowing the creation site of that `Type`, as described above, is not useful; instead, you need to +know the *assignment* (not *creation*) site. + +This is done similarly to how you trace the creation site. Conceptually, you need to create a proxy for that variable that will log every write operation to it. Practically, if you are trying to trace the assignments to a variable `myInfo` of type `Type`, first, rename it to `myInfo_debug`. Then, insert the following at the same level as that variable: + +```scala +var tracer = "", +def myInfo: Type = myInfo_debug, +def myInfo_=(x: Type) = { tracer = Thread.currentThread.getStackTrace.mkString("\n"); myInfo_debug = x } +``` + +[Printers]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/config/Printers.scala +[Denotation]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/Denotations.scala +[PostTyper]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +[ScalaSettings]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala diff --git a/docs/_docs/contributing/issues/checklist.md b/docs/_docs/contributing/issues/checklist.md new file mode 100644 index 000000000000..e2fcf32531de --- /dev/null +++ b/docs/_docs/contributing/issues/checklist.md @@ -0,0 +1,135 @@ +--- +layout: doc-page +title: Pull Request Checklist +--- + +Once you solved the issue you were working on, you'll likely want to see your +changes added to the [Scala 3 repo][lampepfl/dotty]. To do that, you need to +prepare a [pull request][pull-request] with your changes. Assuming that the team +is aware of what you've been working, here are some final steps that you'll want +to keep in mind as you create your PR. + +### 1. Sign the CLA + +Make sure you have signed the [Scala CLA][cla]. If you have any questions about +what this is and why it's required you can read further about it [here][cla]. + +### 2. Make sure your work is on its own branch + +When submitting your pull request it's always best to ensure the branch name is +unique to the changes you're working on. It's important not to submit your PR on +your `main` branch as this blocks maintainers from making any changes to your PR +if necessary. + +### 3: Add Tests + +Add at least one test that replicates the problem in the issue, and that shows it is now resolved. + +You may of course add variations of the test code to try and eliminate edge cases. +[Become familiar with testing in Scala 3](./testing.md). + +### 4: Add Documentation + +Please ensure that all code is documented to explain its use, even if only internal +changes are made. This refers to scaladocs and also any changes that might be +necessary in the reference docs. + +### 5: Double check everything + +Here are a couple tips to keep in mind. + +- [DRY (Don't Repeat Yourself)][dry] +- [Scouts Rule][scouts] +- When adding new code try use [optional braces]. If you're rewriting old code, + you should also use optional braces unless it introduces more code changes + that necessary. + +### 6: Commit Messages + +Here are some guidelines when writing commits for Dotty. + +1. If your work spans multiple local commits (for example; if you do safe point + commits while working in a feature branch or work in a branch for long time + doing merges/rebases etc.) then please do not commit it all but rewrite the + history by squashing the commits into one large commit which is accompanied + by a detailed commit message for (as discussed in the following sections). + For more info, see the article: [Git Workflow][git-workflow]. Additionally, + every commit should be able to be used in isolation—that is, each commit must + build and pass all tests. + +2. The first line should be a descriptive sentence about what the commit is + doing. It should be possible to fully understand what the commit does by just + reading this single line. It is **not ok** to only list the ticket number, + type "minor fix" or similar. If the commit has a corresponding ticket, + include a reference to the ticket number, prefixed with "Closes #", at the + beginning of the first line followed by the title of the ticket, assuming + that it aptly and concisely summarizes the commit in a single line. If the + commit is a small fix, then you are done. If not, go to 3. + +3. Following the single line description (ideally no more than 70 characters + long) should be a blank line followed by an enumerated list with the details + of the commit. + +4. Add keywords for your commit (depending on the degree of automation we reach, + the list may change over time): + * ``Review by @githubuser`` - will notify the reviewer via GitHub. Everyone + is encouraged to give feedback, however. (Remember that @-mentions will + result in notifications also when pushing to a WIP branch, so please only + include this in your commit message when you're ready for your pull + request to be reviewed. Alternatively, you may request a review in the + pull request's description.) + * ``Fix/Fixing/Fixes/Close/Closing/Refs #ticket`` - if you want to mark the + ticket as fixed in the issue tracker (Assembla understands this). + * ``backport to _branch name_`` - if the fix needs to be cherry-picked to + another branch (like 2.9.x, 2.10.x, etc) + +Example: + +``` +fix: here is your pr title briefly mentioning the topic + +Here is the body of your pr with some more information + - Details 1 + - Details 2 + - Details 3 + +Closes #2 +``` + +### 7: Create your PR! + +When the feature or fix is completed you should open a [Pull +Request](https://help.github.com/articles/using-pull-requests) on GitHub. + +If you're not actually finished yet and are just looking for some initial input +on your approach, feel free to open a [Draft PR][draft]. This lets reviewers +know that you're not finished yet. It's also a good idea to put a [wip] in front +of your pr title to make this extra clear. + +Shortly after creating your pull request a maintainer should assign someone to +review it. If this doesn't happen after a few days, feel free to ping someone on +the [Scala Contributors Discor][discord] or tag someone on the PR. Depending on +the type of pull request there might be multiple people that take a look at your +changes. There might also be community input as we try to keep the review +process as open as possible. + +### 8: Addressing feedback + +More than likely you'll get feedback from the reviewers, so you'll want to make +sure to address everything. When in doubt, don't hesitate to ask for +clarification or more information. + +Once you finally see the "LGTM" (Looks Good To Me or Let's Get This Merged) +you're PR will be merged in! + +[pull-request]: https://docs.github.com/en?query=pull+requests +[lampepfl/dotty]: https://github.com/lampepfl/dotty +[cla]: http://typesafe.com/contribute/cla/scala +[issues]: https://github.com/lampepfl/dotty/issues +[full-list]: https://github.com/lampepfl/dotty/blob/master/CONTRIBUTING.md +[discord]: https://discord.gg/TSmY9zkHar +[dry]: https://www.oreilly.com/library/view/97-things-every/9780596809515/ch30.html +[scouts]: https://www.oreilly.com/library/view/97-things-every/9780596809515/ch08.html +[optional-braces]: https://docs.scala-lang.org/scala3/reference/other-new-features/indentation.html +[draft]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests +[git-workflow]: http://sandofsky.com/blog/git-workflow.html diff --git a/docs/_docs/contributing/issues/debugging.md b/docs/_docs/contributing/issues/debugging.md new file mode 100644 index 000000000000..2d8a9e5941e4 --- /dev/null +++ b/docs/_docs/contributing/issues/debugging.md @@ -0,0 +1,189 @@ +--- +layout: doc-page +title: Debugging the Compiler +--- + +The debugger is a powerful tool to navigate the internals of the compiler and track bugs. + +You can start the Scala debugger in VSCode using [Metals](https://scalameta.org/metals/). +In this page you will learn how to configure it, and how to use it. + +## Importing the project in VSCode using Metals + +The first step is to import the build in Metals, if it has not yet been imported. + +To do so you can open the [lampefl/dotty][lampepfl/dotty] repository in VSCode and click `Import build` in Metals view. +It may take a few minutes to import, compile and index the full project. + +![Import build](/images/contribution/import-build.jpg) + +If you have any trouble with importing, you can try to switch the build server from Bloop to sbt, +by running the `Metals: Switch build server` command from VSCode command palette. + +## Configuring the debugger + +To configure the debugger in VSCode, you can go to the `Run and Debug` view and click `create a launch.json file`. +It creates the `launch.json` file in the `.vscode` folder, in which we will define the debug configurations. + +![Create launch.json file](/images/contribution/launch-config-file.jpg) + +To create a debug configuration: +- Open the `.vscode/launch.json` file +- Click the `Add Configuration` button +- Go down the list of templates and select `Scala: Run main class` + +![Create configuration](/images/contribution/create-config.jpg) + +The added configuration should look like this: +```json +{ + "type": "scala", + "request": "launch", + "name": "Untitled", + "mainClass": "???", + "args": [], + "jvmOptions": [], + "env": {} +} +``` + +This is a template that you need to fill out. +First You can give a `name` to your configuration, for instance `Debug Scala 3 Compiler`. + +The two most important parameters, to debug the compiler, are `mainClass` and `args`. +The `mainClass` of the compiler is `dotty.tools.dotc.Main`. +In the `args` you need to specify the compiler arguments, which must contain at least a Scala file to compile and a `-classpath` option. + +To start with, we can compile the `../tests/pos/HelloWorld.scala` file. +In the classpath, we always need at least the `scala-library_2.13` and the bootstrapped `scala3-library_3`. +To locate them on your filesystem you can run the `export scala3-library-bootstrapped/fullClasspath` command in sbt. + +``` +$ sbt +> export scala3-library-bootstrapped/fullClasspath +/home/user/lampepfl/dotty/out/bootstrap/scala3-library-bootstrapped/scala-3.3.1-RC1-bin-SNAPSHOT-nonbootstrapped/classes:/home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.10/scala-library-2.13.10.jar +[success] Total time: 1 s, completed Mar 10, 2023, 4:37:43 PM +``` + +Note that it is important to use the bootstrapped version of the `scala3-library` to get the correct TASTy version. + +Additionally you can add the `-color` and `never` arguments to prevent the compiler from printing ANSI codes as strings in the debug console. + +Here is the final configuration: +```json +{ + "type": "scala", + "request": "launch", + "name": "Debug Scala 3 Compiler", + "mainClass": "dotty.tools.dotc.Main", + "args": [ + "../tests/pos/HelloWorld.scala", + "-classpath", + // To replace with your own paths + "/home/user/lampepfl/dotty/out/bootstrap/scala3-library-bootstrapped/scala-3.3.1-RC1-bin-SNAPSHOT-nonbootstrapped/classes:/home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.10/scala-library-2.13.10.jar", + "-color", + "never" + ], + "jvmOptions": [], + "env": {} +} +``` + +## Customizing the debug configurations + +### Compiling several files at once + +You can compile more than one Scala file, by adding them in the `args`: +```json +"args": [ + "file1.scala", + "file2.scala", + "-classpath", + "/home/user/lampepfl/dotty/out/bootstrap/scala3-library-bootstrapped/scala-3.3.1-RC1-bin-SNAPSHOT-nonbootstrapped/classes:/home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.10/scala-library-2.13.10.jar" +] +``` + +### Depending on a library + +To add a dependency to an external library you need to download it and all its transitive dependencies, and to add them in the classpath. +The Coursier CLI can help you to do that. +For instance to add a dependency to cats you can run: +``` +$ cs fetch org.typelevel::cats-core:2.+ --classpath --scala-version 3 --exclude org.scala-lang:scala-library --exclude org.scala-lang:scala3-library +/home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/org/typelevel/cats-core_3/2.9.0/cats-core_3-2.9.0.jar:/home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/org/typelevel/cats-kernel_3/2.9.0/cats-kernel_3-2.9.0.jar +``` + +And concatenate the output into the classpath argument, which should already contain the scala-library_2.13 and the bootstrapped scala3-library: + +```json +"args": [ + "using-cats.scala", + "-classpath", + "/home/user/lampepfl/dotty/out/bootstrap/scala3-library-bootstrapped/scala-3.3.1-RC1-bin-SNAPSHOT-nonbootstrapped/classes:/home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/org/scala-lang/scala-library/2.13.10/scala-library-2.13.10.jar:/home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/org/typelevel/cats-core_3/2.9.0/cats-core_3-2.9.0.jar:/home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/org/typelevel/cats-kernel_3/2.9.0/cats-kernel_3-2.9.0.jar" +] +``` + +### Add more compiler options + +In the `args` you can add any additional compiler option you want. + +For instance you can add `-Xprint:all` to print all the generated trees after each mega phase. + +Run `scalac -help` to get an overview of the available compiler options. + +### Defining more than one launch configuration + +You can create as many debug configurations as you need: to compile different files, with different compiler options or different classpaths. + +## Starting the debugger + +Before starting the debugger you need to put a breakpoint in the part of the code that you want to debug. +If you don't know where to start, you can put a breakpoint in the `main` method of the `dotty.tools.dotc.Driver` trait. + +![First breakpoint](/images/contribution/breakpoint.jpg) + +Now to start the debugger, open the debug view, find the drop-down list of all the debug configurations and click on yours. +The debugger should start and pause on your breakpoint. + +![Start debugger](/images/contribution/start-debugger.jpg) + +## Using the debugger + +### Navigating the call stack + +When the debugger has paused, you can see the current call stack in the `Debug and Run` view. +Each frame of the call stack contains different variables, whose values you can see in the `Variables` section of the `Debug and Run` view. + +![Call stack](/images/contribution/call-stack.jpg) + +Analysing the call stack and the variables can help you understand the path taken by the compiler to reach that state. + +### The debugging steps + +The debug toolbar contains the `Continue / Pause`, `Step Over`, `Step Into`, `Step Out`, `Restart` and `Stop` buttons. + +![Debugging steps](/images/contribution/toolbar.jpg) + +You can use the step buttons to execute the code step by step and get a precise understanding of the program. + +### The debug console + +When the debugger has paused, you can evaluate any Scala 3 expression in the debug console. +This is useful to inspect some values or to execute some parts of the code. + +For instance, you can evaluate `tree.show` to pretty-print a tree. + +![Import build](/images/contribution/debug-console.jpg) + +### Conditional breakpoints + +In a breakpoint you can define a condition, in the form of a Boolean expression written in Scala. +The program will stop on the breakpoint as soon as the condition is met. + +To add a condition, right-click on a breakpoint and pick `Edit breakpoint...`. + +For instance, if you know that a bug happens on typing a method `foo`, you can use the condition `tree.symbol.name.show == "foo"` in a breakpoint in the `Typer`. + +![Import build](/images/contribution/conditional-breakpoint.jpg) + +[lampepfl/dotty]: https://github.com/lampepfl/dotty diff --git a/docs/_docs/contributing/issues/efficiency.md b/docs/_docs/contributing/issues/efficiency.md new file mode 100644 index 000000000000..07307646a4bb --- /dev/null +++ b/docs/_docs/contributing/issues/efficiency.md @@ -0,0 +1,24 @@ +--- +layout: doc-page +title: Improving Your Workflow +--- + +In the previous sections of this chapter, you saw some techniques for +working with the compiler. Some of these techniques can be used +repetitively, e.g.: + +- Navigating stack frames +- Printing variables in certain ways +- Instrumenting variable definitions with tracers + +The above procedures often take a lot of time when done manually, reducing productivity: +as the cost (in terms of time and effort) is high, you may avoid attempting to do so, +and possibly miss valuable information. + +If you're doing those things really frequently, it is recommended to script your editor +to reduce the number of steps. E.g. navigating to the definition of a stack frame +part when you click it, or instrumenting variables for printing. + +An example of how it is done for Sublime Text 3 is [here](https://github.com/anatoliykmetyuk/scala-debug-sublime). + +True, it takes some time to script your editor, but if you spend a lot of time with issues, it pays off. diff --git a/docs/_docs/contributing/issues/index.md b/docs/_docs/contributing/issues/index.md new file mode 100644 index 000000000000..db348d7edd9d --- /dev/null +++ b/docs/_docs/contributing/issues/index.md @@ -0,0 +1,17 @@ +--- +layout: index +title: Finding the Cause of an Issue +--- + +An issue found in the [GitHub repo][lampepfl/dotty] usually describes some code that +manifests undesired behaviour. + +This chapter of the guide describes the different steps to contribute to Dotty: +- [Reproducing an Issue](./reproduce.md) +- [Finding the Cause of an Issue](./cause.md) +- [Debugging the Compiler](./debugging.md) +- [Other debugging techniques](./other-debugging.md) +- [Inspect the values](./inspection.md) +- [Improving your workflow](./efficiency.md) +- [Testing a Fix](./testing.md) +- [Checklist](./checklist.md) diff --git a/docs/_docs/contributing/issues/inspection.md b/docs/_docs/contributing/issues/inspection.md new file mode 100644 index 000000000000..abedc09ecd3b --- /dev/null +++ b/docs/_docs/contributing/issues/inspection.md @@ -0,0 +1,181 @@ +--- +layout: doc-page +title: How to Inspect Values +--- + +In this section, you will find out how to debug the contents of certain objects +while the compiler is running, and inspect produced artifacts of the compiler. + +## Inspecting variables in-place + +Frequently you will need to inspect the content of a particular variable. +You can either use `println`s or the debugger, more info on how to setup the latter. + +In the remeainder of this article we'll use `println()` inserted in the code, but the same effect can be accomplished by stopping at a breakpoint, and typing `` in the [debug console](./debugging.md#the-debug-console) of the debugger. + +When printing a variable, it's always a good idea to call `show` on that variable: `println(x.show)`. +Many objects of the compiler define `show`, returning a human-readable string. +e.g. if called on a tree, the output will be the tree's representation as source code, rather than +the underlying raw data. + +Sometimes you need to print flags. Flags are metadata attached to [symbols] containing information such as whether a +class is abstract, comes from Java, what modifiers a variable has (private, protected etc) and so on. +Flags are stored in a single `Long` value, each bit of which represents whether a particular flag is set. + +To print flags, you can use the `flagsString` method, e.g. `println(x.flagsString)`. + +## Pretty Printing with a String Interpolator + +You can also pretty print objects with string interpolators, +these default to call `.show` when possible, avoiding boilerplate +and also helping format error messages. + +Import them with the following: + +```scala +import dotty.tools.dotc.core.Decorators.* +``` + +Here is a table of explanations for their use: + +| Usage | Description | +|--------|-----------------------------------| +|`i""` | General purpose string formatting. It calls `.show` on objects
      mixing in Showable, `String.valueOf` otherwise | +|`em""` | Formatting for error messages: Like `i` but suppress
      follow-on, error messages after the first one if some
      of their arguments are "non-sensical". | +|`ex""` | Formatting with added explanations: Like `em`, but add
      explanations to give more info about type variables
      and to disambiguate where needed. | + + +## Obtaining debug output from the compiler + +As explained in [navigation](../issues/cause.md), we can debug the code being generated as it is transformed +through the compiler. As well as plain tree output, there are many compiler options that +add extra debug information to trees when compiling a file; you can find the full list +in [ScalaSettings]. + +## Stopping the compiler early +Sometimes you may want to stop the compiler after a certain phase, for example to prevent +knock-on errors from occurring from a bug in an earlier phase. Use the flag +`-Ystop-after:` to prevent any phases executing afterwards. + +> e.g. `-Xprint:` where `phase` is a miniphase, will print after +> the whole phase group is complete, which may be several miniphases after `phase`. +> Instead you can use `-Ystop-after: -Xprint:` to stop +> immediately after the miniphase and see the trees that you intended. + +## Printing TASTy of a Class + +If you are working on an issue related to TASTy, it is good to know how to inspect +the contents of a TASTy file, produced from compilation of Scala files. + +The next example uses an [issue directory](../issues/reproduce.md#dotty-issue-workspace) to compile a class and print its TASTy. +In the directory, you should create a file `tasty/Foo.scala` (with contents of `class Foo`), +and create a file `tasty/launch.iss` with the following contents: + +``` +$ (rm -rv out || true) && mkdir out # clean up compiler output, create `out` dir. + +scala3/scalac -d $here/out $here/Foo.scala + +scala3/scalac -print-tasty $here/out/Foo.tasty +``` + +With sbt command `issue tasty` you will see output such as the following: + +``` +-------------------------------------------------------------------------------- +local/foo/out/Foo.tasty +-------------------------------------------------------------------------------- +Names: + 0: ASTs + 1: + 2: Foo + 3: +... +``` +and so on. + +## Inspecting The Representation of Types + +> [learn more about types](../architecture/types.md) in `dotc`. + +If you are curious about the representation of a type, say `[T] =>> List[T]`, +you can use a helper program [dotty.tools.printTypes][DottyTypeStealer], +it prints the internal representation of types, along with their class. It can be +invoked from the sbt shell with three arguments as follows: +```bash +sbt:scala3> scala3-compiler/Test/runMain + dotty.tools.printTypes + + + +``` + +- The first argument, `source`, is an arbitrary string that introduces some Scala definitions. +It may be the empty string `""`. +- The second argument, `kind`, determines the format of the following arguments, +accepting one of the following options: + - `rhs` - accept return types of definitions + - `class` - accept signatures for classes + - `method` - accept signatures for methods + - `type` - accept signatures for type definitions + - The empty string `""`, in which case `rhs` will be assumed. +- The remaining arguments are type signature strings, accepted in the format determined by +`kind`, and collected into a sequence `typeStrings`. Signatures are the part of a definition +that comes after its name, (or a simple type in the case of `rhs`) and may reference +definitions introduced by the `source` argument. + +Each one of `typeStrings` is then printed, displaying their internal structure, alongside their class. + +### Examples + +Here, given a previously defined `class Box { type X }`, you can inspect the return type `Box#X`: +```bash +sbt:scala3> scala3-compiler/Test/runMain +> dotty.tools.printTypes +> "class Box { type X }" +> "rhs" +> "Box#X" +[info] running (fork) dotty.tools.printTypes "class Box { type X }" rhs Box#X +TypeRef(TypeRef(ThisType(TypeRef(NoPrefix,module class )),class Box),type X) [class dotty.tools.dotc.core.Types$CachedTypeRef] +``` + +Here are some other examples you can try: +- `...printTypes "" "class" "[T] extends Seq[T] {}"` +- `...printTypes "" "method" "(x: Int): x.type"` +- `...printTypes "" "type" "<: Int" "= [T] =>> List[T]"` + +### Don't just print: extracting further information + +`dotty.tools.printTypes` is useful to to see the representation +of a type at a glance, but sometimes you want to extract more. Instead, you can use the +method `dotty.tools.DottyTypeStealer.stealType`. With the same inputs as `printTypes`, +it returns both a `Context` containing the definitions passed, along with the list of types. + +As a worked example let's create a test case to verify the structure of `Box#X` that you saw earlier: +```scala +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Types.* + +import org.junit.Test + +import dotty.tools.DottyTypeStealer, DottyTypeStealer.Kind + +class StealBox: + + @Test + def stealBox: Unit = + val (ictx, List(rhs)) = + DottyTypeStealer.stealType("class Box { type X }", Kind.rhs, "Box#X") + + given Context = ictx + + rhs match + case X @ TypeRef(Box @ TypeRef(ThisType(empty), _), _) => + assert(Box.name.toString == "Box") + assert(X.name.toString == "X") + assert(empty.name.toString == "") +``` + +[DottyTypeStealer]: https://github.com/lampepfl/dotty/blob/master/compiler/test/dotty/tools/DottyTypeStealer.scala +[ScalaSettings]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +[symbols]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/core/SymDenotations.scala diff --git a/docs/_docs/contributing/debugging.md b/docs/_docs/contributing/issues/other-debugging.md similarity index 94% rename from docs/_docs/contributing/debugging.md rename to docs/_docs/contributing/issues/other-debugging.md index 959ad6706290..1aa0fb85e5f8 100644 --- a/docs/_docs/contributing/debugging.md +++ b/docs/_docs/contributing/issues/other-debugging.md @@ -1,26 +1,8 @@ --- layout: doc-page -title: Debugging Techniques +title: Other Debugging Techniques --- -# Debugging Techniques -- [Setting up the playground](#setting-up-the-playground) -- [Show for human readable output](#show-for-human-readable-output) -- [How to disable color](#how-to-disable-color) -- [Reporting as a non-intrusive println](#reporting-as-a-non-intrusive-println) -- [Printing out trees after phases](#printing-out-trees-after-phases) -- [Printing out stack traces of compile time errors](#printing-out-stack-traces-of-compile-time-errors) -- [Configuring the printer output](#configuring-the-printer-output) -- [Figuring out an object creation site](#figuring-out-an-object-creation-site) - * [Via ID](#via-id) - * [Via tracer](#via-tracer) -- [Built-in Logging Architecture](#built-in-logging-architecture) - * [Printers](#printers) - * [Tracing](#tracing) - * [Reporter](#reporter) - -
      Table of contents generated with markdown-toc - ## Setting up the playground Consider the `../issues/Playground.scala` (relative to the Dotty directory) file is: diff --git a/docs/_docs/contributing/issues/reproduce.md b/docs/_docs/contributing/issues/reproduce.md new file mode 100644 index 000000000000..41d96327ef24 --- /dev/null +++ b/docs/_docs/contributing/issues/reproduce.md @@ -0,0 +1,127 @@ +--- +layout: doc-page +title: Reproducing an Issue +--- + +To try fixing it, you will first need to reproduce the issue, so that +- you can understand its cause +- you can verify that any changes made to the codebase have a positive impact on the issue. + +Say you want to reproduce locally issue [#7710], you would first copy the code from the *"Minimised Code"* +section of the issue to a file named e.g. `local/i7710.scala`, +and then try to compile it from the sbt console opened in the dotty root directory: +```bash +$ sbt +sbt:scala3> scala3/scalac -d local/out local/i7710.scala +``` +> Here, the `-d` flag specifies a directory `local/out` where generated code will be output. + +You can then verify that the local reproduction has the same behaviour as originally reported in the issue. +If so, then you can start to try and fix it. Otherwise, perhaps the issue is out of date, or +is missing information about how to accurately reproduce the issue. + +## Dotty Issue Workspace + +Sometimes you will need more complex commands to reproduce an issue, and it is useful to script these, which +can be done with [dotty-issue-workspace]. It allows to bundle sbt commands for issue reproduction in one +file and then run them from the Dotty project's sbt console. + +### Try an Example Issue + +Let's use [dotty-issue-workspace] to reproduce issue [#7710]: +1. Follow [the steps in the README][workspace-readme] to install the plugin. +2. In your Issue Workspace directory (as defined in the plugin's README file, + "Getting Started" section, step 2), create a subdirectory for the + issue: `mkdir i7710`. +3. Create a file for the reproduction: `cd i7710; touch Test.scala`. In that file, + insert the code from the issue. +4. In the same directory, create a file `launch.iss` with the following content: + ```bash + $ (rm -rv out || true) && mkdir out # clean up compiler output, create `out` dir. + + scala3/scalac -d $here/out $here/Test.scala + ``` + + - The first line, `$ (rm -rv out || true) && mkdir out` specifies a shell command + (it starts with `$`), in this case to ensure that there is a fresh `out` + directory to hold compiler output. + - The next line, `scala3/scalac -d $here/out $here/Test.scala` specifies an sbt + command, which will compile `Test.scala` and place any output into `out`. + `$here` is a special variable that will be replaced by the path of the parent + directory of `launch.iss` when executing the commands. +5. Now, from a terminal you can run the issue from sbt in the dotty directory + ([See here](../getting-started.md#compiling-and-running) for a reminder if you have not cloned the repo.): + ```bash + $ sbt + sbt:scala3> issue i7710 + ``` + This will execute all the commands in the `i7710/launch.iss` file one by one. + If you've set up `dotty-issue-workspace` as described in its README, + the `issue` task will know where to find the folder by its name. + +### Using Script Arguments + +You can use script arguments inside `launch.iss` to reduce the number of steps when +working with issues. + +Say you have an issue `foo`, with two alternative files that are very similar: +`original.scala`, which reproduces the issue, and `alt.scala`, which does not, +and you want to compile them selectively? + +You can achieve this via the following `launch.iss`: + +```bash +$ (rm -rv out || true) && mkdir out # clean up compiler output, create `out` dir. + +scala3/scalac -d $here/out $here/$1.scala # compile the first argument following `issue foo ` +``` + +It is similar to the previous example, except now you will compile a file `$1.scala`, referring +to the first argument passed after the issue name. The command invoked would look like +`issue foo original` to compile `original.scala`, and `issue foo alt` for `alt.scala`. + +In general, you can refer to arguments passed to the `issue ` command using +the dollar notation: `$1` for the first argument, `$2` for the second and so on. + +### Multiline Commands + +Inside a `launch.iss` file, one command can be spread accross multiple lines. For example, +if your command has multiple arguments, you can put each argument on a new line. + +Multiline commands can even have comments inbetween lines. This is useful +if you want to try variants of a command with optional arguments (such as configuration). +You can put the optional arguments on separate lines, and then decide when they are passed to +the command by placing `#` in front to convert it to a comment (i.e. the argument will +not be passed). This saves typing the same arguments each time you want to use them. + +The following `launch.iss` file is an example of how you can use multiline commands as a +template for solving issues that [run compiled code](../issues/testing.md#checking-program-output). It demonstrates configuring the +`scala3/scalac` command using compiler flags, which are commented out. +Put your favourite flags there for quick usage. + +```bash +$ (rm -rv out || true) && mkdir out # clean up compiler output, create `out` dir. + +scala3/scalac # Invoke the compiler task defined by the Dotty sbt project + -d $here/out # All the artefacts go to the `out` folder created earlier + # -Xprint:typer # Useful debug flags, commented out and ready for quick usage. Should you need one, you can quickly access it by uncommenting it. + # -Ydebug-error + # -Yprint-debug + # -Yprint-debug-owners + # -Yshow-tree-ids + # -Ydebug-tree-with-id 340 + # -Ycheck:all + $here/$1.scala # Invoke the compiler on the file passed as the second argument to the `issue` command. E.g. `issue foo Hello` will compile `Hello.scala` assuming the issue folder name is `foo`. + +scala3/scala -classpath $here/out Test # Run main method of `Test` generated by the compiler run. +``` + +## Conclusion + +In this section, you have seen how to reproduce an issue locally, and next you will see +how to try and detect its root cause. + +[lampepfl/dotty]: https://github.com/lampepfl/dotty/issues +[#7710]: https://github.com/lampepfl/dotty/issues/7710 +[dotty-issue-workspace]: https://github.com/anatoliykmetyuk/dotty-issue-workspace +[workspace-readme]: https://github.com/anatoliykmetyuk/dotty-issue-workspace#getting-started \ No newline at end of file diff --git a/docs/_docs/contributing/issues/testing.md b/docs/_docs/contributing/issues/testing.md new file mode 100644 index 000000000000..1f7c35c6d58a --- /dev/null +++ b/docs/_docs/contributing/issues/testing.md @@ -0,0 +1,212 @@ +--- +layout: doc-page +title: Testing Your Changes +--- + +It is important to add tests before a pull request, to verify that everything is working as expected, +and act as proof of what is valid/invalid Scala code (in case it is broken in the future). +In this section you will see the testing procedures in Scala 3. + +## Running all Tests + +Running all tests in Dotty is as simple as: + +```bash +$ sbt test +``` +Specifically, `sbt test` runs all tests that do _not_ require a bootstrapped +compiler. In practice, this means that it runs all compilation tests meeting +this criterion, as well as all non-compiler tests. + +To run all tests of Scala 3, including for compiler, REPL, libraries and more, run the following in sbt: + +```bash +$ sbt +sbt:scala3> scala3-bootstrapped/test +``` + +Often however it is not necessary to test everything if your changes are localised to one area, +you will see in the following sections the different kinds of tests, and how +to run individual tests. + +## Compilation Tests + +Compilation tests run the compiler over input files, using various settings. Input files +are found within the `tests/` directory at the root of the compiler repo. + +Test input files are categorised further by placing them in the subdirectories +of the `tests/` directory. A small selection of test categories include: + +- `tests/pos` – tests that should compile: pass if compiles successfully. +- `tests/neg` – should not compile: pass if fails compilation. Useful, e.g., to test an expected compiler error. +- `tests/run` – these tests not only compile but are also run. + +### Naming and Running a Test Case + +Tests are, by convention, named after the number of the issue they are fixing. +e.g. if you are fixing issue 101, then the test should be named `i101.scala`, for a single-file test, +or be within a directory called `i101/` for a multi-file test. + +To run the test, invoke the sbt command `testCompilation i101` (this will match all tests with `"i101"` in +the name, so it is useful to use a unique name) + +The test groups – `pos`, `neg`, etc. – are defined in [CompilationTests]. If you want to run a group +of tests, e.g. `pos`, you can do so via `testOnly *CompilationTests -- *pos` command. + +### Testing a Single Input File + +If your issue is reproducible by only one file, put that file under an appropriate category. +For example, if your issue is about getting rid of a spurious compiler error (that is a code that doesn't compile should, in fact, compile), you can create a file `tests/pos/i101.scala`. + +### Testing Multiple Input Files + +If you need more than one file to reproduce an issue, create a directory instead of a file +e.g. `tests/pos/i101/`, and put all the Scala files that are needed to reproduce the issue there. +There are two ways to organise the input files within: + +**1: Requiring classpath dependency:** Sometimes issues require one file to be compiled after the other, +(e.g. if the issue only happens with a library dependency, like with Java interop). In this case, +the outputs of the first file compiled will be available to the next file compiled, available via the classpath. +This is called *separate compilation*. + +To achieve this, within `tests/pos/i101/`, add a suffix `_n` to each file name, where `n` is an integer defining the +order in which the file will compile. E.g. if you have two files, `Lib.scala` and `Main.scala`, and you need them +compiled separately – Lib first, Main second, then name them `Lib_1.scala` and `Main_2.scala`. + +**2: Without classpath dependency:** If your issue does not require a classpath dependency, your files can be compiled +in a single run, this is called *joint compilation*. In this case use file names without the `_n` suffix. + +### Checking Program Output + +`tests/run` tests verify the run-time behaviour of a test case. The output is checked by invoking a main method +on a class `Test`, this can be done with either +```scala +@main def Test: Unit = assert(1 > 0) +``` +or +```scala +object Test extends scala.App: + assert(1 > 0) +``` + +If your program also prints output, this can be compared against `*.check` files. +These contain the expected output of a program. Checkfiles are named after the issue they are checking, +e.g. `tests/run/i101.check` will check either `tests/run/i101.scala` or `tests/run/i101/`. + +### Checking Compilation Errors + +`tests/neg` tests verify that a file does not compile, and user-facing errors are produced. There are other neg +categories such as `neg-custom-args`, i.e. with `neg` prefixing the directory name. Test files in the `neg*` +categories require annotations for the lines where errors are expected. To do this add one `// error` token to the +end of a line for each expected error. For example, if there are three expected errors, the end of the line should contain +`// error // error // error`. + +You can verify the content of the error messages with a `*.check` file. These contain the expected output of the +compiler. Checkfiles are named after the issue they are checking, +e.g. `i101.check` will check either `tests/neg/i101.scala` or `tests/neg/i101/`. +*Note:* checkfiles are not required for the test to pass, however they do add stronger constraints that the errors +are as expected. + +### If Checkfiles do not Match Output + +If the actual output mismatches the expected output, the test framework will dump the actual output in the file +`*.check.out` and fail the test suite. It will also output the instructions to quickly replace the expected output +with the actual output, in the following format: + +``` +Test output dumped in: tests/neg/Sample.check.out + See diff of the checkfile + > diff tests/neg/Sample.check tests/neg/Sample.check.out + Replace checkfile with current output + > mv tests/neg/Sample.check.out tests/neg/Sample.check +``` + +### Tips for creating Checkfiles + +To create a checkfile for a test, you can do one of the following: + +1. Create an empty checkfile + - then add arbitrary content + - run the test + - when it fails, use the `mv` command reported by the test to replace the initial checkfile with the actual output. +2. Manually compile the file you are testing with `scala3/scalac` + - copy-paste whatever console output the compiler produces to the checkfile. + +### Automatically Updating Checkfiles + +When complex or many checkfiles must be updated, `testCompilation` can run in a mode where it overrides the +checkfiles with the test outputs. +```bash +$ sbt +> testCompilation --update-checkfiles +``` + +Use `--help` to see all the options +```bash +$ sbt +> testCompilation --help +``` + +### Bootstrapped-only tests + +To run `testCompilation` on a bootstrapped Dotty compiler, use +`scala3-compiler-bootstrapped/testCompilation` (with the same syntax as above). +Some tests can only be run in bootstrapped compilers; that includes all tests +with `with-compiler` in their name. + +### From TASTy tests + +`testCompilation` has an additional mode to run tests that compile code from a `.tasty` file. +Modify the lists in [compiler/test/dotc] to enable or disable tests from `.tasty` files. + +```bash +$ sbt +> testCompilation --from-tasty +``` + +## Unit Tests + +Unit tests cover the other areas of the compiler, such as interactions with the REPL, scripting tools and more. +They are defined in [compiler/test], so if your use case isn't covered by this guide, +you may need to consult the codebase. Some common areas are highlighted below: + +### SemanticDB tests + +To test the SemanticDB output from the `extractSemanticDB` phase (enabled with the `-Xsemanticdb` flag), run the following sbt command: +```bash +$ sbt +sbt:scala3> scala3-compiler-bootstrapped/testOnly + dotty.tools.dotc.semanticdb.SemanticdbTests +``` + +[SemanticdbTests] uses source files in `tests/semanticdb/expect` to generate "expect files": +these verify both +- SemanticDB symbol occurrences inline in sourcecode (`*.expect.scala`) +- complete output of all SemanticDB information (`metac.expect`). + +Expect files are used as regression tests to detect changes in the compiler. +Their correctness is determined by human inspection. + +If expect files change then [SemanticdbTests] will fail, and generate new expect files, providing instructions for +comparing the differences and replacing the outdated expect files. + +If you are planning to update the SemanticDB output, you can do it in bulk by running the command +```bash +$ sbt +sbt:scala3> scala3-compiler/Test/runMain + dotty.tools.dotc.semanticdb.updateExpect +``` + +then compare the changes via version control. + +## Troubleshooting + +Some of the tests depend on temporary state stored in the `out` directory. In rare cases, that directory +can enter an inconsistent state and cause spurious test failures. If you suspect a spurious test failure, +you can run `rm -rf out/*` from the root of the repository and run your tests again. If that fails, you +can try `git clean -xfd`. + +[CompilationTests]: https://github.com/lampepfl/dotty/blob/master/compiler/test/dotty/tools/dotc/CompilationTests.scala +[compiler/test]: https://github.com/lampepfl/dotty/blob/master/compiler/test/ +[compiler/test/dotc]: https://github.com/lampepfl/dotty/tree/master/compiler/test/dotc +[SemanticdbTests]: https://github.com/lampepfl/dotty/blob/master/compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala diff --git a/docs/_docs/contributing/procedures/index.md b/docs/_docs/contributing/procedures/index.md index 01c76f72c00c..db2b09dbe80f 100644 --- a/docs/_docs/contributing/procedures/index.md +++ b/docs/_docs/contributing/procedures/index.md @@ -2,3 +2,7 @@ layout: index title: Procedures --- + +This chapter of the guide describes: +- [How to release a procedure](./release.md) +- [How to test the vulpix framework](./vulpix.md) \ No newline at end of file diff --git a/docs/_docs/contributing/procedures/vulpix.md b/docs/_docs/contributing/procedures/vulpix.md index 5e8a2eab425b..1eea2fa24778 100644 --- a/docs/_docs/contributing/procedures/vulpix.md +++ b/docs/_docs/contributing/procedures/vulpix.md @@ -3,7 +3,6 @@ layout: doc-page title: Test Vulpix Framework --- -# Test Vulpix Framework If you are modifying the Vulpix framework and need a playground with dummy tests to try out your modifications, do the following. Create the directory structure for the playground: diff --git a/docs/_docs/contributing/testing.md b/docs/_docs/contributing/testing.md deleted file mode 100644 index a01cdb08f8ab..000000000000 --- a/docs/_docs/contributing/testing.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -layout: doc-page -title: Testing in Dotty ---- - -Running all tests in Dotty is as simple as: - -```bash -$ sbt test -``` - -Specifically, `sbt test` runs all tests that do _not_ require a bootstrapped -compiler. In practice, this means that it runs all compilation tests meeting -this criterion, as well as all non-compiler tests. - -The entire suite of tests can be run using the bootstrapped compiler as follows: - -```bash -$ sbt -> scala3-bootstrapped/test -``` - -There are currently several forms of tests in Dotty. These can be split into -two categories: - -## Unit tests -These tests can be found in `/test` and are used to check -functionality of specific parts of the codebase in isolation e.g: parsing, -scanning and message errors. - -To run all tests in e.g., for the compiler test-suite you can write: - -```bash -$ sbt -> scala3-compiler/test -``` - -To run a single test class you use `testOnly` and the fully qualified class name. -For example: - -```bash -> testOnly dotty.tools.dotc.transform.TreeTransformerTest -``` - -The test command follows a regular expression-based syntax `testOnly * -- *`. -The right-hand side picks a range of names for methods and the left-hand side picks a range of class names and their -fully-qualified paths. - -Consequently, you can restrict the aforementioned executed test to a subset of methods by appending ``-- *method_name``. -The example below picks up all methods with the name `canOverwrite`: - -```bash -> testOnly dotty.tools.dotc.transform.TreeTransformerTest -- *canOverwrite -``` - -Additionally, you can run all tests named `method_name`, in any class, without providing a class name: - -```bash -> testOnly -- *canOverwrite -``` - -You can also run all paths of classes of a certain name: - -```bash -> testOnly *.TreeTransformerTest -``` - -### Testing with checkfiles -Some tests support checking the output of the run or the compilation against a checkfile. A checkfile is a file in which the expected output of the compilation or run is defined. A test against a checkfile fails if the actual output mismatches the expected output. - -Currently, the `run` and `neg` (compilation must fail for the test to succeed) tests support the checkfiles. `run`'s checkfiles contain an expected run output of the successfully compiled program. `neg`'s checkfiles contain an expected error output during compilation. - -Absence of a checkfile is **not** a condition for the test failure. E.g. if a `neg` test fails with the expected number of errors and there is no checkfile for it, the test still passes. - -Checkfiles are located in the same directories as the tests they check, have the same name as these tests with the extension `*.check`. E.g. if you have a test named `tests/neg/foo.scala`, you can create a checkfile for it named `tests/neg/foo.check`. And if you have a test composed of several files in a single directory, e.g. `tests/neg/manyScalaFiles`, the checkfile will be `tests/neg/manyScalaFiles.check`. - -If the actual output mismatches the expected output, the test framework will dump the actual output in the file `*.check.out` and fail the test suite. It will also output the instructions to quickly replace the expected output with the actual output, in the following format: - -``` -Test output dumped in: tests/playground/neg/Sample.check.out - See diff of the checkfile - > diff tests/playground/neg/Sample.check tests/playground/neg/Sample.check.out - Replace checkfile with current output - > mv tests/playground/neg/Sample.check.out tests/playground/neg/Sample.check -``` - -To create a checkfile for a test, you can do one of the following: - -- Create a dummy checkfile with a random content, run the test, and, when it fails, use the `mv` command reported by the test to replace the dummy checkfile with the actual output. -- Manually compile the file you are testing with `scalac` and copy-paste whatever console output the compiler produces to the checkfile. - -## Integration tests -These tests are Scala source files expected to compile with Dotty (pos tests), -along with their expected output (run tests) or errors (neg tests). - -All of these tests are contained in the `./tests/*` directories and can be run with the `testCompilation` command. Tests in folders named `with-compiler` are an exception, see next section. - -Currently to run these tests you need to invoke from sbt: - -```bash -$ sbt -> testCompilation -``` - -(which is effectively the same with `testOnly dotty.tools.dotc.CompilationTests`) - -It is also possible to run tests filtered, again from sbt: - -```bash -$ sbt -> testCompilation companions -``` - -This will run both the test `./tests/pos/companions.scala` and -`./tests/neg/companions.scala` since both of these match the given string. -This also means that you could run `testCompilation` with no arguments to run all integration tests. - -When complex checkfiles must be updated, `testCompilation` can run in a mode where it overrides the checkfiles with the test outputs. -```bash -$ sbt -> testCompilation --update-checkfiles -``` - -Use `--help` to see all the options -```bash -$ sbt -> testCompilation --help -``` - -### Joint and separate sources compilation - -When the sources of a test consist of multiple source files places in a single directory they are passed to the compiler in a single run and the compiler decides in which order to compile them. In some cases, however, to reproduce a specific test scenario it might be necessary to compile the source files in several steps in a specified order. To achieve that one can add a `_${step_index}` suffix to a file name (before the `.scala` or `.java` extension) indicating the order of compilation. E.g. if the test directory contains files named `Foo_1.scala`, `Bar_2.scala` and `Baz_2.scala` then `Foo_1.scala` will be compiled first and after that `Bar_2.scala` together with `Baz_2.scala`. - -The other kind of suffix that can modify how particular files are compiled is `_c${compilerVersion}`. When specified, the file will be compiled with a specific version of the compiler instead of the one developed on the current branch. - -Different suffixes can be mixed together (their order is not important although consistency is advised), e.g. `Foo_1_c3.0.2`, `Bar_2_c3.1.0`. - -### Bootstrapped-only tests - -To run `testCompilation` on a bootstrapped Dotty compiler, use -`scala3-compiler-bootstrapped/testCompilation` (with the same syntax as above). -Some tests can only be run in bootstrapped compilers; that includes all tests -with `with-compiler` in their name. - -### From TASTy tests - -`testCompilation` has an additional mode to run tests that compile code from a `.tasty` file. - Modify blacklist and whitelists in `compiler/test/dotc` to enable or disable tests from `.tasty` files. - - ```bash - $ sbt - > testCompilation --from-tasty - ``` - - This mode can be run under `scala3-compiler-bootstrapped/testCompilation` to test on a bootstrapped Dotty compiler. - -### SemanticDB tests - -```bash -$ sbt -> scala3-compiler-bootstrapped/testOnly dotty.tools.dotc.semanticdb.SemanticdbTests -``` - -The output of the `extractSemanticDB` phase, enabled with `-Xsemanticdb` is tested with the bootstrapped JUnit test -`dotty.tools.dotc.semanticdb.SemanticdbTests`. It uses source files in `tests/semanticdb/expect` to generate -two kinds of output file that are compared with "expect files": placement of semanticdb symbol occurrences inline in -sourcecode (`*.expect.scala`), for human verification by inspection; and secondly metap formatted output which outputs -all information stored in semanticdb (`metac.expect`). -Expect files are used as regression tests to detect changes in the compiler. - -The test suite will create a new file if it detects any difference, which can be compared with the -original expect file, or if the user wants to globally replace all expect files for semanticdb they can use -`scala3-compiler-bootstrapped/test:runMain dotty.tools.dotc.semanticdb.updateExpect`, and compare the changes via version -control. - -### Test regimes - -Continuous integration, managed by GitHub Actions, does not run all jobs when a pull request is created. -In particular, test jobs for testing under JDK 8 and Windows are not run. Those jobs are run only for the nightly build. - -If a PR may fail differentially under either JDK 8 or Windows, the test jobs may be triggered by adding -a special command to the PR comment text: - -``` -[test_java8] -[test_windows_full] -``` -Furthermore, CI tests are bootstrapped. A job to also run tests non-bootstrapped may be triggered manually: -``` -[test_non_bootstrapped] -``` -A trivial PR, such as a fix for a typo in a comment or when contributing other documentation, may benefit by skipping CI tests altogether: -``` -[skip ci] -``` -Other jobs which are normally run can also be selectively skipped: -``` -[skip community_build] -[skip test_windows_fast] -``` - -## Troubleshooting - -Some of the tests depend on temporary state stored in the `out` directory. In rare cases, that directory -can enter an inconsistent state and cause spurious test failures. If you suspect a spurious test failure, -you can run `rm -rf out/*` from the root of the repository and run your tests again. If that fails, you -can try `git clean -xfd`. diff --git a/docs/_docs/contributing/tools/index.md b/docs/_docs/contributing/tools/index.md index 92503ee82013..e784e3e15d61 100644 --- a/docs/_docs/contributing/tools/index.md +++ b/docs/_docs/contributing/tools/index.md @@ -2,3 +2,8 @@ layout: index title: IDEs and Tools --- + +This chapter of the guide describes how to use Dotty with IDEs and other tools: +- [IDEs](./ide.md) +- [Use Mill](./mill.md) +- [Use Scalafix](./scalafix.md) diff --git a/docs/_docs/contributing/tools/scalafix.md b/docs/_docs/contributing/tools/scalafix.md index 58c7d0eb7b3a..30c7050f8b3e 100644 --- a/docs/_docs/contributing/tools/scalafix.md +++ b/docs/_docs/contributing/tools/scalafix.md @@ -3,8 +3,6 @@ layout: doc-page title: Working with Scalafix --- -# Working with Scalafix - First, create a new rule as follows (command from https://scalacenter.github.io/scalafix/docs/developers/setup.html): ```bash diff --git a/docs/_docs/contributing/workflow.md b/docs/_docs/contributing/workflow.md index 956ce2998c75..36d91bf02707 100644 --- a/docs/_docs/contributing/workflow.md +++ b/docs/_docs/contributing/workflow.md @@ -103,8 +103,27 @@ The basics of working with Dotty codebase are documented [here](https://dotty.ep | Command | Description | |------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------| +| `scala3/scalac` | Run the compiler directly, with any current changes. | +| `scala3/scala` | Run the main method of a given class name. | | `scalac ../issues/Playground.scala` | Compile the given file – path relative to the Dotty directory. Output the compiled class files to the Dotty directory itself. | | `scala Playground` | Run the compiled class `Playground`. Dotty directory is on classpath by default. | | `repl` | Start REPL | +| `scala3/scalac -print-tasty Foo.tasty` | Print the TASTy of top-level class `Foo` | +| `scala3-bootstrapped/test` | Run all tests for Scala 3. (Slow, recommended for CI only) | +| `scala3-bootstrapped/publishLocal` | Build Scala 3 locally. (Use to debug a specific project) | +| `scalac ../issues/Playground.scala` | Compile the given file – path relative to the Dotty directory. Output the compiled class files to the Dotty directory itself.| | `testOnly dotty.tools.dotc.CompilationTests -- *pos` | Run test (method) `pos` from `CompilationTests` suite. | | `testCompilation sample` | In all test suites, run test files containing the word `sample` in their title. | +| `scala3-compiler/Test/runMain dotty.tools.printTypes`| Print types underlying representation | + + +## Shell Commands + +| Command | Description | +|--------------------------------------|------------------------------------------------------------------| +| `rm -rv *.tasty *.class out || true` | clean all compiled artifacts, from root dotty directory | + + + + + diff --git a/docs/sidebar.yml b/docs/sidebar.yml index d640f15ffa6f..345134cf2e9b 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -166,11 +166,18 @@ subsection: directory: docs/contributing index: contributing/index.md subsection: - - page: contributing/contribute-knowledge.md - page: contributing/getting-started.md - - page: contributing/workflow.md - - page: contributing/testing.md - - page: contributing/debugging.md + - index: contributing/workflow.md + subsection: + - page: contributing/issues/reproduce.md + - page: contributing/issues/cause.md + - page: contributing/issues/areas.md + - page: contributing/issues/debugging.md + - page: contributing/issues/other-debugging.md + - page: contributing/issues/inspection.md + - page: contributing/issues/efficiency.md + - page: contributing/issues/testing.md + - page: contributing/issues/checklist.md - title: IDEs and Tools directory: tools index: contributing/tools/index.md @@ -179,10 +186,21 @@ subsection: - page: contributing/tools/mill.md - page: contributing/tools/scalafix.md - title: Procedures + directory: procedures index: contributing/procedures/index.md subsection: - page: contributing/procedures/release.md - page: contributing/procedures/vulpix.md + - title: High Level Architecture + directory: architecture + index: contributing/architecture/index.md + subsection: + - page: contributing/architecture/lifecycle.md + - page: contributing/architecture/context.md + - page: contributing/architecture/phases.md + - page: contributing/architecture/types.md + - page: contributing/architecture/time.md + - page: contributing/architecture/symbols.md - title: Internals directory: docs/internals index: internals/index.md From 5c4493b65b00827f0486101216fee64120e3c732 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 11:51:34 +0200 Subject: [PATCH 574/657] test: add in a regression test for #8300 Closes #8300 --- tests/pos/i8300.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/pos/i8300.scala diff --git a/tests/pos/i8300.scala b/tests/pos/i8300.scala new file mode 100644 index 000000000000..f106b24dbd1c --- /dev/null +++ b/tests/pos/i8300.scala @@ -0,0 +1,15 @@ +// https://github.com/lampepfl/dotty/issues/8300 + +type Bar[X] = X match { + case List[a] => List[Tuple1[a]] + case Set[a] => Set[Tuple1[a]] +} + +object Test: + (Set(1, 2, 3), List("a", "b")).map( + [A] => + (a: A) => + a match { + case it: Iterable[x] => it.map(Tuple1(_)).asInstanceOf[Bar[A]] + } + ) From ab64f155bc748a0b4da33ca0004025350b601c3b Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 11:57:31 +0200 Subject: [PATCH 575/657] test: add in a regression test for #8321 Closes #8321 --- tests/pos/i8321.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/pos/i8321.scala diff --git a/tests/pos/i8321.scala b/tests/pos/i8321.scala new file mode 100644 index 000000000000..825598d0b668 --- /dev/null +++ b/tests/pos/i8321.scala @@ -0,0 +1,9 @@ + +object Test: + inline def concat[A <: Tuple, B <: Tuple]( + a: Option[A], + b: Option[B] + ): Option[Tuple.Concat[A, B]] = + a.zip(b).map(_ ++ _) + + concat(Some(1, 2), Some(3, 4)) From d23db881918b7916bfe5fbfd8fabd13466916495 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 12:29:18 +0200 Subject: [PATCH 576/657] test: add in a regression test for #8742 There was also some other tests that were _very_ similar to this, so it seemed like the logical place to also just include this one instead of creating another file for it. Closes #8742 --- tests/neg-custom-args/fatal-warnings/i8711.check | 4 ++++ tests/neg-custom-args/fatal-warnings/i8711.scala | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/tests/neg-custom-args/fatal-warnings/i8711.check b/tests/neg-custom-args/fatal-warnings/i8711.check index 0035af0755d4..491d1678b5ac 100644 --- a/tests/neg-custom-args/fatal-warnings/i8711.check +++ b/tests/neg-custom-args/fatal-warnings/i8711.check @@ -6,3 +6,7 @@ 12 | case x: C => x // error | ^^^^ | Unreachable case +-- [E030] Match case Unreachable Error: tests/neg-custom-args/fatal-warnings/i8711.scala:17:9 -------------------------- +17 | case x: (B | C) => x // error + | ^^^^^^^^^^ + | Unreachable case diff --git a/tests/neg-custom-args/fatal-warnings/i8711.scala b/tests/neg-custom-args/fatal-warnings/i8711.scala index e37f7a8b039f..46fc5a85c90a 100644 --- a/tests/neg-custom-args/fatal-warnings/i8711.scala +++ b/tests/neg-custom-args/fatal-warnings/i8711.scala @@ -12,4 +12,9 @@ object Test { case x: C => x // error case _ => } + + def baz(x: A) = x match { + case x: (B | C) => x // error + case _ => + } } From c5badad29946e07fbf3bcfea33ce1c54ebde5bfe Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 13:32:29 +0200 Subject: [PATCH 577/657] test: add in a regression test for #9361 Closes #9361 --- tests/pos/i9361.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/pos/i9361.scala diff --git a/tests/pos/i9361.scala b/tests/pos/i9361.scala new file mode 100644 index 000000000000..18efd203d885 --- /dev/null +++ b/tests/pos/i9361.scala @@ -0,0 +1,9 @@ +// https://github.com/lampepfl/dotty/issues/9361 + +import scala.quoted._ + +trait CPM[F[_]] + +def matchTerm(t: Expr[Any])(using qctx: Quotes): Unit = + t match + case '{ ??? : CPM[m2] } => ??? From 627b558c534f86861c3cfda7599a9fb2d583830f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 12 May 2023 14:02:03 +0200 Subject: [PATCH 578/657] Refactor way we handle hole contents and tags while pickling quotes --- .../tools/dotc/transform/PickleQuotes.scala | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index e7a628f68663..563e7dad20dd 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -33,11 +33,11 @@ import scala.annotation.constructorOnly * val x1: U1 = ??? * val x2: U2 = ??? * ... - * {{{ 3 | x1 | contents0 | T0 }}} // hole + * {{{ 3 | x1 | holeContents0 | T0 }}} // hole * ... - * {{{ 4 | x2 | contents1 | T1 }}} // hole + * {{{ 4 | x2 | holeContents1 | T1 }}} // hole * ... - * {{{ 5 | x1, x2 | contents2 | T2 }}} // hole + * {{{ 5 | x1, x2 | holeContents2 | T2 }}} // hole * ... * } * ``` @@ -93,25 +93,25 @@ class PickleQuotes extends MacroTransform { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match case Apply(Select(quote: Quote, nme.apply), List(quotes)) => - val (contents, quote1) = makeHoles(quote) + val (holeContents, quote1) = extractHolesContents(quote) val quote2 = encodeTypeArgs(quote1) - val contents1 = contents.map(transform(_)) ::: quote.tags - PickleQuotes.pickle(quote2, quotes, contents1) + val holeContents1 = holeContents.map(transform(_)) + PickleQuotes.pickle(quote2, quotes, holeContents1) case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod => tree case _ => super.transform(tree) } - private def makeHoles(quote: tpd.Quote)(using Context): (List[Tree], tpd.Quote) = + private def extractHolesContents(quote: tpd.Quote)(using Context): (List[Tree], tpd.Quote) = class HoleContentExtractor extends Transformer: - private val contents = List.newBuilder[Tree] + private val holeContents = List.newBuilder[Tree] override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match case tree @ Hole(isTerm, _, _, content) => assert(isTerm) assert(!content.isEmpty) - contents += content + holeContents += content val holeType = getTermHoleType(tree.tpe) val hole = untpd.cpy.Hole(tree)(content = EmptyTree).withType(holeType) cpy.Inlined(tree)(EmptyTree, Nil, hole) @@ -147,10 +147,10 @@ class PickleQuotes extends MacroTransform { mapOver(tp) } - /** Get the contents of the transformed tree */ + /** Get the holeContents of the transformed tree */ def getContents() = - val res = contents.result - contents.clear() + val res = holeContents.result + holeContents.clear() res end HoleContentExtractor @@ -159,7 +159,7 @@ class PickleQuotes extends MacroTransform { val quote1 = cpy.Quote(quote)(body1, quote.tags) (holeMaker.getContents(), quote1) - end makeHoles + end extractHolesContents /** Encode quote tags as holes in the quote body. * @@ -236,7 +236,7 @@ object PickleQuotes { val name: String = "pickleQuotes" val description: String = "turn quoted trees into explicit run-time data structures" - def pickle(quote: Quote, quotes: Tree, contents: List[Tree])(using Context) = { + def pickle(quote: Quote, quotes: Tree, holeContents: List[Tree])(using Context) = { val body = quote.body val bodyType = quote.bodyType @@ -334,19 +334,14 @@ object PickleQuotes { case x :: Nil => Literal(Constant(x)) case xs => tpd.mkList(xs.map(x => Literal(Constant(x))), TypeTree(defn.StringType)) - // TODO split holes earlier into types and terms. This all holes in each category can have consecutive indices - val (typeSplices, termSplices) = contents.zipWithIndex.partition { - _._1.tpe.derivesFrom(defn.QuotedTypeClass) - } - // This and all closures in typeSplices are removed by the BetaReduce phase val types = - if typeSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without contents as small as possible - else SeqLiteral(typeSplices.map(_._1), TypeTree(defn.QuotedTypeClass.typeRef.appliedTo(WildcardType))) + if quote.tags.isEmpty then Literal(Constant(null)) // keep pickled quote without holeContents as small as possible + else SeqLiteral(quote.tags, TypeTree(defn.QuotedTypeClass.typeRef.appliedTo(WildcardType))) // This and all closures in termSplices are removed by the BetaReduce phase val termHoles = - if termSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without contents as small as possible + if holeContents.isEmpty then Literal(Constant(null)) // keep pickled quote without holeContents as small as possible else Lambda( MethodType( @@ -354,7 +349,7 @@ object PickleQuotes { List(defn.IntType, defn.SeqType.appliedTo(defn.AnyType), defn.QuotesClass.typeRef), defn.QuotedExprClass.typeRef.appliedTo(defn.AnyType)), args => - val cases = termSplices.map { case (splice, idx) => + val cases = holeContents.zipWithIndex.map { case (splice, idx) => val defn.FunctionOf(argTypes, defn.FunctionOf(quotesType :: _, _, _), _) = splice.tpe: @unchecked val rhs = { val spliceArgs = argTypes.zipWithIndex.map { (argType, i) => @@ -413,7 +408,7 @@ object PickleQuotes { case _ => None if body.isType then - if contents.isEmpty && body.symbol.isPrimitiveValueClass then taggedType() + if holeContents.isEmpty && body.symbol.isPrimitiveValueClass then taggedType() else pickleAsTasty() else getLiteral(body) match From 71959523381bbb6450dbb6c76e98b03dff205623 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 12 May 2023 14:21:35 +0200 Subject: [PATCH 579/657] Remove illegal use of WildcardType --- compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 563e7dad20dd..19443dfb42e3 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -337,7 +337,7 @@ object PickleQuotes { // This and all closures in typeSplices are removed by the BetaReduce phase val types = if quote.tags.isEmpty then Literal(Constant(null)) // keep pickled quote without holeContents as small as possible - else SeqLiteral(quote.tags, TypeTree(defn.QuotedTypeClass.typeRef.appliedTo(WildcardType))) + else SeqLiteral(quote.tags, TypeTree(defn.QuotedTypeClass.typeRef.appliedTo(TypeBounds.emptyPolyKind))) // This and all closures in termSplices are removed by the BetaReduce phase val termHoles = From c779c26582c72d4802a3a89b3eb8634a5d2b7e07 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 17:42:08 +0200 Subject: [PATCH 580/657] test: add in a regression test for #10242 Closes #10242 --- tests/pos/i10242.scala | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/pos/i10242.scala diff --git a/tests/pos/i10242.scala b/tests/pos/i10242.scala new file mode 100644 index 000000000000..707c4c9f0a0c --- /dev/null +++ b/tests/pos/i10242.scala @@ -0,0 +1,32 @@ +// https://github.com/lampepfl/dotty/issues/10242 +type Foo[A, B <: A] = A + +type Bar[A] = A match { + case Foo[String, x] => Unit +} + +import scala.compiletime.ops.int.* + +sealed trait HList +final case class HCons[H <: Int & Singleton, T <: HList](head: H, tail: T) + extends HList +sealed trait HNil extends HList +case object HNil extends HNil + +sealed trait Tensor[T, S <: HList] + +object tf: + def zeros[S <: HList](shape: S): Tensor[Float, S] = ??? + + type NumElements[X <: HList] <: Int = X match + case HNil => 1 + case HCons[head, tail] => head * NumElements[tail] + + def reshape[T, From <: HList, To <: HList]( + tensor: Tensor[T, From], + shape: To + )(using NumElements[From] =:= NumElements[To]): Tensor[T, To] = ??? + +object test: + val x = HCons(1, HCons(2, HNil)) + val y = tf.reshape(tf.zeros(x), HCons(2, HCons(1, HNil))) From 9310fc8561d1ee1677b64ef2886975f73ed3f6d7 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 08:40:43 +0200 Subject: [PATCH 581/657] test: add in regression test for #7445 This adds in a regression test for the minimized issue, but the original one now has a different issue with pickling. I've added this in as well, but added it to the excludes. --- compiler/test/dotc/pos-test-pickling.blacklist | 3 +++ tests/pos/i7445a.scala | 10 ++++++++++ tests/pos/i7445b.scala | 12 ++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 tests/pos/i7445a.scala create mode 100644 tests/pos/i7445b.scala diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index d1dd83f36ff7..86b14e584d15 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -94,3 +94,6 @@ i4176-gadt.scala i13974a.scala java-inherited-type1 + +# recursion limit exceeded +i7445b.scala diff --git a/tests/pos/i7445a.scala b/tests/pos/i7445a.scala new file mode 100644 index 000000000000..2b54166de3f0 --- /dev/null +++ b/tests/pos/i7445a.scala @@ -0,0 +1,10 @@ +// https://github.com/lampepfl/dotty/issues/7445 + +object Main { + type O1[A] = { + type OutInner[X] = Unit + type Out = OutInner[A] + } + + def f1: O1[Int]#Out = ??? +} diff --git a/tests/pos/i7445b.scala b/tests/pos/i7445b.scala new file mode 100644 index 000000000000..1d49479ef0a5 --- /dev/null +++ b/tests/pos/i7445b.scala @@ -0,0 +1,12 @@ +// https://github.com/lampepfl/dotty/issues/7445 + +type O1[A] = { + type OutInner[Ts] <: Tuple = Ts match { + case EmptyTuple => EmptyTuple + case h *: t => h *: OutInner[t] + } + + type Out = OutInner[A] +} + +def f1: O1[(Int, Int)]#Out = ??? From 27b84f009a52b7b47c8f3982833985eab3599c59 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 12 May 2023 11:38:25 +0200 Subject: [PATCH 582/657] test: add in a regression test for #7414 While the original reported issue does now compile, the pickling tests fail with: ```diff - .fun:((q: DepTest.Dep[DepTest.obj.type]#t.Dependent): scala.Unit)>@tests/pos/i7414.scala<223..235> + .fun:((q: DepTest.obj.Dependent): scala.Unit)>@tests/pos/i7414.scala<223..235> ``` This adds in a test for this, but adds it to the excludes. I'm assuming this was what was meant by this comment: https://github.com/lampepfl/dotty/issues/7414#issuecomment-1545331428. Refs: #7414 --- compiler/test/dotc/pos-test-pickling.blacklist | 1 + tests/pos/i7414.scala | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 tests/pos/i7414.scala diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index d1dd83f36ff7..697ec4e2ec81 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -21,6 +21,7 @@ i15181.scala i15922.scala t5031_2.scala i16997.scala +i7414.scala # Tree is huge and blows stack for printing Text i7034.scala diff --git a/tests/pos/i7414.scala b/tests/pos/i7414.scala new file mode 100644 index 000000000000..fd85ed2a2265 --- /dev/null +++ b/tests/pos/i7414.scala @@ -0,0 +1,12 @@ +// https://github.com/lampepfl/dotty/issues/7414 + +object DepTest { + trait Trait { + case class Dependent() + } + object obj extends Trait + case class Dep[T <: Trait](t: T) { + def fun(q: t.Dependent): Unit = ??? + } + Dep(obj).fun(obj.Dependent()) +} From 720bff8dc45bcbdd58fa55e2fcc54fbf2c10f589 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Sun, 14 May 2023 11:55:55 +0200 Subject: [PATCH 583/657] test: add in a regression test for #11118 This adds in a regression test showing that just like in Scala 2 a user sees a warning about how the right hand side doesn't match the type of the left. closes #11118 --- tests/neg/i11118.check | 12 ++++++++++++ tests/neg/i11118.scala | 2 ++ 2 files changed, 14 insertions(+) create mode 100644 tests/neg/i11118.check create mode 100644 tests/neg/i11118.scala diff --git a/tests/neg/i11118.check b/tests/neg/i11118.check new file mode 100644 index 000000000000..0af98c7f580a --- /dev/null +++ b/tests/neg/i11118.check @@ -0,0 +1,12 @@ +-- Warning: tests/neg/i11118.scala:2:12 -------------------------------------------------------------------------------- +2 |val (a,b) = (1,2,3) // error // warning + | ^^^^^^^ + | pattern's type (Any, Any) does not match the right hand side expression's type (Int, Int, Int) + | + | If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression, + | which may result in a MatchError at runtime. + | This patch can be rewritten automatically under -rewrite -source 3.2-migration. +-- Error: tests/neg/i11118.scala:2:4 ----------------------------------------------------------------------------------- +2 |val (a,b) = (1,2,3) // error // warning + | ^ + | this case is unreachable since type (Int, Int, Int) is not a subclass of class Tuple2 diff --git a/tests/neg/i11118.scala b/tests/neg/i11118.scala new file mode 100644 index 000000000000..23d9b2b604b6 --- /dev/null +++ b/tests/neg/i11118.scala @@ -0,0 +1,2 @@ +// https://github.com/lampepfl/dotty/issues/11118 +val (a,b) = (1,2,3) // error // warning From bced8c8ec99fbec2f5e63b276efc2dac733afd65 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Sun, 14 May 2023 12:20:06 +0200 Subject: [PATCH 584/657] test: add in a regression test for #11223 [skip community_build] closes #11223 --- tests/pos/i11223.scala | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/pos/i11223.scala diff --git a/tests/pos/i11223.scala b/tests/pos/i11223.scala new file mode 100644 index 000000000000..a3583ca955c4 --- /dev/null +++ b/tests/pos/i11223.scala @@ -0,0 +1,21 @@ +object T: + trait M[F[_]] + trait EE[A, B] + trait AE[A] + trait DE[A] + trait CE[A] + + type A1 = M[AE] + type D1 = M[DE] | M[[a] =>> EE[Int, a]] + type C1 = M[CE] + + trait F[+R, +A]: + def <+>[U, B](b: F[U, B]): F[R | U, A] = null + def int: F[R | A1, Int] + + def d1[A](f: => A): F[D1, A] = null + def m[R, A](f: F[R | C1, A]): F[R | C1, A] = null + + def x = m { // adding type annotation here helps (m[D1 | A1 | C1, Int]) + d1(123).int <+> null + } From e38316ea2cb02da896e7043fdfa465888a77b2fd Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Sun, 14 May 2023 12:27:58 +0200 Subject: [PATCH 585/657] test: add in a regression test for #11255 [skip community_build] closes #11255 --- tests/pos/i11255.scala | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/pos/i11255.scala diff --git a/tests/pos/i11255.scala b/tests/pos/i11255.scala new file mode 100644 index 000000000000..6dd1e100e696 --- /dev/null +++ b/tests/pos/i11255.scala @@ -0,0 +1,26 @@ +class A +class B extends A + +object O: + opaque type Id[T] = T + extension [T](id: Id[T]) def get: T = id + def f[S <: A, T](ff: S => T): Id[S => T] = ??? + def g[S <: A, T](ff: S => T): Option[S => T] = ??? + def h[S, T](ff: S => T): Id[S => T] = ??? + +object example: + import O._ + + val a = new A + val b = new B + + val f1 = f((a: A) => 0) + f1.get.apply(a) + val f2 = f((b: B) => 0) + f2.get.apply(b) + + val g1 = g((a: A) => 0) + g1.get.apply(a) + + val h1 = h((a: A) => 0) + h1.get.apply(a) From 6086e5708106cba614172aa901851ed5e14a423d Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Sun, 14 May 2023 12:53:14 +0200 Subject: [PATCH 586/657] test: add in a regression test for #11681 This adds in both the original given example and a further minimized one. [skip community_build] closes #11681 --- tests/pos/i11681.scala | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/pos/i11681.scala diff --git a/tests/pos/i11681.scala b/tests/pos/i11681.scala new file mode 100644 index 000000000000..587285911610 --- /dev/null +++ b/tests/pos/i11681.scala @@ -0,0 +1,28 @@ +// https://github.com/lampepfl/dotty/issues/11681 + +import scala.collection.Factory + +final case class Gen[+A]() { + def take[C[X] <: Iterable[X], B]( + n: Int + )(implicit w: A <:< C[B], f: Factory[B, C[B]]): Gen[C[B]] = + Gen() +} + +object Usage { + def expected: Gen[List[Int]] = + Gen[List[Int]]().take(3) +} + +object example: + type G[A] + given G[H[Int]] = ??? + + trait H[X] + object H { + given H[Int] = ??? + } + + def take[C[_]](using w: G[C[Int]], f: C[Int]) = ??? + + def test = take From 05d95dde9ac448928e3b6973e21a0f7748c1016f Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Sun, 14 May 2023 13:00:48 +0200 Subject: [PATCH 587/657] test: add in a regression test for #11706 [skip community_build] closes #11706 --- tests/run/i11706.check | 2 ++ tests/run/i11706.scala | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/run/i11706.check create mode 100644 tests/run/i11706.scala diff --git a/tests/run/i11706.check b/tests/run/i11706.check new file mode 100644 index 000000000000..a5c8806279fa --- /dev/null +++ b/tests/run/i11706.check @@ -0,0 +1,2 @@ +3 +3 diff --git a/tests/run/i11706.scala b/tests/run/i11706.scala new file mode 100644 index 000000000000..2ba291f74a46 --- /dev/null +++ b/tests/run/i11706.scala @@ -0,0 +1,30 @@ +// https://github.com/lampepfl/dotty/issues/11706 +import scala.compiletime.erasedValue + +object Obj: + + inline def length[Tuple]: Int = loop[Tuple] + + private inline def loop[Tuple]: Int = + inline erasedValue[Tuple] match + case _: EmptyTuple => 0 + case _: (head *: tail) => 1 + loop[tail] + +end Obj + +// Same code, but in a trait instead of an object +trait Trait: + + inline def length[Tuple]: Int = loop[Tuple] + + private inline final def loop[Tuple]: Int = + inline erasedValue[Tuple] match + case _: EmptyTuple => 0 + case _: (head *: tail) => 1 + loop[tail] + +end Trait + +@main def Test() = + println(Obj.length[(Int, Int, String)]) // OK + new Trait: + println(length[(Int, Int, String)]) // ERROR From 7959821ab55523b14e71178087d10acf3dd54c55 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Sun, 14 May 2023 13:24:08 +0200 Subject: [PATCH 588/657] test: add in a regression test for #12032 [skip community_build] closes #12032 --- tests/run/i12032.check | 2 ++ tests/run/i12032.scala | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/run/i12032.check create mode 100644 tests/run/i12032.scala diff --git a/tests/run/i12032.check b/tests/run/i12032.check new file mode 100644 index 000000000000..3bd1f0e29744 --- /dev/null +++ b/tests/run/i12032.check @@ -0,0 +1,2 @@ +foo +bar diff --git a/tests/run/i12032.scala b/tests/run/i12032.scala new file mode 100644 index 000000000000..52358332e2c8 --- /dev/null +++ b/tests/run/i12032.scala @@ -0,0 +1,24 @@ +// https://github.com/lampepfl/dotty/issues/12032 +class Foo(val strings: Seq[String]) extends FooLowPriority + +trait FooLowPriority { self: Foo => + @scala.annotation.targetName("appendFromProducers") + def append(v: String): Foo = new Foo(v +: self.strings) +} + +trait FooBar { self: Foo => + @scala.annotation.targetName("appendFromProducers") + final override def append(v: String): Foo = new Foo(self.strings :+ v) +} + +object Foo { + type Bar = Foo with FooBar + + def bar(vs: String*): Bar = new Foo(vs) with FooBar +} + +@main def Test() = + Foo.bar("foo") + .append("bar") + .strings + .foreach(println) From b4fb6873e7f89cfe29b1e23c6a3f6f98405b570a Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Sun, 14 May 2023 14:29:59 +0200 Subject: [PATCH 589/657] test: add in a regression test for #12663 This adds in all 3 examples from the issue reported. [skip community_build] closes #12663 --- tests/pos/i12663.scala | 72 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/pos/i12663.scala diff --git a/tests/pos/i12663.scala b/tests/pos/i12663.scala new file mode 100644 index 000000000000..befbc65316cb --- /dev/null +++ b/tests/pos/i12663.scala @@ -0,0 +1,72 @@ +// https://github.com/lampepfl/dotty/issues/12663 + +final class HookComponentBuilder[Ctx, CtxFn[_]] { + def asd[A](f: Ctx => A): A = ??? + def asd[A](f: CtxFn[A]): A = ??? +} + +object HookCtx { + case class P1[P, H1](props: P, hook1: H1) +} + +object HookCtxFn { + sealed trait P1[P, H1] { type Fn[A] = (P, H1) => A } +} + +object Test { + val b: HookComponentBuilder[ + HookCtx.P1[String, Int], + HookCtxFn.P1[String, Int]#Fn + ] = ??? + + b.asd($ => $.props.length + $.hook1) + b.asd((props, hook1) => props.length + hook1) +} + +final class HookComponentBuilder2[Ctx, CtxFn[_]] { + def asd[A](f: Ctx => A): A = ??? + def asd[A](f: CtxFn[A]): A = ??? +} + +object HookCtx2 { + case class P1[P, H1](props: P, hook1: H1) +} + +object HookCtxFn2 { + type P1[P, H1] = [A] =>> (P, H1) => A +} + +object Test2 { + val b: HookComponentBuilder2[ + HookCtx2.P1[String, Int], + HookCtxFn2.P1[String, Int] + ] = ??? + + b.asd($ => $.props.length + $.hook1) + b.asd((props, hook1) => props.length + hook1) +} + +final class Builder[CtxFn[_]] { + def asd[A](f: Int => A): A = ??? + def asd[A](f: CtxFn[A]): A = ??? +} + +object Test3 { + val b1: Builder[[Z] =>> (String, Int) => Z] = ??? + b1.asd(identity) + b1.asd(_.length + _) + + sealed trait Scala2TL { type F[Z] = (String, Int) => Z } + val b2: Builder[Scala2TL#F] = b1 + b2.asd(identity) + b2.asd(_.length + _) + + type Scala3TL = [Z] =>> (String, Int) => Z + val b3: Builder[Scala3TL] = b1 + b3.asd(identity) + b3.asd(_.length + _) + + val b4: Builder[({ type F[Z] = (String, Int) => Z })#F] = b1 + b4.asd(identity) + b4.asd(_.length + _) +} From 9292bcc5afad502a316ed0f4020cd6ff83c52f11 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Sun, 14 May 2023 14:36:06 +0200 Subject: [PATCH 590/657] test: add in a regression test for #12679 [skip community_build] closes #12679 --- tests/pos/i12679.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/pos/i12679.scala diff --git a/tests/pos/i12679.scala b/tests/pos/i12679.scala new file mode 100644 index 000000000000..fed62c72dd42 --- /dev/null +++ b/tests/pos/i12679.scala @@ -0,0 +1,9 @@ +// https://github.com/lampepfl/dotty/issues/12679 + +object Example: + def foo[F[_]](qux: String, quux: String = ""): F[Unit] = ??? + + def foo[F[_]](qux: Boolean): F[Unit] = ??? + + def example[F[_]](maybeQux: Option[String], bool: Boolean) = + maybeQux.fold(foo[F](bool))(foo[F](_)) From 90549a702e10fa74c11019e8770391c3aa8584fd Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Sun, 14 May 2023 18:08:14 +0200 Subject: [PATCH 591/657] test: add in a regression test for #13216 [skip community_build] closes #13216 --- tests/run/i13216.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/run/i13216.scala diff --git a/tests/run/i13216.scala b/tests/run/i13216.scala new file mode 100644 index 000000000000..174d0f200f31 --- /dev/null +++ b/tests/run/i13216.scala @@ -0,0 +1,11 @@ +// https://github.com/lampepfl/dotty/issues/13216 +import scala.annotation.targetName + +class C(s: String) extends AnyVal { + def m(xs: Seq[Int]): Unit = {} + @targetName("m_seq2") + def m(xs: Seq[Seq[Int]]): Unit = {} +} + +@main def Test = + new C("").m(Seq(123)) From e657ac837510f5d9a8993a3c3a7f0afde4dfe9bf Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 15 May 2023 08:55:04 +0200 Subject: [PATCH 592/657] Update tests/run/i11706.scala --- tests/run/i11706.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run/i11706.scala b/tests/run/i11706.scala index 2ba291f74a46..276ee408d266 100644 --- a/tests/run/i11706.scala +++ b/tests/run/i11706.scala @@ -27,4 +27,4 @@ end Trait @main def Test() = println(Obj.length[(Int, Int, String)]) // OK new Trait: - println(length[(Int, Int, String)]) // ERROR + println(length[(Int, Int, String)]) From 55678d6e948ad3bb94a25333a467e8927e2252af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Mon, 15 May 2023 09:57:52 +0200 Subject: [PATCH 593/657] Set reference version to 3.3.0-RC6 --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 30c3aded5be4..30a731511f43 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -80,7 +80,7 @@ object DottyJSPlugin extends AutoPlugin { object Build { import ScaladocConfigs._ - val referenceVersion = "3.3.0-RC5" + val referenceVersion = "3.3.0-RC6" val baseVersion = "3.3.1-RC1" From cfa9da313ecc1ce48ad451dc759ba0b2bbb40410 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Mon, 15 May 2023 10:36:08 +0200 Subject: [PATCH 594/657] test: add in a regression test for #13334 [skip community_build] closes #13334 --- tests/run/i13334.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/run/i13334.scala diff --git a/tests/run/i13334.scala b/tests/run/i13334.scala new file mode 100644 index 000000000000..2ee0987c13cc --- /dev/null +++ b/tests/run/i13334.scala @@ -0,0 +1,16 @@ +// https://github.com/lampepfl/dotty/issues/13334 +trait DFC +given DFC = new DFC {} + +trait TC +object TC: + def foo()(using DFC): Unit = {} + + inline given (using DFC): TC = new TC: + foo() + +class Foo(using DFC): + summon[TC] + +@main def Test() = + val top = new Foo From 6b91b5f1ea55512cc08c4c2dc2975a87379e7ef5 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Mon, 15 May 2023 13:12:02 +0200 Subject: [PATCH 595/657] test: add in a regression test for #13691 Looks like there was already another test linked to this, but this adds in the original example to fully close the issue. [skip community_build] close #13691 --- tests/run/i13691b.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/run/i13691b.scala diff --git a/tests/run/i13691b.scala b/tests/run/i13691b.scala new file mode 100644 index 000000000000..1da726827467 --- /dev/null +++ b/tests/run/i13691b.scala @@ -0,0 +1,12 @@ +// https://github.com/lampepfl/dotty/issues/13691 +import language.experimental.saferExceptions + +trait Decoder[+T]: + def apply(): T + +given Decoder[Int throws Exception] = new Decoder[Int throws Exception]: + def apply(): Int throws Exception = 1 + +@main def Test(): Unit = + import unsafeExceptions.canThrowAny + summon[Decoder[Int throws Exception]]() From 2ac9525b658b9aa7f2e4876b85713665a597fec9 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Mon, 15 May 2023 13:50:46 +0200 Subject: [PATCH 596/657] test: add in a regression test for #14096 [skip community_build] closes #14096 --- tests/pos/i14096.scala | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/pos/i14096.scala diff --git a/tests/pos/i14096.scala b/tests/pos/i14096.scala new file mode 100644 index 000000000000..59365231b121 --- /dev/null +++ b/tests/pos/i14096.scala @@ -0,0 +1,7 @@ +// https://github.com/lampepfl/dotty/issues/14096 +object Test: + object Forte: + def test[T](i: Int, config: String = ""): Int = 1 + def test[T](i: String): Int = 2 + + Forte.test[Int](1) From 00d4e98a6e9cac3f0eaa4eb970de9377a63cacef Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Mon, 15 May 2023 14:05:39 +0200 Subject: [PATCH 597/657] test: add in a regression test for #14271 [skip community_build] closes #14271 --- tests/pos/i14271.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/pos/i14271.scala diff --git a/tests/pos/i14271.scala b/tests/pos/i14271.scala new file mode 100644 index 000000000000..8f46940afd09 --- /dev/null +++ b/tests/pos/i14271.scala @@ -0,0 +1,14 @@ +// https://github.com/lampepfl/dotty/issues/14271 +class Bound[T] +class MyClass[T <: Bound[T]] + +class Container[V] { + def doSth(): V = ??? +} + +def bug() = { + val m = new Container[MyClass[_]] + if (true) { + m.doSth() + } +} From e0d1f37583654d66ce6fc6bef9e0744fb89ea1b1 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Mon, 15 May 2023 14:10:08 +0200 Subject: [PATCH 598/657] test: add in a regression test for #14278 [skip community_build] closes #14278 --- tests/pos/i14278.scala | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/pos/i14278.scala diff --git a/tests/pos/i14278.scala b/tests/pos/i14278.scala new file mode 100644 index 000000000000..ebc9376fbad5 --- /dev/null +++ b/tests/pos/i14278.scala @@ -0,0 +1,6 @@ +// https://github.com/lampepfl/dotty/issues/14278 +class Foo + +extension (foo: Foo) + def patch(arg: List[Int], arg2: Int = 0): Unit = {} + def patch(arg: Int): Unit = patch(List(arg)) From 3270d8e85b4e1b1b23a1db7eac618d0c36f41af2 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Mon, 15 May 2023 14:29:29 +0200 Subject: [PATCH 599/657] test: add in a regression test for #14582 [skip community_build] closes #14582 --- tests/run/i14582.scala | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/run/i14582.scala diff --git a/tests/run/i14582.scala b/tests/run/i14582.scala new file mode 100644 index 000000000000..bce33aa170b2 --- /dev/null +++ b/tests/run/i14582.scala @@ -0,0 +1,18 @@ +// https://github.com/lampepfl/dotty/issues/14582 +@main def Test() = + val map = Map( + "a" -> 1, + "b" -> 2 + ) + + val mapView = map.view + + val optionMapView = Some(mapView) + + val listOfTuples: List[(String, String)] = List(("c", "d"), ("e", "f")) + + val mapViewWithDefault = optionMapView.getOrElse(Map()) + + val result = mapViewWithDefault ++ listOfTuples + + result.toSeq From 33e71b3943f53f1c24fe889eb8411503e139379a Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Mon, 15 May 2023 14:49:21 +0200 Subject: [PATCH 600/657] test: add in a regression test for #14642 [skip community_build] closes #14642 --- tests/pos/i14642.scala | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/pos/i14642.scala diff --git a/tests/pos/i14642.scala b/tests/pos/i14642.scala new file mode 100644 index 000000000000..b69da7d8d6d7 --- /dev/null +++ b/tests/pos/i14642.scala @@ -0,0 +1,16 @@ +// https://github.com/lampepfl/dotty/issues/14642 +case object A +case class B() +case class C() +type Union = A.type | B | C +val a: List[A.type] = ??? +val b: List[B] = ??? +val c: List[C] = ??? +val l1: List[Union] = a ++ b +val l2: List[Union] = + a ++ b ++ c +val l3: List[Union] = + (a: List[ + Union + ]) ++ b ++ c +val l4: List[Union] = (a: List[Union]) ++ (b ++ c) From 8eb7041fb4cd76a4b8e5ad1f67be4f556611af06 Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Mon, 15 May 2023 15:55:19 +0200 Subject: [PATCH 601/657] Update MAINTENANCE.md --- MAINTENANCE.md | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/MAINTENANCE.md b/MAINTENANCE.md index d1309a6b404d..54e74f7cb7ca 100644 --- a/MAINTENANCE.md +++ b/MAINTENANCE.md @@ -61,28 +61,36 @@ At the end of their supervision period, the supervisor reports to the team durin The following is the list of all the principal areas of the compiler and the core team members who are responsible for their maintenance: +### Compiler - Parser: @odersky - Typer: @odersky, @smarter, (@dwijnand) - Erasure: @smarter, @odersky - Enums: @bishabosha +- Derivation & Mirrors: @bishabosha, (@dwijnand) - Export: @bishabosha, @odersky - Pattern Matching: @dwijnand, (@liufengyun), @sjrd - Inline: @nicolasstucki, @odersky -- Metaprogramming (Quotes, Reflect, Staging): @nicolasstucki, @aherlihy -- Match types: @OlivierBlanvillain, @dwijnand -- GADT: @abgruszecki, @dwijnand -- Scaladoc: @KacperFKorban, @BarkingBad, @pikinier20 -- Initialization checker: @olhotak, @liufengyun, @anatoliykmetyuk +- Metaprogramming (Quotes, Reflect, Staging): @nicolasstucki, @jchyb +- Match types: @sjrd, @dwijnand, @Decel +- GADT: @dwijnand, @Linyxus +- Initialization checker: @olhotak, @liufengyun - Safe nulls: @noti0na1, @olhotak +- Lazy vals: @szymon-rd, @sjrd - tailrec: @sjrd, @mbovel - JS backend: @sjrd -- forward compat (-scala-release): @prolativ, @Kordyjan, (@nicolasstucki) -- Benchmarks: @anatoliykmetyuk, @mbovel -- REPL: @dwijnand, @anatoliykmetyuk, @prolativ +- JVM backend: @sjrd +- Java-compat: @smarter + +### Tooling +- REPL: @dwijnand, @prolativ +- Scaladoc: @Florian3k +- SemanticDB: @tanishiking +- Coverage: @TheElectronWill +- Linting (especially unused warnings) / Reporting UX: @szymon-rd + +### Infrastructure - CI: @anatoliykmetyuk - Community Build: @anatoliykmetyuk +- Open Community Build: @WojciechMazur - Vulpix: @dwijnand, @prolativ -- JVM backend: @Kordyjan, (@sjrd) -- Derivation & Mirrors: @bishabosha, (@dwijnand) -- Linting (especially unused warnings) / Reporting UX: VirtusLab TBD? -- Java-compat: @Kordyjan +- Benchmarks: @mbovel From b00b5c61e254c8cd468363e7c672915e1064ac98 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Mon, 15 May 2023 17:19:59 +0200 Subject: [PATCH 602/657] test: add in regression test for #14830 [skip community_build] closes #14830 --- tests/pos/i14830.scala | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/pos/i14830.scala diff --git a/tests/pos/i14830.scala b/tests/pos/i14830.scala new file mode 100644 index 000000000000..592a47c1a53c --- /dev/null +++ b/tests/pos/i14830.scala @@ -0,0 +1,6 @@ + +// https://github.com/lampepfl/dotty/issues/14830 +val a: Comparable[String] = "Fred" +val b: { def length: Int } = "Fred" +val c: Comparable[String] & { def length: Int } = "Fred" +val d: Comparable[String] & { def length(): Int } = "Fred" From a0bdfe88f38178cc7412d416f285f7a7acc4eeed Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 2 May 2023 14:41:58 +0200 Subject: [PATCH 603/657] Add SplicePattern AST to parse and type quote pattern splices --- .../src/dotty/tools/dotc/ast/Desugar.scala | 6 +- compiler/src/dotty/tools/dotc/ast/Trees.scala | 25 ++++ compiler/src/dotty/tools/dotc/ast/untpd.scala | 1 + .../dotty/tools/dotc/parsing/Parsers.scala | 4 +- .../tools/dotc/printing/RefinedPrinter.scala | 6 + .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../tools/dotc/typer/QuotesAndSplices.scala | 130 ++++++++---------- .../src/dotty/tools/dotc/typer/Typer.scala | 1 + 8 files changed, 98 insertions(+), 77 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 8a080972fb31..f0580c29e762 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -338,9 +338,9 @@ object desugar { def quotedPattern(tree: untpd.Tree, expectedTpt: untpd.Tree)(using Context): untpd.Tree = { def adaptToExpectedTpt(tree: untpd.Tree): untpd.Tree = tree match { // Add the expected type as an ascription - case _: untpd.Splice => + case _: untpd.SplicePattern => untpd.Typed(tree, expectedTpt).withSpan(tree.span) - case Typed(expr: untpd.Splice, tpt) => + case Typed(expr: untpd.SplicePattern, tpt) => cpy.Typed(tree)(expr, untpd.makeAndType(tpt, expectedTpt).withSpan(tpt.span)) // Propagate down the expected type to the leafs of the expression @@ -1979,7 +1979,7 @@ object desugar { case Quote(body, _) => new UntypedTreeTraverser { def traverse(tree: untpd.Tree)(using Context): Unit = tree match { - case Splice(expr) => collect(expr) + case SplicePattern(body, _) => collect(body) case _ => traverseChildren(tree) } }.traverse(body) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index f3d7369d8d5d..54c15b9909fa 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -737,6 +737,22 @@ object Trees { type ThisTree[+T <: Untyped] = Splice[T] } + /** A tree representing a pattern splice `${ pattern }`, `$ident` or `$ident(args*)` in a quote pattern. + * + * Parser will only create `${ pattern }` and `$ident`, hence they will not have args. + * While typing, the `$ident(args*)` the args are identified and desugared into a `SplicePattern` + * containing them. + * + * SplicePattern are removed after typing the pattern and are not present in TASTy. + * + * @param body The tree that was spliced + * @param args The arguments of the splice (the HOAS arguments) + */ + case class SplicePattern[+T <: Untyped] private[ast] (body: Tree[T], args: List[Tree[T]])(implicit @constructorOnly src: SourceFile) + extends TermTree[T] { + type ThisTree[+T <: Untyped] = SplicePattern[T] + } + /** A type tree that represents an existing or inferred type */ case class TypeTree[+T <: Untyped]()(implicit @constructorOnly src: SourceFile) extends DenotingTree[T] with TypTree[T] { @@ -1147,6 +1163,7 @@ object Trees { type Inlined = Trees.Inlined[T] type Quote = Trees.Quote[T] type Splice = Trees.Splice[T] + type SplicePattern = Trees.SplicePattern[T] type TypeTree = Trees.TypeTree[T] type InferredTypeTree = Trees.InferredTypeTree[T] type SingletonTypeTree = Trees.SingletonTypeTree[T] @@ -1325,6 +1342,10 @@ object Trees { case tree: Splice if (expr eq tree.expr) => tree case _ => finalize(tree, untpd.Splice(expr)(sourceFile(tree))) } + def SplicePattern(tree: Tree)(body: Tree, args: List[Tree])(using Context): SplicePattern = tree match { + case tree: SplicePattern if (body eq tree.body) && (args eq tree.args) => tree + case _ => finalize(tree, untpd.SplicePattern(body, args)(sourceFile(tree))) + } def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree case _ => finalize(tree, untpd.SingletonTypeTree(ref)(sourceFile(tree))) @@ -1566,6 +1587,8 @@ object Trees { cpy.Quote(tree)(transform(body)(using quoteContext), transform(tags)) case tree @ Splice(expr) => cpy.Splice(tree)(transform(expr)(using spliceContext)) + case tree @ SplicePattern(body, args) => + cpy.SplicePattern(tree)(transform(body)(using spliceContext), transform(args)) case tree @ Hole(isTerm, idx, args, content) => cpy.Hole(tree)(isTerm, idx, transform(args), transform(content)) case _ => @@ -1711,6 +1734,8 @@ object Trees { this(this(x, body)(using quoteContext), tags) case Splice(expr) => this(x, expr)(using spliceContext) + case SplicePattern(body, args) => + this(this(x, body)(using spliceContext), args) case Hole(_, _, args, content) => this(this(x, args), content) case _ => diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 91299be537e8..e3488034fef8 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -399,6 +399,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion) def Quote(body: Tree, tags: List[Tree])(implicit src: SourceFile): Quote = new Quote(body, tags) def Splice(expr: Tree)(implicit src: SourceFile): Splice = new Splice(expr) + def SplicePattern(body: Tree, args: List[Tree])(implicit src: SourceFile): SplicePattern = new SplicePattern(body, args) def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree() def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree() def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 7ea113cb7bc7..7a29ac3f7a38 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1750,10 +1750,10 @@ object Parsers { def splice(isType: Boolean): Tree = val start = in.offset atSpan(in.offset) { + val inPattern = (staged & StageKind.QuotedPattern) != 0 val expr = if (in.name.length == 1) { in.nextToken() - val inPattern = (staged & StageKind.QuotedPattern) != 0 withinStaged(StageKind.Spliced)(if (inPattern) inBraces(pattern()) else stagedBlock()) } else atSpan(in.offset + 1) { @@ -1769,6 +1769,8 @@ object Parsers { else "To use a given Type[T] in a quote just write T directly" syntaxError(em"$msg\n\nHint: $hint", Span(start, in.lastOffset)) Ident(nme.ERROR.toTypeName) + else if inPattern then + SplicePattern(expr, Nil) else Splice(expr) } diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 0b539db283ed..51aaa0932e5e 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -735,6 +735,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case Splice(expr) => val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) keywordStr("$") ~ spliceTypeText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}") + case SplicePattern(pattern, args) => + val spliceTypeText = (keywordStr("[") ~ toTextGlobal(tree.typeOpt) ~ keywordStr("]")).provided(printDebug && tree.typeOpt.exists) + keywordStr("$") ~ spliceTypeText ~ { + if args.isEmpty then keywordStr("{") ~ inPattern(toText(pattern)) ~ keywordStr("}") + else toText(pattern.symbol.name) ~ "(" ~ toTextGlobal(args, ", ") ~ ")" + } case Hole(isTerm, idx, args, content) => val (prefix, postfix) = if isTerm then ("{{{", "}}}") else ("[[[", "]]]") val argsText = toTextGlobal(args, ", ") diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 79d6501ccb2d..fbed4b77d3fe 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -1097,7 +1097,7 @@ trait Applications extends Compatibility { } else { val app = tree.fun match - case _: untpd.Splice if ctx.mode.is(Mode.QuotedPattern) => typedAppliedSplice(tree, pt) + case _: untpd.SplicePattern => typedAppliedSplice(tree, pt) case _ => realApply app match { case Apply(fn @ Select(left, _), right :: Nil) if fn.hasType => diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index ea2b380dc0ed..070449e3ee96 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -17,6 +17,7 @@ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.inlines.PrepareInlineable import dotty.tools.dotc.staging.StagingLevel.* import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.dotc.typer.ErrorReporting.errorTree import dotty.tools.dotc.typer.Implicits._ import dotty.tools.dotc.typer.Inferencing._ import dotty.tools.dotc.util.Spans._ @@ -74,45 +75,55 @@ trait QuotesAndSplices { def typedSplice(tree: untpd.Splice, pt: Type)(using Context): Tree = { record("typedSplice") checkSpliceOutsideQuote(tree) + assert(!ctx.mode.is(Mode.QuotedPattern)) tree.expr match { case untpd.Quote(innerExpr, Nil) if innerExpr.isTerm => report.warning("Canceled quote directly inside a splice. ${ '{ XYZ } } is equivalent to XYZ.", tree.srcPos) return typed(innerExpr, pt) case _ => } - if (ctx.mode.is(Mode.QuotedPattern)) - if (isFullyDefined(pt, ForceDegree.flipBottom)) { - def spliceOwner(ctx: Context): Symbol = - if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner - val pat = typedPattern(tree.expr, defn.QuotedExprClass.typeRef.appliedTo(pt))( - using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(spliceOwner(ctx))) - val baseType = pat.tpe.baseType(defn.QuotedExprClass) - val argType = if baseType != NoType then baseType.argTypesHi.head else defn.NothingType - Splice(pat, argType).withSpan(tree.span) - } - else { - report.error(em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.expr.srcPos) - tree.withType(UnspecifiedErrorType) - } - else { - if (level == 0) { - // Mark the first inline method from the context as a macro - def markAsMacro(c: Context): Unit = - if (c.owner eq c.outer.owner) markAsMacro(c.outer) - else if (c.owner.isInlineMethod) c.owner.setFlag(Macro) - else if (!c.outer.owner.is(Package)) markAsMacro(c.outer) - else assert(ctx.reporter.hasErrors) // Did not find inline def to mark as macro - markAsMacro(ctx) - } - - // TODO typecheck directly (without `exprSplice`) - val internalSplice = - untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) - typedApply(internalSplice, pt)(using spliceContext).withSpan(tree.span) match - case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => - cpy.Splice(tree)(spliced) - case tree => tree + if (level == 0) { + // Mark the first inline method from the context as a macro + def markAsMacro(c: Context): Unit = + if (c.owner eq c.outer.owner) markAsMacro(c.outer) + else if (c.owner.isInlineMethod) c.owner.setFlag(Macro) + else if (!c.outer.owner.is(Package)) markAsMacro(c.outer) + else assert(ctx.reporter.hasErrors) // Did not find inline def to mark as macro + markAsMacro(ctx) } + + // TODO typecheck directly (without `exprSplice`) + val internalSplice = + untpd.Apply(untpd.ref(defn.QuotedRuntime_exprSplice.termRef), tree.expr) + typedApply(internalSplice, pt)(using spliceContext).withSpan(tree.span) match + case tree @ Apply(TypeApply(_, tpt :: Nil), spliced :: Nil) if tree.symbol == defn.QuotedRuntime_exprSplice => + cpy.Splice(tree)(spliced) + case tree => tree + } + + def typedSplicePattern(tree: untpd.SplicePattern, pt: Type)(using Context): Tree = { + record("typedSplicePattern") + if isFullyDefined(pt, ForceDegree.flipBottom) then + def patternOuterContext(ctx: Context): Context = + if (ctx.mode.is(Mode.QuotedPattern)) patternOuterContext(ctx.outer) else ctx + val typedArgs = tree.args.map { + case arg: untpd.Ident => + typedExpr(arg) + case arg => + report.error("Open pattern expected an identifier", arg.srcPos) + EmptyTree + } + for arg <- typedArgs if arg.symbol.is(Mutable) do // TODO support these patterns. Possibly using scala.quoted.util.Var + report.error("References to `var`s cannot be used in higher-order pattern", arg.srcPos) + val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) + val patType = if tree.args.isEmpty then pt else defn.FunctionOf(argTypes, pt) + val pat = typedPattern(tree.body, defn.QuotedExprClass.typeRef.appliedTo(patType))( + using spliceContext.retractMode(Mode.QuotedPattern).addMode(Mode.Pattern).withOwner(patternOuterContext(ctx).owner)) + val baseType = pat.tpe.baseType(defn.QuotedExprClass) + val argType = if baseType.exists then baseType.argTypesHi.head else defn.NothingType + untpd.cpy.SplicePattern(tree)(pat, typedArgs).withType(pt) + else + errorTree(tree, em"Type must be fully defined.\nConsider annotating the splice using a type ascription:\n ($tree: XYZ).", tree.body.srcPos) } def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = @@ -127,29 +138,17 @@ trait QuotesAndSplices { */ def typedAppliedSplice(tree: untpd.Apply, pt: Type)(using Context): Tree = { assert(ctx.mode.is(Mode.QuotedPattern)) - val untpd.Apply(splice: untpd.Splice, args) = tree: @unchecked - def isInBraces: Boolean = splice.span.end != splice.expr.span.end - if !isFullyDefined(pt, ForceDegree.flipBottom) then - report.error(em"Type must be fully defined.", splice.srcPos) - tree.withType(UnspecifiedErrorType) - else if isInBraces then // ${x}(...) match an application + val untpd.Apply(splice: untpd.SplicePattern, args) = tree: @unchecked + def isInBraces: Boolean = splice.span.end != splice.body.span.end + if isInBraces then // ${x}(...) match an application val typedArgs = args.map(arg => typedExpr(arg)) val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) - val splice1 = typedSplice(splice, defn.FunctionOf(argTypes, pt)) - Apply(splice1.select(nme.apply), typedArgs).withType(pt).withSpan(tree.span) + val splice1 = typedSplicePattern(splice, defn.FunctionOf(argTypes, pt)) + untpd.cpy.Apply(tree)(splice1.select(nme.apply), typedArgs).withType(pt) else // $x(...) higher-order quasipattern - val typedArgs = args.map { - case arg: untpd.Ident => - typedExpr(arg) - case arg => - report.error("Open pattern expected an identifier", arg.srcPos) - EmptyTree - } if args.isEmpty then - report.error("Missing arguments for open pattern", tree.srcPos) - val argTypes = typedArgs.map(_.tpe.widenTermRefExpr) - val typedPat = typedSplice(splice, defn.FunctionOf(argTypes, pt)) - ref(defn.QuotedRuntimePatterns_patternHigherOrderHole).appliedToType(pt).appliedTo(typedPat, SeqLiteral(typedArgs, TypeTree(defn.AnyType))) + report.error("Missing arguments for open pattern", tree.srcPos) + typedSplicePattern(untpd.cpy.SplicePattern(tree)(splice.body, args), pt) } /** Type a pattern variable name `t` in quote pattern as `${given t$giveni: Type[t @ _]}`. @@ -227,29 +226,16 @@ trait QuotesAndSplices { val freshTypeBindingsBuff = new mutable.ListBuffer[Tree] val typePatBuf = new mutable.ListBuffer[Tree] override def transform(tree: Tree)(using Context) = tree match { - case Typed(splice: Splice, tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => + case Typed(splice @ SplicePattern(pat, Nil), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => transform(tpt) // Collect type bindings transform(splice) - case Apply(TypeApply(fn, targs), Splice(pat) :: args :: Nil) if fn.symbol == defn.QuotedRuntimePatterns_patternHigherOrderHole => - args match // TODO support these patterns. Possibly using scala.quoted.util.Var - case SeqLiteral(args, _) => - for arg <- args; if arg.symbol.is(Mutable) do - report.error("References to `var`s cannot be used in higher-order pattern", arg.srcPos) - try ref(defn.QuotedRuntimePatterns_higherOrderHole.termRef).appliedToTypeTrees(targs).appliedTo(args).withSpan(tree.span) - finally { - val patType = pat.tpe.widen - val patType1 = patType.translateFromRepeated(toArray = false) - val pat1 = if (patType eq patType1) pat else pat.withType(patType1) - patBuf += pat1 - } - case Splice(pat) => - try ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) - finally { - val patType = pat.tpe.widen - val patType1 = patType.translateFromRepeated(toArray = false) - val pat1 = if (patType eq patType1) pat else pat.withType(patType1) - patBuf += pat1 - } + case SplicePattern(pat, args) => + val patType = pat.tpe.widen + val patType1 = patType.translateFromRepeated(toArray = false) + val pat1 = if (patType eq patType1) pat else pat.withType(patType1) + patBuf += pat1 + if args.isEmpty then ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) + else ref(defn.QuotedRuntimePatterns_higherOrderHole.termRef).appliedToType(tree.tpe).appliedTo(SeqLiteral(args, TypeTree(defn.AnyType))).withSpan(tree.span) case Select(pat: Bind, _) if tree.symbol.isTypeSplice => val sym = tree.tpe.dealias.typeSymbol if sym.exists then diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cfc9e968ef41..9a9e194c9e0b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3096,6 +3096,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case untpd.EmptyTree => tpd.EmptyTree case tree: untpd.Quote => typedQuote(tree, pt) case tree: untpd.Splice => typedSplice(tree, pt) + case tree: untpd.SplicePattern => typedSplicePattern(tree, pt) case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here case tree: untpd.Hole => typedHole(tree, pt) case _ => typedUnadapted(desugar(tree, pt), pt, locked) From b7cc75301208f306f82db79802a81de262680f43 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 16 May 2023 10:02:12 +0200 Subject: [PATCH 604/657] Fix docs --- compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 19443dfb42e3..15a1a823589c 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -59,9 +59,9 @@ import scala.annotation.constructorOnly * ]], * typeHole = Seq(a, b), * termHole = (idx: Int, args: List[Any], quotes: Quotes) => idx match { - * case 3 => content0.apply(args(0).asInstanceOf[Expr[U1]]).apply(quotes) // beta reduced - * case 4 => content1.apply(args(0).asInstanceOf[Expr[U2]]).apply(quotes) // beta reduced - * case 5 => content2.apply(args(0).asInstanceOf[Expr[U1]], args(1).asInstanceOf[Expr[U2]]).apply(quotes) // beta reduced + * case 3 => holeContents0.apply(args(0).asInstanceOf[Expr[U1]]).apply(quotes) // beta reduced + * case 4 => holeContents1.apply(args(0).asInstanceOf[Expr[U2]]).apply(quotes) // beta reduced + * case 5 => holeContents2.apply(args(0).asInstanceOf[Expr[U1]], args(1).asInstanceOf[Expr[U2]]).apply(quotes) // beta reduced * }, * ) * ``` From dc7a2239be0c3b6bd2e520944922cbaad6b180ff Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 16 May 2023 12:09:47 +0200 Subject: [PATCH 605/657] Remove unnecessary `tasty-inspector` dependency --- project/Build.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 30a731511f43..dbf574252113 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -926,7 +926,6 @@ object Build { lazy val `stdlib-bootstrapped` = project.in(file("stdlib-bootstrapped")). withCommonSettings(Bootstrapped). dependsOn(dottyCompiler(Bootstrapped) % "provided; compile->runtime; test->test"). - dependsOn(`scala3-tasty-inspector` % "test->test"). settings(commonBootstrappedSettings). settings( moduleName := "scala-library", From 98ad63eb3f5b5093a561e40c8f4a0c4c8d97697d Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Wed, 17 May 2023 00:48:14 +0200 Subject: [PATCH 606/657] Account for capture dependencies when substituting --- compiler/src/dotty/tools/dotc/core/Types.scala | 3 ++- tests/pos-custom-args/captures/cc-dep-param.scala | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/pos-custom-args/captures/cc-dep-param.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 2e3ab1a5ded8..73a64f2c1b8f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3880,7 +3880,8 @@ object Types { /** Does one of the parameter types contain references to earlier parameters * of this method type which cannot be eliminated by de-aliasing? */ - def isParamDependent(using Context): Boolean = paramDependencyStatus == TrueDeps + def isParamDependent(using Context): Boolean = + paramDependencyStatus == TrueDeps || paramDependencyStatus == CaptureDeps /** Is there a dependency involving a reference in a capture set, but * otherwise no true result dependency? diff --git a/tests/pos-custom-args/captures/cc-dep-param.scala b/tests/pos-custom-args/captures/cc-dep-param.scala new file mode 100644 index 000000000000..1440cd4d7d40 --- /dev/null +++ b/tests/pos-custom-args/captures/cc-dep-param.scala @@ -0,0 +1,8 @@ +import language.experimental.captureChecking + +trait Foo[T] +def test(): Unit = + val a: Foo[Int]^ = ??? + val useA: () ->{a} Unit = ??? + def foo[X](x: Foo[X]^, op: () ->{x} Unit): Unit = ??? + foo(a, useA) From 6870649ca53597aaac404be2951ef475c053f502 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Wed, 17 May 2023 00:48:52 +0200 Subject: [PATCH 607/657] Fix capture set healing Capture set healing happens at the end of capture checking (in the `postCheck` function), which checks the capture set in each type argument and widen the ill-formed `TermParamRef`s by widening them. However, it is possible that a capture set is healed first, and then get a `TermParamRef` propagated into it later when we are healing another capture set. This lead to unsoundness. This commit installs an handler on capture sets when healing them and will inspect the newly added elements and heal these new elements as well. --- .../src/dotty/tools/dotc/cc/CaptureSet.scala | 15 +++++++++++- .../dotty/tools/dotc/cc/CheckCaptures.scala | 7 ++++-- .../captures/usingLogFile-alt.check | 7 ++++++ .../captures/usingLogFile-alt.scala | 23 +++++++++++++++++++ .../captures/usingLogFile.check | 7 ------ .../captures/usingLogFile.scala | 2 +- 6 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 tests/neg-custom-args/captures/usingLogFile-alt.check create mode 100644 tests/neg-custom-args/captures/usingLogFile-alt.scala diff --git a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala index ce903acab411..fdc4f66beafa 100644 --- a/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala +++ b/compiler/src/dotty/tools/dotc/cc/CaptureSet.scala @@ -275,6 +275,11 @@ sealed abstract class CaptureSet extends Showable: if isUniversal then handler() this + /** Invoke handler on the elements to check wellformedness of the capture set */ + def ensureWellformed(handler: List[CaptureRef] => Context ?=> Unit)(using Context): this.type = + handler(elems.toList) + this + /** An upper approximation of this capture set, i.e. a constant set that is * subcaptured by this set. If the current set is a variable * it is the intersection of all upper approximations of known supersets @@ -375,6 +380,9 @@ object CaptureSet: /** A handler to be invoked if the root reference `cap` is added to this set */ var rootAddedHandler: () => Context ?=> Unit = () => () + /** A handler to be invoked when new elems are added to this set */ + var newElemAddedHandler: List[CaptureRef] => Context ?=> Unit = _ => () + var description: String = "" /** Record current elements in given VarState provided it does not yet @@ -405,7 +413,8 @@ object CaptureSet: if !isConst && recordElemsState() then elems ++= newElems if isUniversal then rootAddedHandler() - // assert(id != 2 || elems.size != 2, this) + newElemAddedHandler(newElems.toList) + // assert(id != 5 || elems.size != 3, this) (CompareResult.OK /: deps) { (r, dep) => r.andAlso(dep.tryInclude(newElems, this)) } @@ -425,6 +434,10 @@ object CaptureSet: rootAddedHandler = handler super.disallowRootCapability(handler) + override def ensureWellformed(handler: List[CaptureRef] => (Context) ?=> Unit)(using Context): this.type = + newElemAddedHandler = handler + super.ensureWellformed(handler) + private var computingApprox = false /** Roughly: the intersection of all constant known supersets of this set. diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 6ec360eca938..3df31c305a91 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -976,8 +976,11 @@ class CheckCaptures extends Recheck, SymTransformer: recur(refs, Nil) private def healCaptureSet(cs: CaptureSet): Unit = - val toInclude = widenParamRefs(cs.elems.toList.filter(!isAllowed(_)).asInstanceOf) - toInclude.foreach(checkSubset(_, cs, tree.srcPos)) + def avoidance(elems: List[CaptureRef])(using Context): Unit = + val toInclude = widenParamRefs(elems.filter(!isAllowed(_)).asInstanceOf) + //println(i"HEAL $cs by widening to $toInclude") + toInclude.foreach(checkSubset(_, cs, tree.srcPos)) + cs.ensureWellformed(avoidance) private var allowed: SimpleIdentitySet[TermParamRef] = SimpleIdentitySet.empty diff --git a/tests/neg-custom-args/captures/usingLogFile-alt.check b/tests/neg-custom-args/captures/usingLogFile-alt.check new file mode 100644 index 000000000000..31e97b7dfda1 --- /dev/null +++ b/tests/neg-custom-args/captures/usingLogFile-alt.check @@ -0,0 +1,7 @@ +-- Error: tests/neg-custom-args/captures/usingLogFile-alt.scala:18:2 --------------------------------------------------- +18 | usingFile( // error + | ^^^^^^^^^ + | Sealed type variable T cannot be instantiated to box () => Unit since + | that type captures the root capability `cap`. + | This is often caused by a local capability in the body of method usingFile + | leaking as part of its result. diff --git a/tests/neg-custom-args/captures/usingLogFile-alt.scala b/tests/neg-custom-args/captures/usingLogFile-alt.scala new file mode 100644 index 000000000000..6b529ee6f892 --- /dev/null +++ b/tests/neg-custom-args/captures/usingLogFile-alt.scala @@ -0,0 +1,23 @@ +// Reported in issue #17517 + +import language.experimental.captureChecking +import java.io.* + +object Test: + class Logger(f: OutputStream^): + def log(msg: String): Unit = ??? + + def usingFile[sealed T](name: String, op: OutputStream^ => T): T = + val f = new FileOutputStream(name) + val result = op(f) + f.close() + result + + def usingLogger[sealed T](f: OutputStream^)(op: Logger^{f} => T): T = ??? + + usingFile( // error + "foo", + file => { + usingLogger(file)(l => () => l.log("test")) + } + ) diff --git a/tests/neg-custom-args/captures/usingLogFile.check b/tests/neg-custom-args/captures/usingLogFile.check index f9230e37a1cf..d3bc9082202c 100644 --- a/tests/neg-custom-args/captures/usingLogFile.check +++ b/tests/neg-custom-args/captures/usingLogFile.check @@ -45,10 +45,3 @@ | that type captures the root capability `cap`. | This is often caused by a local capability in the body of method usingFile | leaking as part of its result. --- Error: tests/neg-custom-args/captures/usingLogFile.scala:72:6 ------------------------------------------------------- -72 | usingLogger(_, l => () => l.log("test"))) // error - | ^^^^^^^^^^^ - | Sealed type variable T cannot be instantiated to box () => Unit since - | that type captures the root capability `cap`. - | This is often caused by a local capability in the body of method usingLogger - | leaking as part of its result. diff --git a/tests/neg-custom-args/captures/usingLogFile.scala b/tests/neg-custom-args/captures/usingLogFile.scala index 40c90b934464..e7c23573ca6e 100644 --- a/tests/neg-custom-args/captures/usingLogFile.scala +++ b/tests/neg-custom-args/captures/usingLogFile.scala @@ -69,5 +69,5 @@ object Test4: def test = val later = usingFile("logfile", // error - usingLogger(_, l => () => l.log("test"))) // error + usingLogger(_, l => () => l.log("test"))) // ok, since we can widen `l` to `file` instead of to `cap` later() From 44fed3dd529db6686f8c7caf8479a5bb915f2299 Mon Sep 17 00:00:00 2001 From: Matt Bovel Date: Wed, 17 May 2023 13:52:28 +0200 Subject: [PATCH 608/657] Handle empty files and truncated YAML front matter --- .../src/dotty/tools/scaladoc/site/common.scala | 11 ++++++++--- .../test-documentations/emptyPage/_docs/hello.md | 0 .../noConfigEnd/_docs/hello.md | 3 +++ .../scaladoc/site/SiteGeneratationTest.scala | 16 ++++++++++++++++ 4 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 scaladoc/test-documentations/emptyPage/_docs/hello.md create mode 100644 scaladoc/test-documentations/noConfigEnd/_docs/hello.md diff --git a/scaladoc/src/dotty/tools/scaladoc/site/common.scala b/scaladoc/src/dotty/tools/scaladoc/site/common.scala index c0c959cf205c..0811d217537f 100644 --- a/scaladoc/src/dotty/tools/scaladoc/site/common.scala +++ b/scaladoc/src/dotty/tools/scaladoc/site/common.scala @@ -63,11 +63,16 @@ def yamlParser(using ctx: StaticSiteContext): Parser = Parser.builder(defaultMar def loadTemplateFile(file: File, defaultTitle: Option[TemplateName] = None)(using ctx: StaticSiteContext): TemplateFile = { val lines = Files.readAllLines(file.toPath).asScala.toList - val (config, content) = if (lines.head == ConfigSeparator) { + val (config, content) = if (!lines.isEmpty && lines.head == ConfigSeparator) { // Taking the second occurrence of ConfigSeparator. // The rest may appear within the content. - val index = lines.drop(1).indexOf(ConfigSeparator) + 2 - (lines.take(index), lines.drop(index)) + val secondSeparatorIndex = lines.drop(1).indexOf(ConfigSeparator) + if secondSeparatorIndex != -1 then + (lines.take(secondSeparatorIndex + 2), lines.drop(secondSeparatorIndex + 2)) + else + // If there is no second occurrence of ConfigSeparator, we assume that the + // whole file is config. + (lines.tail, Nil) } else (Nil, lines) val configParsed = yamlParser.parse(config.mkString(LineSeparator)) diff --git a/scaladoc/test-documentations/emptyPage/_docs/hello.md b/scaladoc/test-documentations/emptyPage/_docs/hello.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/scaladoc/test-documentations/noConfigEnd/_docs/hello.md b/scaladoc/test-documentations/noConfigEnd/_docs/hello.md new file mode 100644 index 000000000000..3809c65bce02 --- /dev/null +++ b/scaladoc/test-documentations/noConfigEnd/_docs/hello.md @@ -0,0 +1,3 @@ +--- +title: My page +foo: bar diff --git a/scaladoc/test/dotty/tools/scaladoc/site/SiteGeneratationTest.scala b/scaladoc/test/dotty/tools/scaladoc/site/SiteGeneratationTest.scala index 7ce16933997a..e012044156cc 100644 --- a/scaladoc/test/dotty/tools/scaladoc/site/SiteGeneratationTest.scala +++ b/scaladoc/test/dotty/tools/scaladoc/site/SiteGeneratationTest.scala @@ -95,6 +95,22 @@ class SiteGeneratationTest extends BaseHtmlTest: testApiPages(mainTitle = projectName, parents = Nil, hasToplevelIndexIndex = false) } + @Test + def emptyPage() = withGeneratedSite(testDocPath.resolve("emptyPage")){ + withHtmlFile("docs/hello.html") { content => + // There should be no content as the page body is empty. + content.assertTextsIn("#content", Nil*) + } + } + + @Test + def noConfigEnd() = withGeneratedSite(testDocPath.resolve("noConfigEnd")){ + withHtmlFile("docs/hello.html") { content => + // There should be no content as the page body is empty. + content.assertTextsIn("#content", Nil*) + } + } + @Test def staticLinking() = withGeneratedSite(testDocPath.resolve("static-links")){ From df20af859cd4d18bc07459a1aee507c2bde39906 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 17 May 2023 16:08:32 +0200 Subject: [PATCH 609/657] Clarify the purpose of the website https://dotty.epfl.ch --- CONTRIBUTING.md | 2 +- docs/_blog/index.html | 2 +- docs/_docs/contributing/workflow.md | 1 + docs/_docs/index.md | 15 +-------------- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48206fd67b3c..90496bcd0c0a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,4 +2,4 @@ Firstly, thanks for being willing to contribute to Dotty! Head on over the [Scala 3 Contributing -Guide](https://docs.scala-lang.org/scala3/guides/contribution/contribution-intro.html), which should have all the info you're looking for. +Guide](https://dotty.epfl.ch/docs/contributing/index.html), which should have all the info you're looking for. diff --git a/docs/_blog/index.html b/docs/_blog/index.html index 055b069b303d..a59b8702d326 100644 --- a/docs/_blog/index.html +++ b/docs/_blog/index.html @@ -1,6 +1,6 @@ --- layout: static-site-main -title: Blog +title: Blog (archive) ---

      {{ page.title }}

      diff --git a/docs/_docs/contributing/workflow.md b/docs/_docs/contributing/workflow.md index 36d91bf02707..fb8108e7ceb0 100644 --- a/docs/_docs/contributing/workflow.md +++ b/docs/_docs/contributing/workflow.md @@ -115,6 +115,7 @@ The basics of working with Dotty codebase are documented [here](https://dotty.ep | `testOnly dotty.tools.dotc.CompilationTests -- *pos` | Run test (method) `pos` from `CompilationTests` suite. | | `testCompilation sample` | In all test suites, run test files containing the word `sample` in their title. | | `scala3-compiler/Test/runMain dotty.tools.printTypes`| Print types underlying representation | +| `scaladoc/generateScalaDocumentation` | Build the documentation website | ## Shell Commands diff --git a/docs/_docs/index.md b/docs/_docs/index.md index 97dc7fd5886b..e61313d81a4a 100644 --- a/docs/_docs/index.md +++ b/docs/_docs/index.md @@ -1,19 +1,6 @@ --- layout: index redirectFrom: /docs/index.html -nightlyOf: https://docs.scala-lang.org/scala3/reference/ --- -Dotty is the project name for technologies that are considered for inclusion in Scala 3. Scala has -pioneered the fusion of object-oriented and functional programming in a typed setting. Scala 3 will -be a big step towards realizing the full potential of these ideas. Its main objectives are to - -- become more opinionated by promoting programming idioms we found to work well, -- simplify where possible, -- eliminate inconsistencies and surprising behaviors, -- build on strong foundations to ensure the design hangs well together, -- consolidate language constructs to improve the language’s consistency, safety, ergonomics, and performance. - -In this documentation you will find information on how to use the Dotty compiler on your machine, -navigate through the code, setup Dotty with your favorite IDE and more! - +This website contains the developer documentation of the Scala 3 compiler. It targets developers interested in contributing to the compiler, or learning its internals. If you want to learn how to use Scala, go [here](https://docs.scala-lang.org/). From 813dd1e8a88d69dcca1ea504e34ec9f461362459 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Wed, 17 May 2023 16:20:49 +0200 Subject: [PATCH 610/657] Fix a bunch of links --- docs/_docs/contributing/architecture/phases.md | 2 +- docs/_docs/contributing/getting-started.md | 2 +- docs/_docs/contributing/index.md | 4 ++-- docs/_docs/reference/metaprogramming/macros-spec.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/_docs/contributing/architecture/phases.md b/docs/_docs/contributing/architecture/phases.md index 15690dfe4e4e..844ae144dddb 100644 --- a/docs/_docs/contributing/architecture/phases.md +++ b/docs/_docs/contributing/architecture/phases.md @@ -105,4 +105,4 @@ These map the transformed trees to Java classfiles or SJSIR files. [patternMatcher]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala [erasure]: https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/Erasure.scala [Mirror]: https://github.com/lampepfl/dotty/blob/master/library/src/scala/deriving/Mirror.scala -[PCP]: {{ site.scala3ref }}/metaprogramming/macros.html#the-phase-consistency-principle +[PCP]: ../../reference/metaprogramming/macros.md#the-phase-consistency-principle diff --git a/docs/_docs/contributing/getting-started.md b/docs/_docs/contributing/getting-started.md index 239e738bbfbb..af9f2f0783b8 100644 --- a/docs/_docs/contributing/getting-started.md +++ b/docs/_docs/contributing/getting-started.md @@ -134,7 +134,7 @@ The main development discussion channels are: [java8]: https://www.oracle.com/java/technologies/javase-jdk8-downloads.html [java11]: https://www.oracle.com/java/technologies/javase-jdk11-downloads.html [adopt]: https://adoptopenjdk.net/ -[compat]: /overviews/jdk-compatibility/overview.html +[compat]: https://docs.scala-lang.org/overviews/jdk-compatibility/overview.html [scala-cla]: https://www.lightbend.com/contribute/cla/scala [dotty-issue]: https://github.com/lampepfl/dotty/issues [dotty-discussion]: https://github.com/lampepfl/dotty/discussions diff --git a/docs/_docs/contributing/index.md b/docs/_docs/contributing/index.md index 27954aefd7a1..0cc87e4b3500 100644 --- a/docs/_docs/contributing/index.md +++ b/docs/_docs/contributing/index.md @@ -19,9 +19,9 @@ if you notice anything out of date, or report any issues ### Get the Most from This Guide `dotc` is built with Scala 3, fully utilising its [new -features](/scala3/new-in-scala3.html). It is recommended that you first have +features](https://docs.scala-lang.org/scala3/new-in-scala3.html). It is recommended that you first have some familiarity with Scala 3 to get the most out of this guide. You can learn -more in the [language reference]({{ site.scala3ref }}). +more in the [language reference](../reference/overview.md). Many code snippets in this guide make use of shell commands (a line beginning with `$`), and in this case a `bash` compatible shell is assumed. You may have diff --git a/docs/_docs/reference/metaprogramming/macros-spec.md b/docs/_docs/reference/metaprogramming/macros-spec.md index 35a1b4b3d43a..27a0a2c1bdcb 100644 --- a/docs/_docs/reference/metaprogramming/macros-spec.md +++ b/docs/_docs/reference/metaprogramming/macros-spec.md @@ -134,7 +134,7 @@ Quotes define all the `Expr[T]` methods as extension methods. `Type[T]` does not have methods and therefore does not appear here. These methods are available as long as `Quotes` is implicitly given in the current scope. -The `Quotes` instance is also the entry point to the [reflection API](./refelction.md) through the `reflect` object. +The `Quotes` instance is also the entry point to the [reflection API](./reflection.md) through the `reflect` object. Finally, `Quotes` provides the internal logic used in quote un-pickling (`QuoteUnpickler`) in quote pattern matching (`QuoteMatching`). These interfaces are added to the self-type of the trait to make sure they are implemented on this object but not visible to users of `Quotes`. From 113b08acb7ee1460519d6c4fab5d0e6a156a0810 Mon Sep 17 00:00:00 2001 From: Yichen Xu Date: Thu, 18 May 2023 02:19:57 +0200 Subject: [PATCH 611/657] Avoid using `Range`s in exact substitutions This improves the completeness of capture checking in the following case: ```scala def run(io: IO^)(op: Op^{io}): Unit = ??? val myIO: IO^ = ??? val myOp: Op^{myIO} = ??? run(myIO)(myOp) ``` The last line previously won't work because when substitute the parameter `io` to `myIO` in the type `(op: Op^{io}): Unit` the type `Op^{io}` get approximated into `Nothing` contravariantly. This is an overshot since the substitution [io := myIO] is exact (or precise) and the approximation (or avoidance) is unnecessary. This commit resolves this issue by checking whether the `to` only contains `CaptureRef` in `SubstParamsMap`. If this condition is met, then it is an exact substitution and we can apply it on the capture set without approximations. --- .../dotty/tools/dotc/cc/CheckCaptures.scala | 27 +++++++++------ .../captures/cc-subst-param-exact.scala | 33 +++++++++++++++++++ 2 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 tests/neg-custom-args/captures/cc-subst-param-exact.scala diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index 6ec360eca938..5ecd29119e3a 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -72,16 +72,23 @@ object CheckCaptures: */ final class SubstParamsMap(from: BindingType, to: List[Type])(using Context) extends ApproximatingTypeMap, IdempotentCaptRefMap: - def apply(tp: Type): Type = tp match - case tp: ParamRef => - if tp.binder == from then to(tp.paramNum) else tp - case tp: NamedType => - if tp.prefix `eq` NoPrefix then tp - else tp.derivedSelect(apply(tp.prefix)) - case _: ThisType => - tp - case _ => - mapOver(tp) + /** This SubstParamsMap is exact if `to` only contains `CaptureRef`s. */ + private val isExactSubstitution: Boolean = to.forall(_.isInstanceOf[CaptureRef]) + + /** As long as this substitution is exact, there is no need to create `Range`s when mapping invariant positions. */ + override protected def needsRangeIfInvariant(refs: CaptureSet): Boolean = !isExactSubstitution + + def apply(tp: Type): Type = + tp match + case tp: ParamRef => + if tp.binder == from then to(tp.paramNum) else tp + case tp: NamedType => + if tp.prefix `eq` NoPrefix then tp + else tp.derivedSelect(apply(tp.prefix)) + case _: ThisType => + tp + case _ => + mapOver(tp) /** Check that a @retains annotation only mentions references that can be tracked. * This check is performed at Typer. diff --git a/tests/neg-custom-args/captures/cc-subst-param-exact.scala b/tests/neg-custom-args/captures/cc-subst-param-exact.scala new file mode 100644 index 000000000000..35e4acb95fdc --- /dev/null +++ b/tests/neg-custom-args/captures/cc-subst-param-exact.scala @@ -0,0 +1,33 @@ +import language.experimental.captureChecking +import caps.* + +trait Ref[T] { def set(x: T): T } +def test() = { + + def swap[T](x: Ref[T]^)(y: Ref[T]^{x}): Unit = ??? + def foo[T](x: Ref[T]^): Unit = + swap(x)(x) + + def bar[T](x: () => Ref[T]^)(y: Ref[T]^{x}): Unit = + swap(x())(y) // error + + def baz[T](x: Ref[T]^)(y: Ref[T]^{x}): Unit = + swap(x)(y) +} + +trait IO +type Op = () -> Unit +def test2(c: IO^, f: Op^{c}) = { + def run(io: IO^)(op: Op^{io}): Unit = op() + run(c)(f) + + def bad(getIO: () => IO^, g: Op^{getIO}): Unit = + run(getIO())(g) // error +} + +def test3() = { + def run(io: IO^)(op: Op^{io}): Unit = ??? + val myIO: IO^ = ??? + val myOp: Op^{myIO} = ??? + run(myIO)(myOp) +} From 893a4c13c82d60892917a52827536c179e6ee862 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Thu, 4 May 2023 09:18:45 +0200 Subject: [PATCH 612/657] deps: bump jackson to 2.15.1 If you're curious the release notes for this version can be found in https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.15. --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 1ac5090b7a6e..54bc6ecadfe0 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -4,7 +4,7 @@ import sbt._ * to ensure the same version of the dependency is used in all projects */ object Dependencies { - private val jacksonVersion = "2.13.3" + private val jacksonVersion = "2.15.1" val `jackson-databind` = "com.fasterxml.jackson.core" % "jackson-databind" % jacksonVersion val `jackson-dataformat-yaml` = From 5038c9c4401e9922e0d5fa11369981f722be0d9c Mon Sep 17 00:00:00 2001 From: Seth Tisue Date: Thu, 18 May 2023 14:39:56 -0700 Subject: [PATCH 613/657] ClassfileParser: allow missing param names (for JDK 21) this is a forward port of scala/scala#10397 (fixing scala/bug#12783, from which Scala 3 also suffers) --- .../dotty/tools/dotc/core/classfile/ClassfileParser.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index eeeb3767bd34..0c701eb03d38 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -758,10 +758,12 @@ class ClassfileParser( case tpnme.MethodParametersATTR => val paramCount = in.nextByte for i <- 0 until paramCount do - val name = pool.getName(in.nextChar) + val index = in.nextChar val flags = in.nextChar - if (flags & JAVA_ACC_SYNTHETIC) == 0 then - res.namedParams += (i -> name.name) + if index != 0 then + val name = pool.getName(index) + if (flags & JAVA_ACC_SYNTHETIC) == 0 then + res.namedParams += (i -> name.name) case tpnme.AnnotationDefaultATTR => sym.addAnnotation(Annotation(defn.AnnotationDefaultAnnot, Nil, sym.span)) From cbf1f18d9291cf05c115da929668573acfc2ecdb Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Fri, 19 May 2023 08:36:57 +0200 Subject: [PATCH 614/657] fix(scaladocs): make sure scaladocs are aligned with actual behavior (#17448) This change just make sure the actual behavior is aligned with the comments about plugin options. refs https://github.com/lampepfl/dotty/discussions/17238 --- compiler/src/dotty/tools/dotc/plugins/Plugin.scala | 2 +- compiler/src/dotty/tools/dotc/plugins/Plugins.scala | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/plugins/Plugin.scala b/compiler/src/dotty/tools/dotc/plugins/Plugin.scala index be6fecebb55a..1baf3a06ad9e 100644 --- a/compiler/src/dotty/tools/dotc/plugins/Plugin.scala +++ b/compiler/src/dotty/tools/dotc/plugins/Plugin.scala @@ -44,7 +44,7 @@ sealed trait Plugin { trait StandardPlugin extends Plugin { /** Non-research plugins should override this method to return the phases * - * @param options commandline options to the plugin, `-P:plugname:opt1,opt2` becomes List(opt1, opt2) + * @param options commandline options to the plugin. * @return a list of phases to be added to the phase plan */ def init(options: List[String]): List[PluginPhase] diff --git a/compiler/src/dotty/tools/dotc/plugins/Plugins.scala b/compiler/src/dotty/tools/dotc/plugins/Plugins.scala index 976b783c40f0..c44fe4cf59b4 100644 --- a/compiler/src/dotty/tools/dotc/plugins/Plugins.scala +++ b/compiler/src/dotty/tools/dotc/plugins/Plugins.scala @@ -116,8 +116,6 @@ trait Plugins { /** Add plugin phases to phase plan */ def addPluginPhases(plan: List[List[Phase]])(using Context): List[List[Phase]] = { - // plugin-specific options. - // The user writes `-P:plugname:opt1,opt2`, but the plugin sees `List(opt1, opt2)`. def options(plugin: Plugin): List[String] = { def namec = plugin.name + ":" ctx.settings.pluginOptions.value filter (_ startsWith namec) map (_ stripPrefix namec) From e777aa52c6cf60bdcb0246731e0c7872ed157b46 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Fri, 19 May 2023 09:26:24 +0200 Subject: [PATCH 615/657] Clarify which websites are built by which command --- docs/_docs/contributing/workflow.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/_docs/contributing/workflow.md b/docs/_docs/contributing/workflow.md index fb8108e7ceb0..1d11dc61a6bf 100644 --- a/docs/_docs/contributing/workflow.md +++ b/docs/_docs/contributing/workflow.md @@ -115,7 +115,8 @@ The basics of working with Dotty codebase are documented [here](https://dotty.ep | `testOnly dotty.tools.dotc.CompilationTests -- *pos` | Run test (method) `pos` from `CompilationTests` suite. | | `testCompilation sample` | In all test suites, run test files containing the word `sample` in their title. | | `scala3-compiler/Test/runMain dotty.tools.printTypes`| Print types underlying representation | -| `scaladoc/generateScalaDocumentation` | Build the documentation website | +| `scaladoc/generateScalaDocumentation` | Build the documentation website (published to https://dotty.epfl.ch) | +| `scaladoc/generateReferenceDocumentation` | Build the reference documentation website (published to https://docs.scala-lang.org/scala3/reference) | ## Shell Commands From 13dd58db60a777a96f1f1d1253229bd74de42884 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 19 May 2023 16:21:34 +0200 Subject: [PATCH 616/657] Register `paramProxy` and `thisProxy` in `Quote` type Before 5ae7861eb246461bc0f970d0e446c2b080440d18 we used to find those types withing the `tpt`. Fixes #17434 --- .../dotty/tools/dotc/inlines/Inliner.scala | 1 + tests/pos-macros/i17434a/Macro.scala | 8 +++++ tests/pos-macros/i17434a/Test.scala | 23 +++++++++++++++ tests/pos-macros/i17434b/Macro.scala | 29 +++++++++++++++++++ tests/pos-macros/i17434b/Test.scala | 6 ++++ tests/pos-macros/i17434c/Macro.scala | 3 ++ tests/pos-macros/i17434c/Test.scala | 1 + tests/pos-macros/i17434d/Macro.scala | 2 ++ tests/pos-macros/i17434d/Test.scala | 4 +++ 9 files changed, 77 insertions(+) create mode 100644 tests/pos-macros/i17434a/Macro.scala create mode 100644 tests/pos-macros/i17434a/Test.scala create mode 100644 tests/pos-macros/i17434b/Macro.scala create mode 100644 tests/pos-macros/i17434b/Test.scala create mode 100644 tests/pos-macros/i17434c/Macro.scala create mode 100644 tests/pos-macros/i17434c/Test.scala create mode 100644 tests/pos-macros/i17434d/Macro.scala create mode 100644 tests/pos-macros/i17434d/Test.scala diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index 54decf137b42..73fa2a2871a2 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -486,6 +486,7 @@ class Inliner(val call: tpd.Tree)(using Context): /** Register type of leaf node */ private def registerLeaf(tree: Tree): Unit = tree match case _: This | _: Ident | _: TypeTree => registerTypes.traverse(tree.typeOpt) + case tree: Quote => registerTypes.traverse(tree.bodyType) case _ => /** Make `tree` part of inlined expansion. This means its owner has to be changed diff --git a/tests/pos-macros/i17434a/Macro.scala b/tests/pos-macros/i17434a/Macro.scala new file mode 100644 index 000000000000..0e399d82a9d1 --- /dev/null +++ b/tests/pos-macros/i17434a/Macro.scala @@ -0,0 +1,8 @@ +import scala.quoted.* + +object SelectDynamicMacroImpl { + def selectImpl[E: Type]( + ref: Expr[SQLSyntaxProvider[_]], + name: Expr[String] + )(using Quotes): Expr[SQLSyntax] = '{SQLSyntax("foo")} +} diff --git a/tests/pos-macros/i17434a/Test.scala b/tests/pos-macros/i17434a/Test.scala new file mode 100644 index 000000000000..8e7c314b238d --- /dev/null +++ b/tests/pos-macros/i17434a/Test.scala @@ -0,0 +1,23 @@ +// test.scala +import scala.language.dynamics + +trait SQLSyntaxProvider[A] extends Dynamic{ + def field(name: String): SQLSyntax = ??? + + inline def selectDynamic(inline name: String): SQLSyntax = + select[A](this, name) + + inline def select[E](ref: SQLSyntaxProvider[A], inline name: String): SQLSyntax = + ${ SelectDynamicMacroImpl.selectImpl[E]('ref, 'name) } +} + +class SQLSyntax(value: String) +trait SQLSyntaxSupport[A] +case class ColumnSQLSyntaxProvider[S <: SQLSyntaxSupport[A], A](support: S) extends SQLSyntaxProvider[A] + +case class Account(id: Long, name: String) +object Account extends SQLSyntaxSupport[Account] + +def Test() = + val p = ColumnSQLSyntaxProvider[Account.type, Account](Account) + assert(p.name == SQLSyntax("name")) diff --git a/tests/pos-macros/i17434b/Macro.scala b/tests/pos-macros/i17434b/Macro.scala new file mode 100644 index 000000000000..adca2888f777 --- /dev/null +++ b/tests/pos-macros/i17434b/Macro.scala @@ -0,0 +1,29 @@ +trait NameOf: + transparent inline def nameOf(inline expr: Any): String = ${NameOfImpl.nameOf('expr)} + transparent inline def nameOf[T](inline expr: T => Any): String = ${NameOfImpl.nameOf('expr)} +object NameOf extends NameOf + +import scala.compiletime.* + +import scala.annotation.tailrec +import scala.quoted.* + +object NameOfImpl { + def nameOf(expr: Expr[Any])(using Quotes): Expr[String] = { + import quotes.reflect.* + @tailrec def extract(tree: Tree): String = tree match { + case Ident(name) => name + case Select(_, name) => name + case Block(List(stmt), term) => extract(stmt) + case DefDef("$anonfun", _, _, Some(term)) => extract(term) + case Block(_, term) => extract(term) + case Apply(term, _) if term.symbol.fullName != ".throw" => extract(term) + case TypeApply(term, _) => extract(term) + case Inlined(_, _, term) => extract(term) + case Typed(term, _) => extract(term) + case _ => throw new MatchError(s"Unsupported expression: ${expr.show}") + } + val name = extract(expr.asTerm) + Expr(name) + } +} diff --git a/tests/pos-macros/i17434b/Test.scala b/tests/pos-macros/i17434b/Test.scala new file mode 100644 index 000000000000..5e71f9c95965 --- /dev/null +++ b/tests/pos-macros/i17434b/Test.scala @@ -0,0 +1,6 @@ +import NameOf._ +def test() = + def func1(x: Int): String = ??? + val funcVal = func1 _ + assert(nameOf(funcVal) == "funcVal") + assert(nameOf(func1 _) == "func1") diff --git a/tests/pos-macros/i17434c/Macro.scala b/tests/pos-macros/i17434c/Macro.scala new file mode 100644 index 000000000000..dc3d2a533117 --- /dev/null +++ b/tests/pos-macros/i17434c/Macro.scala @@ -0,0 +1,3 @@ +import scala.quoted.* +inline def foo[T](expr: T => Any): Unit = ${impl('expr)} +def impl(expr: Expr[Any])(using Quotes): Expr[Unit] = '{} diff --git a/tests/pos-macros/i17434c/Test.scala b/tests/pos-macros/i17434c/Test.scala new file mode 100644 index 000000000000..6561dd193b63 --- /dev/null +++ b/tests/pos-macros/i17434c/Test.scala @@ -0,0 +1 @@ +def test(f: Int => Any) = foo(f) diff --git a/tests/pos-macros/i17434d/Macro.scala b/tests/pos-macros/i17434d/Macro.scala new file mode 100644 index 000000000000..a76c8aab58e4 --- /dev/null +++ b/tests/pos-macros/i17434d/Macro.scala @@ -0,0 +1,2 @@ +import scala.quoted.* +def impl[E: Type](ref: Expr[Foo[_]])(using Quotes): Expr[Unit] = '{ } diff --git a/tests/pos-macros/i17434d/Test.scala b/tests/pos-macros/i17434d/Test.scala new file mode 100644 index 000000000000..3af0ddecd061 --- /dev/null +++ b/tests/pos-macros/i17434d/Test.scala @@ -0,0 +1,4 @@ +trait Foo[A]: + inline def foo(): Unit = bar[this.type](this) + inline def bar[E](ref: Foo[A]): Unit = ${ impl[E]('ref) } +def test(p: Foo[Int]) = p.foo() From da4996a132e2d44a4630c5a17173f468173333e3 Mon Sep 17 00:00:00 2001 From: Guillaume Raffin Date: Tue, 7 Mar 2023 18:54:53 +0100 Subject: [PATCH 617/657] Remove synthetic record constructor if the user has written one manually Co-authored-by: Guillaume Martres --- .../dotty/tools/dotc/core/Definitions.scala | 1 + .../dotty/tools/dotc/parsing/JavaParsers.scala | 10 +++++----- .../src/dotty/tools/dotc/typer/Namer.scala | 18 +++++++++++++----- .../src/dotty/tools/dotc/typer/Typer.scala | 12 +++++++++--- tests/pos-java16+/java-records/R2.java | 8 ++++---- 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index b7211b3ce5e3..a4f27358f673 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -688,6 +688,7 @@ class Definitions { @tu lazy val JavaCalendarClass: ClassSymbol = requiredClass("java.util.Calendar") @tu lazy val JavaDateClass: ClassSymbol = requiredClass("java.util.Date") @tu lazy val JavaFormattableClass: ClassSymbol = requiredClass("java.util.Formattable") + @tu lazy val JavaRecordClass: Symbol = getClassIfDefined("java.lang.Record") @tu lazy val JavaEnumClass: ClassSymbol = { val cls = requiredClass("java.lang.Enum") diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 5ab187871542..6ec896dcb200 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -21,8 +21,7 @@ import reporting._ import dotty.tools.dotc.util.SourceFile import util.Spans._ -import scala.collection.mutable.ListBuffer -import scala.collection.immutable.ListMap +import scala.collection.mutable.{ListBuffer, LinkedHashMap} object JavaParsers { @@ -839,7 +838,7 @@ object JavaParsers { // We need to generate accessors for every param, if no method with the same name is already defined - var fieldsByName = header.map(v => (v.name, (v.tpt, v.mods.annotations))).to(ListMap) + var fieldsByName = header.map(v => (v.name, (v.tpt, v.mods.annotations))).to(LinkedHashMap) for case DefDef(name, paramss, _, _) <- body if paramss.isEmpty && fieldsByName.contains(name) @@ -855,7 +854,8 @@ object JavaParsers { // generate the canonical constructor val canonicalConstructor = - DefDef(nme.CONSTRUCTOR, joinParams(tparams, List(header)), TypeTree(), EmptyTree).withMods(Modifiers(Flags.JavaDefined, mods.privateWithin)) + DefDef(nme.CONSTRUCTOR, joinParams(tparams, List(header)), TypeTree(), EmptyTree) + .withMods(Modifiers(Flags.JavaDefined | Flags.Synthetic, mods.privateWithin)) // return the trees val recordTypeDef = atSpan(start, nameOffset) { @@ -866,7 +866,7 @@ object JavaParsers { tparams = tparams, true ) - ) + ).withMods(mods) } addCompanionObject(statics, recordTypeDef) end recordDecl diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 4eeb5540f137..cc4433f75a68 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -862,7 +862,6 @@ class Namer { typer: Typer => * with a user-defined method in the same scope with a matching type. */ private def invalidateIfClashingSynthetic(denot: SymDenotation): Unit = - def isCaseClassOrCompanion(owner: Symbol) = owner.isClass && { if (owner.is(Module)) owner.linkedClass.is(CaseClass) @@ -879,10 +878,19 @@ class Namer { typer: Typer => !sd.symbol.is(Deferred) && sd.matches(denot))) val isClashingSynthetic = - denot.is(Synthetic, butNot = ConstructorProxy) - && desugar.isRetractableCaseClassMethodName(denot.name) - && isCaseClassOrCompanion(denot.owner) - && (definesMember || inheritsConcreteMember) + denot.is(Synthetic, butNot = ConstructorProxy) && + ( + (desugar.isRetractableCaseClassMethodName(denot.name) + && isCaseClassOrCompanion(denot.owner) + && (definesMember || inheritsConcreteMember) + ) + || + // remove synthetic constructor of a java Record if it clashes with a non-synthetic constructor + (denot.isConstructor + && denot.owner.is(JavaDefined) && denot.owner.derivesFrom(defn.JavaRecordClass) + && denot.owner.unforcedDecls.lookupAll(denot.name).exists(c => c != denot.symbol && c.info.matches(denot.info)) + ) + ) if isClashingSynthetic then typr.println(i"invalidating clashing $denot in ${denot.owner}") diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 02df12ecc0bc..49b26122d15e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2433,11 +2433,17 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer } def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(using Context): Tree = { - if (!sym.info.exists) { // it's a discarded synthetic case class method, drop it - assert(sym.is(Synthetic) && desugar.isRetractableCaseClassMethodName(sym.name)) + def canBeInvalidated(sym: Symbol): Boolean = + sym.is(Synthetic) + && (desugar.isRetractableCaseClassMethodName(sym.name) || + (sym.isConstructor && sym.owner.derivesFrom(defn.JavaRecordClass))) + + if !sym.info.exists then + // it's a discarded method (synthetic case class method or synthetic java record constructor), drop it + assert(canBeInvalidated(sym)) sym.owner.info.decls.openForMutations.unlink(sym) return EmptyTree - } + // TODO: - Remove this when `scala.language.experimental.erasedDefinitions` is no longer experimental. // - Modify signature to `erased def erasedValue[T]: T` if sym.eq(defn.Compiletime_erasedValue) then diff --git a/tests/pos-java16+/java-records/R2.java b/tests/pos-java16+/java-records/R2.java index 01da13d83b65..4b3f881628b9 100644 --- a/tests/pos-java16+/java-records/R2.java +++ b/tests/pos-java16+/java-records/R2.java @@ -5,9 +5,9 @@ public int getInt() { } // Canonical constructor - // public R(int i, java.lang.String s) { - // this.i = i; - // this.s = s.intern(); - // } + public R(int i, java.lang.String s) { + this.i = i; + this.s = s.intern(); + } } } From ac2b53baea5555a475456b98d8b8763608de3c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Tue, 23 May 2023 13:35:46 +0200 Subject: [PATCH 618/657] Fix #17549: Unify how Memoize and Constructors decide what fields need storing. The Memoize and Constructors have to work together and agree on which `final val`s actually need storing in a field. Previously, they used slightly different criteria: one on the result type, and one on the rhs (with an additional Scala.js-specific eligibility condition). That discrepancy resulted in the crash/wrong codegen in the issue. We now unify both: we avoid a field if and only if all of the following apply: * it is a `final val`, * its result *type* is a `ConstantType`, and * it is eligible according to Scala.js semantics. In particular, there is no condition on the right-hand-side. We avoid a field even if the right-hand-side has side effects. The side effects are moved to the constructor regardless. --- This introduces both progressions and regressions in the amount of fields we generate. We can avoid fields even for side-effecting rhs'es, as long as the result type is constant. On the other hand, we now create a field for `final val`s with non-constant result type, even if the rhs is a literal. While the latter is a regression for Scala 3, it aligns with the behavior of Scala 2. It also has the nice benefit that whether or not a val has a field is now independent of its *implementation*, and only dependent on its *API*. Overall, I think this is a trade-off worth taking. We could reintroduce that optimization in the future (but in classes only; not in traits), if we really want to, although that would require dedicated code. --- .../tools/dotc/transform/Constructors.scala | 54 +++++++++++-------- .../dotty/tools/dotc/transform/Memoize.scala | 23 ++------ .../dotty/tools/dotc/transform/SymUtils.scala | 24 ++++++++- .../tools/dotc/transform/sjs/JSSymUtils.scala | 17 ++++++ tests/run/erased-inline-vals.scala | 35 +++++++++++- tests/run/final-fields.check | 2 +- tests/run/i17549.check | 6 +++ tests/run/i17549.scala | 27 ++++++++++ 8 files changed, 142 insertions(+), 46 deletions(-) create mode 100644 tests/run/i17549.check create mode 100644 tests/run/i17549.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index 59b90ff7f084..4dd7205e4ee0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -226,31 +226,39 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = constrStats += intoConstr(stat, sym) } else dropped += sym - case stat @ DefDef(name, _, tpt, _) - if stat.symbol.isGetter && stat.symbol.owner.is(Trait) && !stat.symbol.is(Lazy) && !stat.symbol.isConstExprFinalVal => + case stat @ DefDef(name, _, tpt, _) if stat.symbol.isGetter && !stat.symbol.is(Lazy) => val sym = stat.symbol assert(isRetained(sym), sym) - if !stat.rhs.isEmpty && !isWildcardArg(stat.rhs) then - /* !!! Work around #9390 - * This should really just be `sym.setter`. However, if we do that, we'll miss - * setters for mixed in `private var`s. Even though the scope clearly contains the - * setter symbol with the correct Name structure (since the `find` finds it), - * `.decl(setterName)` used by `.setter` through `.accessorNamed` will *not* find it. - * Could it be that the hash table of the `Scope` is corrupted? - * We still try `sym.setter` first as an optimization, since it will work for all - * public vars in traits and all (public or private) vars in classes. - */ - val symSetter = - if sym.setter.exists then - sym.setter - else - val setterName = sym.asTerm.name.setterName - sym.owner.info.decls.find(d => d.is(Accessor) && d.name == setterName) - val setter = - if (symSetter.exists) symSetter - else sym.accessorNamed(Mixin.traitSetterName(sym.asTerm)) - constrStats += Apply(ref(setter), intoConstr(stat.rhs, sym).withSpan(stat.span) :: Nil) - clsStats += cpy.DefDef(stat)(rhs = EmptyTree) + if sym.isConstExprFinalVal then + if stat.rhs.isInstanceOf[Literal] then + clsStats += stat + else + constrStats += intoConstr(stat.rhs, sym) + clsStats += cpy.DefDef(stat)(rhs = Literal(sym.constExprFinalValConstantType.value).withSpan(stat.span)) + else if !sym.owner.is(Trait) then + clsStats += stat + else + if !stat.rhs.isEmpty && !isWildcardArg(stat.rhs) then + /* !!! Work around #9390 + * This should really just be `sym.setter`. However, if we do that, we'll miss + * setters for mixed in `private var`s. Even though the scope clearly contains the + * setter symbol with the correct Name structure (since the `find` finds it), + * `.decl(setterName)` used by `.setter` through `.accessorNamed` will *not* find it. + * Could it be that the hash table of the `Scope` is corrupted? + * We still try `sym.setter` first as an optimization, since it will work for all + * public vars in traits and all (public or private) vars in classes. + */ + val symSetter = + if sym.setter.exists then + sym.setter + else + val setterName = sym.asTerm.name.setterName + sym.owner.info.decls.find(d => d.is(Accessor) && d.name == setterName) + val setter = + if (symSetter.exists) symSetter + else sym.accessorNamed(Mixin.traitSetterName(sym.asTerm)) + constrStats += Apply(ref(setter), intoConstr(stat.rhs, sym).withSpan(stat.span) :: Nil) + clsStats += cpy.DefDef(stat)(rhs = EmptyTree) case DefDef(nme.CONSTRUCTOR, ((outerParam @ ValDef(nme.OUTER, _, _)) :: _) :: Nil, _, _) => clsStats += mapOuter(outerParam.symbol).transform(stat) case _: DefTree => diff --git a/compiler/src/dotty/tools/dotc/transform/Memoize.scala b/compiler/src/dotty/tools/dotc/transform/Memoize.scala index 1392d00011a2..03ac15b39ffe 100644 --- a/compiler/src/dotty/tools/dotc/transform/Memoize.scala +++ b/compiler/src/dotty/tools/dotc/transform/Memoize.scala @@ -20,8 +20,6 @@ import sjs.JSSymUtils._ import util.Store -import dotty.tools.backend.sjs.JSDefinitions.jsdefn - object Memoize { val name: String = "memoize" val description: String = "add private fields to getters and setters" @@ -130,32 +128,17 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase => } if sym.is(Accessor, butNot = NoFieldNeeded) then - /* Tests whether the semantics of Scala.js require a field for this symbol, irrespective of any - * optimization we think we can do. This is the case if one of the following is true: - * - it is a member of a JS type, since it needs to be visible as a JavaScript field - * - is is exported as static member of the companion class, since it needs to be visible as a JavaScript static field - * - it is exported to the top-level, since that can only be done as a true top-level variable, i.e., a field - */ - def sjsNeedsField: Boolean = - ctx.settings.scalajs.value && ( - sym.owner.isJSType - || sym.hasAnnotation(jsdefn.JSExportTopLevelAnnot) - || sym.hasAnnotation(jsdefn.JSExportStaticAnnot) - ) - def adaptToField(field: Symbol, tree: Tree): Tree = if (tree.isEmpty) tree else tree.ensureConforms(field.info.widen) def isErasableBottomField(field: Symbol, cls: Symbol): Boolean = !field.isVolatile && ((cls eq defn.NothingClass) || (cls eq defn.NullClass) || (cls eq defn.BoxedUnitClass)) - && !sjsNeedsField + && !sym.sjsNeedsField if sym.isGetter then - val constantFinalVal = - sym.isAllOf(Accessor | Final, butNot = Mutable) && tree.rhs.isInstanceOf[Literal] && !sjsNeedsField - if constantFinalVal then - // constant final vals do not need to be transformed at all, and do not need a field + if sym.isConstExprFinalVal then + // const-expr final vals do not need to be transformed at all, and do not need a field tree else val field = newField.asTerm diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index feb841ba5c6c..c02a7d90cb8c 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -18,6 +18,8 @@ import Annotations.Annotation import Phases._ import ast.tpd.Literal +import dotty.tools.dotc.transform.sjs.JSSymUtils.sjsNeedsField + import scala.annotation.tailrec object SymUtils: @@ -259,9 +261,29 @@ object SymUtils: self.owner.info.decl(fieldName).suchThat(!_.is(Method)).symbol } + /** Is this symbol a constant expression final val? + * + * This is the case if all of the following are true: + * + * - it is a `final val`, + * - its result type is a `ConstantType`, and + * - it does not need an explicit field because of Scala.js semantics (see `JSSymUtils.sjsNeedsField`). + * + * Constant expression final vals do not need an explicit field to store + * their value. See the Memoize-Mixin-Constructors phase trio. + */ def isConstExprFinalVal(using Context): Boolean = atPhaseNoLater(erasurePhase) { - self.is(Final) && self.info.resultType.isInstanceOf[ConstantType] + self.is(Final, butNot = Mutable) && self.info.resultType.isInstanceOf[ConstantType] + } && !self.sjsNeedsField + + /** The `ConstantType` of a val known to be `isConstrExprFinalVal`. + * + * @pre `self.isConstantExprFinalVal` is true. + */ + def constExprFinalValConstantType(using Context): ConstantType = + atPhaseNoLater(erasurePhase) { + self.info.resultType.asInstanceOf[ConstantType] } def isField(using Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala b/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala index 30eed76b18ec..115d41dd3d46 100644 --- a/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/sjs/JSSymUtils.scala @@ -211,6 +211,23 @@ object JSSymUtils { } } } + + /** Tests whether the semantics of Scala.js require a field for this symbol, + * irrespective of any optimization we think we can do. + * + * This is the case if one of the following is true: + * + * - it is a member of a JS type, since it needs to be visible as a JavaScript field + * - is is exported as static member of the companion class, since it needs to be visible as a JavaScript static field + * - it is exported to the top-level, since that can only be done as a true top-level variable, i.e., a field + */ + def sjsNeedsField(using Context): Boolean = + ctx.settings.scalajs.value && ( + sym.owner.isJSType + || sym.hasAnnotation(jsdefn.JSExportTopLevelAnnot) + || sym.hasAnnotation(jsdefn.JSExportStaticAnnot) + ) + end sjsNeedsField } private object JSUnaryOpMethodName { diff --git a/tests/run/erased-inline-vals.scala b/tests/run/erased-inline-vals.scala index 00c9c8c168c7..c39a8295af5d 100644 --- a/tests/run/erased-inline-vals.scala +++ b/tests/run/erased-inline-vals.scala @@ -16,6 +16,27 @@ class D: inline def x: Int = 5 inline val y = 6 +object SideEffects: + val sideEffects = scala.collection.mutable.ListBuffer.empty[String] + +trait E: + final val a: 7 = + SideEffects.sideEffects += "E.a" + 7 + final val b = + SideEffects.sideEffects += "E.b" + 8 +end E + +class F extends E: + final val c: 9 = + SideEffects.sideEffects += "F.c" + 9 + final val d = + SideEffects.sideEffects += "F.d" + 10 +end F + @main def Test = val b: B = new B assert(b.x == 1) @@ -37,12 +58,24 @@ class D: assert(d.x == 5) assert(d.y == 6) + val f: F = new F + assert(SideEffects.sideEffects.toList == List("E.a", "E.b", "F.c", "F.d")) + assert(f.a == 7) + assert(f.b == 8) + assert(f.c == 9) + assert(f.d == 10) assert(classOf[B].getDeclaredMethods.size == 2) assert(classOf[B].getDeclaredFields.isEmpty) assert(classOf[C].getDeclaredMethods.size == 2) - assert(classOf[C].getDeclaredFields.isEmpty) + assert(classOf[C].getDeclaredFields.size == 1) // x, but not y assert(classOf[D].getDeclaredMethods.isEmpty) assert(classOf[D].getFields.isEmpty) + + assert(classOf[E].getDeclaredMethods.size == 5) + assert(classOf[E].getDeclaredFields.isEmpty) + + assert(classOf[F].getDeclaredMethods.size == 2) + assert(classOf[F].getDeclaredFields.isEmpty) diff --git a/tests/run/final-fields.check b/tests/run/final-fields.check index 3ebde7d7f735..af090f65a32a 100644 --- a/tests/run/final-fields.check +++ b/tests/run/final-fields.check @@ -2,6 +2,6 @@ T.f1 T.f2 T.f3 T.f4 -3 2 -3 -4 +3 2 3 -4 3 g diff --git a/tests/run/i17549.check b/tests/run/i17549.check new file mode 100644 index 000000000000..df4c241f5ffe --- /dev/null +++ b/tests/run/i17549.check @@ -0,0 +1,6 @@ +T.x +C.y +1 +2 +1 +2 diff --git a/tests/run/i17549.scala b/tests/run/i17549.scala new file mode 100644 index 000000000000..165e40512153 --- /dev/null +++ b/tests/run/i17549.scala @@ -0,0 +1,27 @@ +trait T: + final val x: 1 = + println("T.x") + 1 +end T + +trait U: + def x: Any + def y: Any + +class C extends T with U: + final val y: 2 = + println("C.y") + 2 +end C + +object Test: + def main(args: Array[String]): Unit = + val c = new C + println(c.x) + println(c.y) + + val u: U = c + println(u.x) + println(u.y) + end main +end Test From a10478d0d8de665b5e97f87dfebb350f2f2a467b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 23 May 2023 23:41:34 +0200 Subject: [PATCH 619/657] Set reference version to 3.3.0 --- project/Build.scala | 4 ++-- project/MiMaFilters.scala | 30 ------------------------------ 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index dbf574252113..910ee7ef4f58 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -80,7 +80,7 @@ object DottyJSPlugin extends AutoPlugin { object Build { import ScaladocConfigs._ - val referenceVersion = "3.3.0-RC6" + val referenceVersion = "3.3.0" val baseVersion = "3.3.1-RC1" @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.2.2" + val previousDottyVersion = "3.3.0" object CompatMode { final val BinaryCompatible = 0 diff --git a/project/MiMaFilters.scala b/project/MiMaFilters.scala index c5b00510bece..112a5601615c 100644 --- a/project/MiMaFilters.scala +++ b/project/MiMaFilters.scala @@ -3,23 +3,7 @@ import com.typesafe.tools.mima.core._ object MiMaFilters { val Library: Seq[ProblemFilter] = Seq( - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.unsafeBox"), - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.unsafeUnbox"), - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.cap"), - ProblemFilters.exclude[MissingClassProblem]("scala.caps$Sealed"), - ProblemFilters.exclude[DirectMissingMethodProblem]("scala.CanEqual.canEqualMap"), - ProblemFilters.exclude[MissingClassProblem]("scala.caps$Pure"), - ProblemFilters.exclude[MissingClassProblem]("scala.caps$unsafe$"), ProblemFilters.exclude[MissingClassProblem]("scala.annotation.unchecked.uncheckedCaptures"), - ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.3.3-migration"), - ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language.3.3"), - ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$3$u002E3$"), - ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$3$u002E3$minusmigration$"), - ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary"), - ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$"), - ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Break"), - ProblemFilters.exclude[MissingClassProblem]("scala.util.boundary$Label"), - ProblemFilters.exclude[MissingClassProblem]("scala.quoted.runtime.QuoteMatching$"), // Scala.js only: new runtime support class in 3.2.3; not available to users ProblemFilters.exclude[MissingClassProblem]("scala.scalajs.runtime.AnonFunctionXXL"), @@ -27,26 +11,12 @@ object MiMaFilters { // New experimental features in 3.3.X ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.clauseInterleaving"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$clauseInterleaving$"), - ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.into"), - ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$into$"), ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.stdLibPatches.language#experimental.relaxedExtensionImports"), ProblemFilters.exclude[MissingClassProblem]("scala.runtime.stdLibPatches.language$experimental$relaxedExtensionImports$"), // end of New experimental features in 3.3.X - - // Added java.io.Serializable as LazyValControlState supertype - ProblemFilters.exclude[MissingTypesProblem]("scala.runtime.LazyVals$LazyValControlState"), - ProblemFilters.exclude[MissingTypesProblem]("scala.runtime.LazyVals$Waiting"), - ) val TastyCore: Seq[ProblemFilter] = Seq( - ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.TastyBuffer.reset"), - ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.TastyFormat.APPLYsigpoly"), - ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.TastyHash.pjwHash64"), - ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.tasty.util.Util.dble") ) val Interfaces: Seq[ProblemFilter] = Seq( - ProblemFilters.exclude[ReversedMissingMethodProblem]("dotty.tools.dotc.interfaces.Diagnostic.diagnosticRelatedInformation"), - ProblemFilters.exclude[DirectMissingMethodProblem]("dotty.tools.dotc.interfaces.Diagnostic.diagnosticRelatedInformation"), - ProblemFilters.exclude[MissingClassProblem]("dotty.tools.dotc.interfaces.DiagnosticRelatedInformation") ) } From ca7c29f4b9c5dc1a70a54124e87db9839662021d Mon Sep 17 00:00:00 2001 From: Lucas Leblanc <44496264+Dedelweiss@users.noreply.github.com> Date: Wed, 24 May 2023 08:36:37 +0200 Subject: [PATCH 620/657] Fix: Add max-width for images in api doc (#17558) Fixes #17551 --- .../dotty_res/styles/theme/components/api-member.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scaladoc/resources/dotty_res/styles/theme/components/api-member.css b/scaladoc/resources/dotty_res/styles/theme/components/api-member.css index c1a491815201..47b64c304a70 100644 --- a/scaladoc/resources/dotty_res/styles/theme/components/api-member.css +++ b/scaladoc/resources/dotty_res/styles/theme/components/api-member.css @@ -42,6 +42,10 @@ margin-block-end: 0; } +.documentableElement .doc img { + max-width: 100%; +} + .documentableElement .annotations { display: none; } From 484145390b860e8426e52955899f4013737c5f47 Mon Sep 17 00:00:00 2001 From: Chris Kipp Date: Wed, 24 May 2023 10:35:43 +0200 Subject: [PATCH 621/657] test: add in a regression test for #15546 [skip community_build] closes #15546 --- tests/pos/i15546.scala | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/pos/i15546.scala diff --git a/tests/pos/i15546.scala b/tests/pos/i15546.scala new file mode 100644 index 000000000000..19c7f15b24f1 --- /dev/null +++ b/tests/pos/i15546.scala @@ -0,0 +1,14 @@ +// https://github.com/lampepfl/dotty/issues/15546 + +trait Foo[F[_]] + +object Bug { + def apply[F[_]: Foo]( + await: Boolean, + whatever: Int = 0 + ): Nothing = ??? + + def apply[F[_]: Foo]: Nothing = + apply[F](false) +} + From 390f836c5ac0d8aaa30b331745e361b75139f68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 24 May 2023 13:29:23 +0200 Subject: [PATCH 622/657] Add changelog for 3.3.1-RC1 --- changelogs/3.3.1-RC1.md | 288 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 changelogs/3.3.1-RC1.md diff --git a/changelogs/3.3.1-RC1.md b/changelogs/3.3.1-RC1.md new file mode 100644 index 000000000000..4e52eb874891 --- /dev/null +++ b/changelogs/3.3.1-RC1.md @@ -0,0 +1,288 @@ +# Highlights of the release + +- Support records in JavaParsers [#16762](https://github.com/lampepfl/dotty/pull/16762) +- Port JVM backend refactor from Scala 2 [#15322](https://github.com/lampepfl/dotty/pull/15322) + +# Other changes and fixes + +## Backend + +- Disallow mixins where super calls bind to vals [#16908](https://github.com/lampepfl/dotty/pull/16908) +- Fix #15107: Avoid re-emitting a LineNumber after only LabelNodes. [#16813](https://github.com/lampepfl/dotty/pull/16813) + +## Coverage + +- Fix #17042: Preserve the shape of secondary ctors in instrumentCoverage. [#17111](https://github.com/lampepfl/dotty/pull/17111) + +## Default parameters + +- Dupe fix when finding default arg getters [#17058](https://github.com/lampepfl/dotty/pull/17058) + +## Documentation + +- Fix: ensure syntax blocks for ebnf are marked as such [#16837](https://github.com/lampepfl/dotty/pull/16837) + +## Erasure + +- Handle `@companionClass` and `@companionMethod` meta-annotations [#17091](https://github.com/lampepfl/dotty/pull/17091) + +## Extension Methods + +- Support extension methods imported from different objects [#17050](https://github.com/lampepfl/dotty/pull/17050) + +## GADTs + +- Fix tuple member selection so it works with GADT healing [#16766](https://github.com/lampepfl/dotty/pull/16766) +- Fix upper bound constraints, that are higher-kinded [#16744](https://github.com/lampepfl/dotty/pull/16744) +- Split out immutable GadtConstraint [#16602](https://github.com/lampepfl/dotty/pull/16602) + +## Implicits + +- Improve subtyping check for not yet eta-expanded higher kinded types [#17139](https://github.com/lampepfl/dotty/pull/17139) +- Harden tpd.Apply/TypeApply in case of errors [#16887](https://github.com/lampepfl/dotty/pull/16887) +- Try to be more subtle when inferring type parameters of class parents [#16896](https://github.com/lampepfl/dotty/pull/16896) +- Include `P` in the implicit scope of `P.this.type` [#17088](https://github.com/lampepfl/dotty/pull/17088) + +## Incremental Compilation + +- Fix under-compilation when the method type in a SAM changes [#16996](https://github.com/lampepfl/dotty/pull/16996) + +## Infrastructure + +- Set reference version to 3.3.0-RC6 [#17504](https://github.com/lampepfl/dotty/pull/17504) +- Fix #17119: Download Coursier from GitHub directly [#17141](https://github.com/lampepfl/dotty/pull/17141) + +## Inline + +- Remove NamedArg from inlined arguments [#17228](https://github.com/lampepfl/dotty/pull/17228) +- Don't generate a Select for a TermRef with NoPrefix [#16754](https://github.com/lampepfl/dotty/pull/16754) +- Prepare bodies of inline forwarders eagerly [#16757](https://github.com/lampepfl/dotty/pull/16757) +- Do not remove inline method implementations until PruneErasedDefs [#17408](https://github.com/lampepfl/dotty/pull/17408) + +## Java Interop + +- ClassfileParser: allow missing param names (for JDK 21) [#17536](https://github.com/lampepfl/dotty/pull/17536) + +## Linting + +- Improve -Wunused: locals, privates with unset vars warning #16639 [#17160](https://github.com/lampepfl/dotty/pull/17160) +- Fix wunused false positive when deriving alias type [#17157](https://github.com/lampepfl/dotty/pull/17157) +- Port `-Wnonunit-statement` setting for dotty [#16936](https://github.com/lampepfl/dotty/pull/16936) + +## Match Types + +- Normalize match type usage during implicit lookup [#17457](https://github.com/lampepfl/dotty/pull/17457) +- Fix #13757: Explicitly disallow higher-kinded scrutinees of match types. [#17322](https://github.com/lampepfl/dotty/pull/17322) +- Fix match type reduction with wildcard type arguments [#17065](https://github.com/lampepfl/dotty/pull/17065) +- Fix check whether classtag can be generated for match types [#16708](https://github.com/lampepfl/dotty/pull/16708) + +## Parser + +- Allow lines starting with `.` to fall outside previous indentation widths [#17056](https://github.com/lampepfl/dotty/pull/17056) + +## Pattern Matching + +- Fix #11541: Specialize ClassTag[T] in exhaustivity check [#17385](https://github.com/lampepfl/dotty/pull/17385) +- Check outer class prefixes in type projections when pattern matching [#17136](https://github.com/lampepfl/dotty/pull/17136) +- Make unchecked cases non-`@unchecked` and non-unreachable [#16958](https://github.com/lampepfl/dotty/pull/16958) +- Fix #16899: Better handle X instanceOf P where X is T1 | T2 [#17382](https://github.com/lampepfl/dotty/pull/17382) + +## Pickling + +- ClassfileParser: Avoid cycle when accessing companion in inner class lookup [#16882](https://github.com/lampepfl/dotty/pull/16882) + +## Polyfunctions + +- Fix type aliases in beta-reduction of polyfunctions [#17054](https://github.com/lampepfl/dotty/pull/17054) + +## Quotes + +- Register `paramProxy` and `thisProxy` in `Quote` type [#17541](https://github.com/lampepfl/dotty/pull/17541) +- Only check newVal/newMethod privateWithin on -Xcheck-macros [#17437](https://github.com/lampepfl/dotty/pull/17437) +- Unencode quote and splice trees [#17342](https://github.com/lampepfl/dotty/pull/17342) +- Correctly type Expr.ofTupleFromSeq for arity > 22 [#17261](https://github.com/lampepfl/dotty/pull/17261) +- Use TermRef to distinguish distinct Type[T] instances [#17205](https://github.com/lampepfl/dotty/pull/17205) +- Check level consistency of SingletonTypeTree as a type [#17209](https://github.com/lampepfl/dotty/pull/17209) +- Fix splice type variable pattern detection [#17048](https://github.com/lampepfl/dotty/pull/17048) +- Avoid creation of `@SplicedType` quote local refrences [#17051](https://github.com/lampepfl/dotty/pull/17051) +- Dealias type references when healing types in quotes [#17049](https://github.com/lampepfl/dotty/pull/17049) +- Replace quoted type variables in signature of HOAS pattern result [#16951](https://github.com/lampepfl/dotty/pull/16951) +- Beta-reduce directly applied PolymorphicFunction [#16623](https://github.com/lampepfl/dotty/pull/16623) +- Use `Object.toString` for `quoted.{Expr, Type}` [#16663](https://github.com/lampepfl/dotty/pull/16663) +- Fix Splicer.isEscapedVariable [#16838](https://github.com/lampepfl/dotty/pull/16838) +- Fix references to class members defined in quotes [#17107](https://github.com/lampepfl/dotty/pull/17107) +- Handle pickled forward references in pickled expressions [#16855](https://github.com/lampepfl/dotty/pull/16855) +- Fix #16615 - crashes of path dependent types in spliced Type.of [#16773](https://github.com/lampepfl/dotty/pull/16773) +- Disallow local term references in staged types [#16362](https://github.com/lampepfl/dotty/pull/16362) +- Refactor level checking / type healing logic [#17082](https://github.com/lampepfl/dotty/pull/17082) +- Dealias quoted types when staging [#17059](https://github.com/lampepfl/dotty/pull/17059) +- Fix quotes with references to path dependent types [#17081](https://github.com/lampepfl/dotty/pull/17081) +- Make arguments order in quote hole deterministic [#17405](https://github.com/lampepfl/dotty/pull/17405) +- Only transform the body of the quote with QuoteTransformer [#17451](https://github.com/lampepfl/dotty/pull/17451) +- Place staged type captures in Quote AST [#17424](https://github.com/lampepfl/dotty/pull/17424) +- Add SplicePattern AST to parse and type quote pattern splices [#17396](https://github.com/lampepfl/dotty/pull/17396) + +## Reflection + +- -Xcheck-macros: add hint when a symbol in created twice [#16733](https://github.com/lampepfl/dotty/pull/16733) +- Assert that symbols created using reflect API have correct privateWithin symbols [#17352](https://github.com/lampepfl/dotty/pull/17352) +- Fix reflect.LambdaType type test [#16972](https://github.com/lampepfl/dotty/pull/16972) +- Improve `New`/`Select` -Ycheck message [#16746](https://github.com/lampepfl/dotty/pull/16746) +- Improve error message for CyclicReference in macros [#16749](https://github.com/lampepfl/dotty/pull/16749) +- Add reflect `defn.FunctionClass` overloads [#16849](https://github.com/lampepfl/dotty/pull/16849) + +## REPL + +- Always load REPL classes in macros including the output directory [#16866](https://github.com/lampepfl/dotty/pull/16866) + +## Reporting + +- Improve missing argument list error [#17126](https://github.com/lampepfl/dotty/pull/17126) +- Improve implicit parameter error message with aliases [#17125](https://github.com/lampepfl/dotty/pull/17125) +- Improve "constructor proxy shadows outer" handling [#17154](https://github.com/lampepfl/dotty/pull/17154) +- Clarify ambiguous reference error message [#16137](https://github.com/lampepfl/dotty/pull/16137) +- Hint about forbidden combination of implicit values and conversions [#16735](https://github.com/lampepfl/dotty/pull/16735) +- Attach explanation message to diagnostic message [#16787](https://github.com/lampepfl/dotty/pull/16787) +- Propagate implicit search errors from implicit macros [#16840](https://github.com/lampepfl/dotty/pull/16840) +- Detail UnapplyInvalidReturnType error message [#17167](https://github.com/lampepfl/dotty/pull/17167) +- Add way to debug -Xcheck-macros tree checking [#16973](https://github.com/lampepfl/dotty/pull/16973) +- Enrich and finesse compiler crash reporting [#17031](https://github.com/lampepfl/dotty/pull/17031) +- Allow @implicitNotFound messages as explanations [#16893](https://github.com/lampepfl/dotty/pull/16893) +- Include top-level symbols from same file in outer ambiguity error [#17033](https://github.com/lampepfl/dotty/pull/17033) +- Do not issue deprecation warnings when declaring deprecated case classes [#17165](https://github.com/lampepfl/dotty/pull/17165) + +## Scala-JS + +- Fix #17344: Make implicit references to this above dynamic imports explicit. [#17357](https://github.com/lampepfl/dotty/pull/17357) +- Fix #12621: Better error message for JS trait ctor param. [#16811](https://github.com/lampepfl/dotty/pull/16811) +- Fix #16801: Handle Closure's of s.r.FunctionXXL. [#16809](https://github.com/lampepfl/dotty/pull/16809) +- Fix #17549: Unify how Memoize and Constructors decide what fields need storing. [#17560](https://github.com/lampepfl/dotty/pull/17560) + +## Scaladoc + +- Feat: Add a blog configuration with yaml [#17214](https://github.com/lampepfl/dotty/pull/17214) +- Don't render the "$" for module [#17302](https://github.com/lampepfl/dotty/pull/17302) +- Fix: Add scrollbar to the sidebar [#17203](https://github.com/lampepfl/dotty/pull/17203) +- Scaladoc: fix crash when processing extends call [#17260](https://github.com/lampepfl/dotty/pull/17260) +- Fix: Modify the CSS so that the logo of the generated documentation is adaptive [#17172](https://github.com/lampepfl/dotty/pull/17172) +- Fix: Remove the duplicate parameter when generating the scaladoc. [#17097](https://github.com/lampepfl/dotty/pull/17097) +- Fix: padding top in mobile version [#17019](https://github.com/lampepfl/dotty/pull/17019) +- Fix: tap target of the menu in Mobile version [#17018](https://github.com/lampepfl/dotty/pull/17018) +- Scaladoc: Fix expand icon not changing on anchor link [#17053](https://github.com/lampepfl/dotty/pull/17053) +- Scaladoc: fix inkuire generation for PolyTypes [#17129](https://github.com/lampepfl/dotty/pull/17129) +- Re port scroll bar [#17463](https://github.com/lampepfl/dotty/pull/17463) +- Handle empty files and truncated YAML front matter [#17527](https://github.com/lampepfl/dotty/pull/17527) + +## SemanticDB + +- Make sure symbol exists before calling owner [#16860](https://github.com/lampepfl/dotty/pull/16860) +- Support LambdaType (convert from HKTypeLambda) [#16056](https://github.com/lampepfl/dotty/pull/16056) + +## Specification + +- Apply `class-shadowing.md` to the Spec [#16839](https://github.com/lampepfl/dotty/pull/16839) +- Adding base for future Spec into the compiler repo [#16825](https://github.com/lampepfl/dotty/pull/16825) + +## Standard Library + +- Optimization: avoid NotGiven allocations [#17090](https://github.com/lampepfl/dotty/pull/17090) + +## Tooling + +- Disable `ExtractSemanticDB` phase when writing to output directory defined as JAR. [#16790](https://github.com/lampepfl/dotty/pull/16790) +- Print owner of bind symbol with -Yprint-debug-owners [#16854](https://github.com/lampepfl/dotty/pull/16854) +- Small fixes to allow using Metals with scaladoc with sbt [#16816](https://github.com/lampepfl/dotty/pull/16816) + +## Transform + +- Move CrossVersionChecks before FirstTransform [#17301](https://github.com/lampepfl/dotty/pull/17301) +- Fix needsOuterIfReferenced [#17159](https://github.com/lampepfl/dotty/pull/17159) +- Drop incorrect super accessor in trait subclass [#17062](https://github.com/lampepfl/dotty/pull/17062) +- Generate toString only for synthetic companions of case classes [#16890](https://github.com/lampepfl/dotty/pull/16890) +- Check trait constructor for accessibility even if not called at Typer [#17094](https://github.com/lampepfl/dotty/pull/17094) +- Fix #17435: A simpler fix [#17436](https://github.com/lampepfl/dotty/pull/17436) + +## Typer + +- Preserve type bounds for inlined definitions in posttyper [#17190](https://github.com/lampepfl/dotty/pull/17190) +- Change logic to find members of recursive types [#17386](https://github.com/lampepfl/dotty/pull/17386) +- Recognize named arguments in isFunctionWithUnknownParamType [#17161](https://github.com/lampepfl/dotty/pull/17161) +- Better comparisons for type projections [#17092](https://github.com/lampepfl/dotty/pull/17092) +- Allow selectDynamic and applyDynamic to be extension methods [#17106](https://github.com/lampepfl/dotty/pull/17106) +- Fix use of accessibleFrom when finding default arg getters [#16977](https://github.com/lampepfl/dotty/pull/16977) +- Map class literal constant types [#16988](https://github.com/lampepfl/dotty/pull/16988) +- Always use adapted type in withDenotation [#16901](https://github.com/lampepfl/dotty/pull/16901) +- Restrict captureWildcards to only be used if needed [#16799](https://github.com/lampepfl/dotty/pull/16799) +- Don't capture wildcards if in closure or by-name [#16732](https://github.com/lampepfl/dotty/pull/16732) +- Infer: Don't minimise to Nothing if there's an upper bound [#16786](https://github.com/lampepfl/dotty/pull/16786) +- Perform Matchable check only if type test is needed [#16824](https://github.com/lampepfl/dotty/pull/16824) +- Don't eta expand unary varargs methods [#16892](https://github.com/lampepfl/dotty/pull/16892) +- Fix beta-reduction with `Nothing` and `null` args [#16938](https://github.com/lampepfl/dotty/pull/16938) +- Generate kind-correct wildcards when selecting from a wildcard [#17025](https://github.com/lampepfl/dotty/pull/17025) +- Fix #16405 ctd - wildcards prematurely resolving to Nothing [#16764](https://github.com/lampepfl/dotty/pull/16764) +- Test: add regression test for #7790 [#17473](https://github.com/lampepfl/dotty/pull/17473) +- Properly handle `AnyVal`s as refinement members of `Selectable`s [#16286](https://github.com/lampepfl/dotty/pull/16286) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.0..3.3.1-RC1` these are: + +``` + 148 Nicolas Stucki + 65 Martin Odersky + 51 Szymon Rodziewicz + 49 Dale Wijnand + 49 Quentin Bernet + 38 Chris Kipp + 19 David Hua + 18 Lucas + 18 ysthakur + 15 Fengyun Liu + 15 Paweł Marks + 14 Guillaume Martres + 14 Jamie Thompson + 11 Sébastien Doeraene + 9 Timothée Andres + 8 Kacper Korban + 7 Matt Bovel + 7 Som Snytt + 6 Julien Richard-Foy + 6 Lucas Leblanc + 5 Michał Pałka + 4 Anatolii Kmetiuk + 4 Guillaume Raffin + 4 Paul Coral + 4 Wojciech Mazur + 4 Yichen Xu + 3 Decel + 3 Jan Chyb + 2 Adrien Piquerez + 2 Arman Bilge + 2 Carl + 2 Florian3k + 2 Kenji Yoshida + 2 Michael Pilquist + 2 Natsu Kagami + 2 Seth Tisue + 2 Tomasz Godzik + 2 Vasil Vasilev + 2 Yadu Krishnan + 1 Bersier + 1 Flavio Brasil + 1 Jan-Pieter van den Heuvel + 1 Lukas Rytz + 1 Miles Yucht + 1 Mohammad Yousuf Minhaj Zia + 1 Ondra Pelech + 1 Philippus + 1 Rikito Taniguchi + 1 Simon R + 1 brandonspark + 1 github-actions[bot] + 1 liang3zy22 + 1 s.bazarsadaev + 1 Łukasz Wroński + +``` From dfb23f95afa8bec461674140c99b19ea3a9ab010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 24 May 2023 13:30:49 +0200 Subject: [PATCH 623/657] Release 3.3.1-RC1 --- tasty/src/dotty/tools/tasty/TastyFormat.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 226fc14acb39..39d559234868 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -290,7 +290,7 @@ object TastyFormat { * compatibility, but remains backwards compatible, with all * preceeding `MinorVersion`. */ - final val MinorVersion: Int = 4 + final val MinorVersion: Int = 3 /** Natural Number. The `ExperimentalVersion` allows for * experimentation with changes to TASTy without committing @@ -306,7 +306,7 @@ object TastyFormat { * is able to read final TASTy documents if the file's * `MinorVersion` is strictly less than the current value. */ - final val ExperimentalVersion: Int = 1 + final val ExperimentalVersion: Int = 0 /**This method implements a binary relation (`<:<`) between two TASTy versions. * From 8f019275f8dd54fc9554d00292e02fd331ce3427 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 30 May 2023 10:00:08 +0200 Subject: [PATCH 624/657] Dealias types in New before matching quotes Fixes #17606 --- .../scala/quoted/runtime/impl/QuoteMatcher.scala | 2 +- tests/pos-macros/i17606/Macros_1.scala | 14 ++++++++++++++ tests/pos-macros/i17606/Test_2.scala | 8 ++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/pos-macros/i17606/Macros_1.scala create mode 100644 tests/pos-macros/i17606/Test_2.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index 5477628a30a3..bfa4c1c6d1f2 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -301,7 +301,7 @@ object QuoteMatcher { /* Match new */ case New(tpt1) => pattern match - case New(tpt2) if tpt1.tpe.typeSymbol == tpt2.tpe.typeSymbol => matched + case New(tpt2) if tpt1.tpe.dealias.typeSymbol == tpt2.tpe.dealias.typeSymbol => matched case _ => notMatched /* Match this */ diff --git a/tests/pos-macros/i17606/Macros_1.scala b/tests/pos-macros/i17606/Macros_1.scala new file mode 100644 index 000000000000..245f2df66e7b --- /dev/null +++ b/tests/pos-macros/i17606/Macros_1.scala @@ -0,0 +1,14 @@ +package example + +import scala.quoted.* + +object A { + inline def f(inline a: Any): Boolean = ${ impl('a) } + + def impl(a: Expr[Any])(using Quotes): Expr[Boolean] = { + a match { + case '{ new String($x: Array[Byte]) } => Expr(true) + case _ => quotes.reflect.report.errorAndAbort("Expected match", a) + } + } +} diff --git a/tests/pos-macros/i17606/Test_2.scala b/tests/pos-macros/i17606/Test_2.scala new file mode 100644 index 000000000000..ebf535bc2ae9 --- /dev/null +++ b/tests/pos-macros/i17606/Test_2.scala @@ -0,0 +1,8 @@ +package example + +object Main { + def main(args: Array[String]): Unit = { + val x = A.f(new String(Array.empty[Byte])) + println(x) + } +} From 49680df3ecead55d5a53b065dbdf3f06b80b3812 Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 25 Jun 2023 20:11:55 +0200 Subject: [PATCH 625/657] Fix accessibleType for package object prefixes Making a package object explicit re-computes the denotations of an overloaded method. So it should not be done after we have pruned down those denotations by an accessibility test. We now do it before checking accessibility. Fixes #15821 --- .../dotty/tools/dotc/core/Denotations.scala | 4 +-- .../dotty/tools/dotc/typer/TypeAssigner.scala | 30 +++++++++++-------- tests/pos/i15821.scala | 9 ++++++ 3 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 tests/pos/i15821.scala diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 82368fd4dbf5..e56cc453d34d 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1269,8 +1269,8 @@ object Denotations { def hasAltWith(p: SingleDenotation => Boolean): Boolean = denot1.hasAltWith(p) || denot2.hasAltWith(p) def accessibleFrom(pre: Type, superAccess: Boolean)(using Context): Denotation = { - val d1 = denot1 accessibleFrom (pre, superAccess) - val d2 = denot2 accessibleFrom (pre, superAccess) + val d1 = denot1.accessibleFrom(pre, superAccess) + val d2 = denot2.accessibleFrom(pre, superAccess) if (!d1.exists) d2 else if (!d2.exists) d1 else derivedUnionDenotation(d1, d2) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6ac45cbcf04d..be6121e13209 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -77,21 +77,25 @@ trait TypeAssigner { * (2) in Java compilation units, `Object` is replaced by `defn.FromJavaObjectType` */ def accessibleType(tpe: Type, superAccess: Boolean)(using Context): Type = - tpe match + if ctx.isJava && tpe.isAnyRef then + defn.FromJavaObjectType + else tpe match case tpe: NamedType => - val pre = tpe.prefix - val name = tpe.name - def postProcess(d: Denotation) = - if ctx.isJava && tpe.isAnyRef then defn.FromJavaObjectType - else TypeOps.makePackageObjPrefixExplicit(tpe withDenot d) - val d = tpe.denot.accessibleFrom(pre, superAccess) - if d.exists then postProcess(d) + val tpe1 = TypeOps.makePackageObjPrefixExplicit(tpe) + if tpe1 ne tpe then + accessibleType(tpe1, superAccess) else - // it could be that we found an inaccessible private member, but there is - // an inherited non-private member with the same name and signature. - val d2 = pre.nonPrivateMember(name).accessibleFrom(pre, superAccess) - if reallyExists(d2) then postProcess(d2) - else NoType + val pre = tpe.prefix + val name = tpe.name + val d = tpe.denot.accessibleFrom(pre, superAccess) + if d eq tpe.denot then tpe + else if d.exists then tpe.withDenot(d) + else + // it could be that we found an inaccessible private member, but there is + // an inherited non-private member with the same name and signature. + val d2 = pre.nonPrivateMember(name).accessibleFrom(pre, superAccess) + if reallyExists(d2) then tpe.withDenot(d2) + else NoType case tpe => tpe /** Try to make `tpe` accessible, emit error if not possible */ diff --git a/tests/pos/i15821.scala b/tests/pos/i15821.scala new file mode 100644 index 000000000000..a72d13e07bc7 --- /dev/null +++ b/tests/pos/i15821.scala @@ -0,0 +1,9 @@ +def main = + foo.bar(42) + foo.bar + +package object foo { + def bar[F[_]]: Unit = ??? + def bar[F[_]](x: Int): Unit = ??? + private[foo] def bar[F[_]](x: Int)(implicit dummy: DummyImplicit): Unit = ??? +} From 186e4be054c0df229e4a97152635df788432a876 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 26 Jun 2023 14:53:36 +0200 Subject: [PATCH 626/657] Disable specs2 for now. --- .../scala/dotty/communitybuild/CommunityBuildTest.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 146ad6f4f951..8837f7319117 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -93,7 +93,12 @@ class CommunityBuildTestC: @Test def sconfig = projects.sconfig.run() @Test def shapeless = projects.shapeless.run() @Test def sourcecode = projects.sourcecode.run() - @Test def specs2 = projects.specs2.run() + + // Disabled. Currently fails in FutureMatchers.scala. The call to + // `checkResultFailure` goes to a protected method which is not accessible. + // I tried to fix it, but get test failures. + // @Test def specs2 = projects.specs2.run() + @Test def stdLib213 = projects.stdLib213.run() @Test def ujson = projects.ujson.run() @Test def upickle = projects.upickle.run() From 1451dc50ccc7bdf52501af02052aa1059487e393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 28 Jun 2023 15:36:01 +0200 Subject: [PATCH 627/657] Add changelog for 3.3.1-RC2 --- changelogs/3.3.1-RC2.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 changelogs/3.3.1-RC2.md diff --git a/changelogs/3.3.1-RC2.md b/changelogs/3.3.1-RC2.md new file mode 100644 index 000000000000..f21bfa074b66 --- /dev/null +++ b/changelogs/3.3.1-RC2.md @@ -0,0 +1,16 @@ +# Backported fixes + +- Dealias types in `New`` before matching quotes [#17615](https://github.com/lampepfl/dotty/pull/17615) +- Fix `accessibleType` for package object prefixes [#18057](https://github.com/lampepfl/dotty/pull/18057) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC1..3.3.1-RC2` these are: + +``` + 2 Martin Odersky + 2 Paweł Marks + 1 Nicolas Stucki +``` From c9bbcb0f0297b6097eff0dd28f9d5a5cae290e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 28 Jun 2023 15:52:38 +0200 Subject: [PATCH 628/657] Release 3.3.1-RC2 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 910ee7ef4f58..a2ea5ce1a596 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC1" + val baseVersion = "3.3.1-RC2" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.0" + val previousDottyVersion = "3.3.1-RC1" object CompatMode { final val BinaryCompatible = 0 From aed47fd78aec49aebf7d2d97b167e58c9c165cc2 Mon Sep 17 00:00:00 2001 From: odersky Date: Wed, 5 Jul 2023 00:14:35 +0200 Subject: [PATCH 629/657] Add clause for protected visibility from package objects We usually have an access rule that the access to a protected member `foo` in class `C` must be from somewhere nested in a subclass of `C`. But that fails if the member is accessed from a package object `p.package`. In that case, the access does not need to be in the same object, it just has to be in package `p`. This clause was previously missing and is now added. Why was this only recently discovered? #18057 fixed an issue where toplevel protected members were always accessible because explicit package object prefixes were added after the accessibility check was done, and would re-establish the previous members without doing an accessibility check. The fix was done by adding package objects first, then doing he rest of the checks. But that also means that protected toplevel objects now get checked as members of their synthetic package object instead of as members of their package. The change here also makes specs2 compile again. --- .../dotty/communitybuild/CommunityBuildTest.scala | 6 +----- .../dotty/tools/dotc/core/SymDenotations.scala | 11 +++++++---- tests/pos/i18124/definition.scala | 15 +++++++++++++++ tests/pos/i18124/usage.scala | 8 ++++++++ 4 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 tests/pos/i18124/definition.scala create mode 100644 tests/pos/i18124/usage.scala diff --git a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala index 8837f7319117..bf6b6d431509 100644 --- a/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala +++ b/community-build/test/scala/dotty/communitybuild/CommunityBuildTest.scala @@ -93,11 +93,7 @@ class CommunityBuildTestC: @Test def sconfig = projects.sconfig.run() @Test def shapeless = projects.shapeless.run() @Test def sourcecode = projects.sourcecode.run() - - // Disabled. Currently fails in FutureMatchers.scala. The call to - // `checkResultFailure` goes to a protected method which is not accessible. - // I tried to fix it, but get test failures. - // @Test def specs2 = projects.specs2.run() + @Test def specs2 = projects.specs2.run() @Test def stdLib213 = projects.stdLib213.run() @Test def ujson = projects.ujson.run() diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index aa97435d64bb..988a37be4388 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -907,10 +907,13 @@ object SymDenotations { false val cls = owner.enclosingSubClass if !cls.exists then - val encl = if ctx.owner.isConstructor then ctx.owner.enclosingClass.owner.enclosingClass else ctx.owner.enclosingClass - fail(i""" - | Access to protected $this not permitted because enclosing ${encl.showLocated} - | is not a subclass of ${owner.showLocated} where target is defined""") + if pre.termSymbol.isPackageObject && accessWithin(pre.termSymbol.owner) then + true + else + val encl = if ctx.owner.isConstructor then ctx.owner.enclosingClass.owner.enclosingClass else ctx.owner.enclosingClass + fail(i""" + | Access to protected $this not permitted because enclosing ${encl.showLocated} + | is not a subclass of ${owner.showLocated} where target is defined""") else if isType || pre.derivesFrom(cls) || isConstructor || owner.is(ModuleClass) then // allow accesses to types from arbitrary subclasses fixes #4737 // don't perform this check for static members diff --git a/tests/pos/i18124/definition.scala b/tests/pos/i18124/definition.scala new file mode 100644 index 000000000000..1377c94fe7cd --- /dev/null +++ b/tests/pos/i18124/definition.scala @@ -0,0 +1,15 @@ +// definition.scala +package oolong.bson: + + trait BsonValue + protected def merge( + base: BsonValue, + patch: BsonValue, + arraySubvalues: Boolean = false + ): BsonValue = ??? + + private def foo: Int = 1 + + package inner: + protected[bson] def bar = 2 + diff --git a/tests/pos/i18124/usage.scala b/tests/pos/i18124/usage.scala new file mode 100644 index 000000000000..0bc0417c01ad --- /dev/null +++ b/tests/pos/i18124/usage.scala @@ -0,0 +1,8 @@ +// usage.scala +package oolong.bson + +extension (bv: BsonValue) + def :+(other: BsonValue): BsonValue = merge(other, bv, false) + +val x = foo +val y = inner.bar From 9cae4e8af5a54916b9fa128e620bd53cf54d3e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 5 Jul 2023 17:58:03 +0200 Subject: [PATCH 630/657] Add changelog for 3.3.1-RC3 --- changelogs/3.3.1-RC3.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 changelogs/3.3.1-RC3.md diff --git a/changelogs/3.3.1-RC3.md b/changelogs/3.3.1-RC3.md new file mode 100644 index 000000000000..006d887c4f49 --- /dev/null +++ b/changelogs/3.3.1-RC3.md @@ -0,0 +1,15 @@ +# Backported fixes + +- Add clause for protected visibility from package objects [#18134](https://github.com/lampepfl/dotty/pull/18134) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC2..3.3.1-RC3` these are: + +``` + 2 Paweł Marks + 1 Martin Odersky + +``` From 161de6e8e7b0e8f4fd59406bc9c1b9c79c6a634b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Wed, 5 Jul 2023 17:59:29 +0200 Subject: [PATCH 631/657] Release 3.3.1-RC3 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index a2ea5ce1a596..1d4f2c7350a5 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC2" + val baseVersion = "3.3.1-RC3" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.1-RC1" + val previousDottyVersion = "3.3.1-RC2" object CompatMode { final val BinaryCompatible = 0 From 011e6674f0b79a84c4b68a749c73b0c91d7c036e Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 9 May 2023 08:54:12 +0200 Subject: [PATCH 632/657] Revert "Include top-level symbols from same file in outer ambiguity error" This reverts commit 7d4e103a941a30306ddde28a11f8bc3a8841acf8. Closes #17433 --- .../src/dotty/tools/dotc/typer/Typer.scala | 19 ++++------------ tests/neg/ambiref.check | 16 -------------- tests/neg/ambiref.scala | 22 +------------------ tests/pos-special/fatal-warnings/i9260.scala | 2 +- tests/run/protectedacc.scala | 2 +- 5 files changed, 7 insertions(+), 54 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2e7444af8e96..cb23262d1410 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -408,16 +408,11 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer // Does reference `tp` refer only to inherited symbols? def isInherited(denot: Denotation) = def isCurrent(mbr: SingleDenotation): Boolean = - !mbr.symbol.exists || mbr.symbol.owner == ctx.owner || ctx.owner.is(Package) + !mbr.symbol.exists || mbr.symbol.owner == ctx.owner denot match case denot: SingleDenotation => !isCurrent(denot) case denot => !denot.hasAltWith(isCurrent) - /* It is an error if an identifier x is available as an inherited member in an inner scope - * and the same name x is defined in an outer scope in the same source file, unless - * the inherited member (has an overloaded alternative that) coincides with - * (an overloaded alternative of) the definition x. - */ def checkNoOuterDefs(denot: Denotation, last: Context, prevCtx: Context): Unit = def sameTermOrType(d1: SingleDenotation, d2: Denotation) = d2.containsSym(d1.symbol) || d2.hasUniqueSym && { @@ -434,15 +429,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val owner = outer.owner if (owner eq last.owner) && (outer.scope eq last.scope) then checkNoOuterDefs(denot, outer, prevCtx) - else if !owner.isRoot then - val found = - if owner.is(Package) then - owner.denot.asClass.membersNamed(name) - .filterWithPredicate(d => !d.symbol.is(Package) && d.symbol.source == denot.symbol.source) - else - val scope = if owner.isClass then owner.info.decls else outer.scope - scope.denotsNamed(name) - val competing = found.filterWithFlags(required, excluded | Synthetic) + else if !owner.is(Package) then + val scope = if owner.isClass then owner.info.decls else outer.scope + val competing = scope.denotsNamed(name).filterWithFlags(required, excluded) if competing.exists then val symsMatch = competing .filterWithPredicate(sd => sameTermOrType(sd, denot)) diff --git a/tests/neg/ambiref.check b/tests/neg/ambiref.check index 32b4078f1346..5d701b3b3b71 100644 --- a/tests/neg/ambiref.check +++ b/tests/neg/ambiref.check @@ -30,19 +30,3 @@ | and inherited subsequently in class E | | longer explanation available when compiling with `-explain` --- [E049] Reference Error: tests/neg/ambiref.scala:43:10 --------------------------------------------------------------- -43 | println(global) // error - | ^^^^^^ - | Reference to global is ambiguous. - | It is both defined in package - | and inherited subsequently in object D - | - | longer explanation available when compiling with `-explain` --- [E049] Reference Error: tests/neg/ambiref.scala:49:16 --------------------------------------------------------------- -49 | def t = new T { } // error - | ^ - | Reference to T is ambiguous. - | It is both defined in package p - | and inherited subsequently in class C - | - | longer explanation available when compiling with `-explain` diff --git a/tests/neg/ambiref.scala b/tests/neg/ambiref.scala index bb48997cd465..e7a5d5efbd7e 100644 --- a/tests/neg/ambiref.scala +++ b/tests/neg/ambiref.scala @@ -40,24 +40,4 @@ val global = 0 class C: val global = 1 object D extends C: - println(global) // error - -package p: - class T - trait P { trait T } - class C extends P: - def t = new T { } // error - -package scala: - trait P { trait Option[+A] } - class C extends P: - def t = new Option[String] { } // OK, competing scala.Option is not defined in the same compilation unit - -object test5: - class Mu // generates a synthetic companion object with an apply method - trait A { - val Mu = 1 - } - trait B extends A { - def t = Mu // don't warn about synthetic companion - } + println(global) // OK, since global is defined in package \ No newline at end of file diff --git a/tests/pos-special/fatal-warnings/i9260.scala b/tests/pos-special/fatal-warnings/i9260.scala index 0392c1c96fa8..df548f393eea 100644 --- a/tests/pos-special/fatal-warnings/i9260.scala +++ b/tests/pos-special/fatal-warnings/i9260.scala @@ -10,7 +10,7 @@ end AstImpl object untpd extends AstImpl[Null]: - def DefDef(ast: this.Ast): DefDef = ast match + def DefDef(ast: Ast): DefDef = ast match case ast: DefDef => ast end untpd diff --git a/tests/run/protectedacc.scala b/tests/run/protectedacc.scala index 85aa3438faa3..a08e7201fd15 100644 --- a/tests/run/protectedacc.scala +++ b/tests/run/protectedacc.scala @@ -134,7 +134,7 @@ package p { abstract class X[T] extends PolyA[T] { - trait Inner extends this.B { + trait Inner extends B { def self: T; def self2: Node; def getB: Inner; From bf10893d6bc034c5aafccecd47d38b69c0d8f274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 11 Jul 2023 12:58:07 +0200 Subject: [PATCH 633/657] Add changelog for 3.3.1-RC4 --- changelogs/3.3.1-RC3.md | 2 +- changelogs/3.3.1-RC4.md | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 changelogs/3.3.1-RC4.md diff --git a/changelogs/3.3.1-RC3.md b/changelogs/3.3.1-RC3.md index 006d887c4f49..eb19f40b10dc 100644 --- a/changelogs/3.3.1-RC3.md +++ b/changelogs/3.3.1-RC3.md @@ -10,6 +10,6 @@ According to `git shortlog -sn --no-merges 3.3.1-RC2..3.3.1-RC3` these are: ``` 2 Paweł Marks - 1 Martin Odersky + 1 Nicolas Stucki ``` diff --git a/changelogs/3.3.1-RC4.md b/changelogs/3.3.1-RC4.md new file mode 100644 index 000000000000..7d95e0258fad --- /dev/null +++ b/changelogs/3.3.1-RC4.md @@ -0,0 +1,15 @@ +# Backported fixes + +- Revert "Include top-level symbols from same file in outer ambiguity error" [#17438](https://github.com/lampepfl/dotty/pull/17438) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC3..3.3.1-RC4` these are: + +``` + 2 Paweł Marks + 1 Nicolas Stucki + +``` From 555df5304aa882e67e08c917ff4924ab5947d295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 11 Jul 2023 13:00:08 +0200 Subject: [PATCH 634/657] Release 3.3.1-RC4 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 1d4f2c7350a5..a60932eb9e30 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC3" + val baseVersion = "3.3.1-RC4" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.1-RC2" + val previousDottyVersion = "3.3.1-RC3" object CompatMode { final val BinaryCompatible = 0 From e1233d80c5a870ebb51c72e4b26a8fc8004b3774 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 18 Jul 2023 14:32:36 +0200 Subject: [PATCH 635/657] Heal stage inconsistent prefixes of type projections Fixes #17293 --- compiler/src/dotty/tools/dotc/staging/HealType.scala | 2 +- tests/pos-macros/i17293.scala | 12 ++++++++++++ tests/pos-macros/i17293b.scala | 12 ++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/pos-macros/i17293.scala create mode 100644 tests/pos-macros/i17293b.scala diff --git a/compiler/src/dotty/tools/dotc/staging/HealType.scala b/compiler/src/dotty/tools/dotc/staging/HealType.scala index 023271960b40..7d3ca0ad2f63 100644 --- a/compiler/src/dotty/tools/dotc/staging/HealType.scala +++ b/compiler/src/dotty/tools/dotc/staging/HealType.scala @@ -46,7 +46,7 @@ class HealType(pos: SrcPos)(using Context) extends TypeMap { case prefix: TermRef if tp.symbol.isTypeSplice => checkNotWildcardSplice(tp) if level == 0 then tp else getTagRef(prefix) - case _: NamedType | _: ThisType | NoPrefix => + case _: TermRef | _: ThisType | NoPrefix => if levelInconsistentRootOfPath(tp).exists then tryHeal(tp) else diff --git a/tests/pos-macros/i17293.scala b/tests/pos-macros/i17293.scala new file mode 100644 index 000000000000..57eba1181903 --- /dev/null +++ b/tests/pos-macros/i17293.scala @@ -0,0 +1,12 @@ +import scala.quoted.* + +trait OuterTrait { + trait X +} + +def exampleMacro[T <: OuterTrait: Type](expr: Expr[T])(using Quotes): Expr[OuterTrait#X] = { + '{ + val prefix: T = ${ expr } + new prefix.X {} + } +} diff --git a/tests/pos-macros/i17293b.scala b/tests/pos-macros/i17293b.scala new file mode 100644 index 000000000000..a8b73ba6176b --- /dev/null +++ b/tests/pos-macros/i17293b.scala @@ -0,0 +1,12 @@ +import scala.quoted.* + +trait OuterTrait { self => + trait X + + def exampleMacro[T <: self.type: Type](expr: Expr[T])(using Quotes): Expr[self.X] = { + '{ + val prefix: T = ${ expr } + new prefix.X {} + } + } +} \ No newline at end of file From 5f2450aa8ae3c00f6f52eb6a2dbe2427fe0ae6a8 Mon Sep 17 00:00:00 2001 From: Jan Chyb Date: Tue, 25 Jul 2023 12:56:09 +0200 Subject: [PATCH 636/657] Fix regression with Overloaded methods returning Functions Before the regression, FunctionOf unapply would not try dealiasing, meaning that an aliased function type would be handled by a general case. To fix that, instead of handling Function types separately when filtering overloaded methods in `resolveOverloaded1`, we allow to fallback to the general case if the previous one returns nothing. Along with fixing the regression, this also improves other cases, one of which was added to the test. Readd a separate FunctionOf case, but with a fallback --- .../dotty/tools/dotc/typer/Applications.scala | 52 ++++++++++--------- tests/pos/i17245.scala | 20 +++++++ 2 files changed, 48 insertions(+), 24 deletions(-) create mode 100644 tests/pos/i17245.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index fbed4b77d3fe..cb6aec26406a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -2062,31 +2062,35 @@ trait Applications extends Compatibility { if isDetermined(alts2) then alts2 else resolveMapped(alts1, _.widen.appliedTo(targs1.tpes), pt1) - case defn.FunctionOf(args, resultType, _) => - narrowByTypes(alts, args, resultType) - case pt => - val compat = alts.filterConserve(normalizedCompatible(_, pt, keepConstraint = false)) - if (compat.isEmpty) - /* - * the case should not be moved to the enclosing match - * since SAM type must be considered only if there are no candidates - * For example, the second f should be chosen for the following code: - * def f(x: String): Unit = ??? - * def f: java.io.OutputStream = ??? - * new java.io.ObjectOutputStream(f) - */ - pt match { - case SAMType(mtp) => - narrowByTypes(alts, mtp.paramInfos, mtp.resultType) - case _ => - // pick any alternatives that are not methods since these might be convertible - // to the expected type, or be used as extension method arguments. - val convertible = alts.filterNot(alt => - normalize(alt, IgnoredProto(pt)).widenSingleton.isInstanceOf[MethodType]) - if convertible.length == 1 then convertible else compat - } - else compat + val compat0 = pt match + case defn.FunctionOf(args, resType, _) => + narrowByTypes(alts, args, resType) + case _ => + Nil + if (compat0.isEmpty) then + val compat = alts.filterConserve(normalizedCompatible(_, pt, keepConstraint = false)) + if (compat.isEmpty) + /* + * the case should not be moved to the enclosing match + * since SAM type must be considered only if there are no candidates + * For example, the second f should be chosen for the following code: + * def f(x: String): Unit = ??? + * def f: java.io.OutputStream = ??? + * new java.io.ObjectOutputStream(f) + */ + pt match { + case SAMType(mtp) => + narrowByTypes(alts, mtp.paramInfos, mtp.resultType) + case _ => + // pick any alternatives that are not methods since these might be convertible + // to the expected type, or be used as extension method arguments. + val convertible = alts.filterNot(alt => + normalize(alt, IgnoredProto(pt)).widenSingleton.isInstanceOf[MethodType]) + if convertible.length == 1 then convertible else compat + } + else compat + else compat0 } /** The type of alternative `alt` after instantiating its first parameter diff --git a/tests/pos/i17245.scala b/tests/pos/i17245.scala new file mode 100644 index 000000000000..3b5b3a74108d --- /dev/null +++ b/tests/pos/i17245.scala @@ -0,0 +1,20 @@ +import scala.reflect.ClassTag + +trait MockSettings + +object Mockito { + def mock[T : ClassTag]: T = ??? + def mock[T : ClassTag](settings: MockSettings): T = ??? +} + +trait Channel +type OnChannel = Channel => Any + +@main def Test = + val case1: OnChannel = Mockito.mock[OnChannel] + val case2: OnChannel = Mockito.mock + val case3 = Mockito.mock[OnChannel] + val case4: OnChannel = Mockito.mock[OnChannel](summon[ClassTag[OnChannel]]) + + // not a regressive case, but an added improvement with the fix for the above + val case5: Channel => Any = Mockito.mock[Channel => Any] From b85cbb5a3a64cd7b21bf6b2cbd3f75c0f11db8fd Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 17 Jul 2023 18:34:16 +0200 Subject: [PATCH 637/657] Disallow taking singleton types of packages again Fixes #18109 --- compiler/src/dotty/tools/dotc/typer/Checking.scala | 13 ++++++++----- tests/neg/i18109.scala | 11 +++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 tests/neg/i18109.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index b2ab5332c3b2..df5639b50302 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -748,13 +748,16 @@ object Checking { if sym.isNoValue && !ctx.isJava then report.error(JavaSymbolIsNotAValue(sym), tree.srcPos) + /** Check that `tree` refers to a value, unless `tree` is selected or applied + * (singleton types x.type don't count as selections). + */ def checkValue(tree: Tree, proto: Type)(using Context): tree.type = tree match - case tree: RefTree - if tree.name.isTermName - && !proto.isInstanceOf[SelectionProto] - && !proto.isInstanceOf[FunOrPolyProto] => - checkValue(tree) + case tree: RefTree if tree.name.isTermName => + proto match + case _: SelectionProto if proto ne SingletonTypeProto => // no value check + case _: FunOrPolyProto => // no value check + case _ => checkValue(tree) case _ => tree diff --git a/tests/neg/i18109.scala b/tests/neg/i18109.scala new file mode 100644 index 000000000000..7df13b0c36ff --- /dev/null +++ b/tests/neg/i18109.scala @@ -0,0 +1,11 @@ +package foo {} + +package bar { + object Test { + def qux[A] = 123 + def main(args: Array[String]): Unit = { + val y = qux[foo.type] // error + val x = valueOf[foo.type] // error + } + } +} \ No newline at end of file From 110c91f5831b24c6751b30ed45f76c436be9db04 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 7 Aug 2023 18:58:54 +0200 Subject: [PATCH 638/657] A slightly more conservative version of #14128 Two changes - Fix `hasUpperBound` to work correctly for higher-kinded types - A more conservative fix in `IsFullyDefinedAccumulator`. We now maintain the symmetry that - if variance < 0, we maximize - if variance > 0 (and Nothing is admissible) we minimize - only if variance = 0, we use the upper bound as a tie breaker Previously, we maximized even if variance > 0 if there was an upper but no lower bound. But that was asymmetric since there is no corresponding case where we minimize at variance < 0 if there is a lower but no upper bound. --- compiler/src/dotty/tools/dotc/core/Types.scala | 7 ++++++- compiler/src/dotty/tools/dotc/typer/Inferencing.scala | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 73a64f2c1b8f..bb4fd02816a6 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -246,6 +246,11 @@ object Types { case _ => false } + /** Is this type exactly `Any`, or a type lambda ending in `Any`? */ + def isTopOfSomeKind(using Context): Boolean = dealias match + case tp: TypeLambda => tp.resType.isTopOfSomeKind + case _ => isExactlyAny + def isBottomType(using Context): Boolean = if ctx.mode.is(Mode.SafeNulls) && !ctx.phase.erasedTypes then hasClassSymbol(defn.NothingClass) else isBottomTypeAfterErasure @@ -4813,7 +4818,7 @@ object Types { def hasLowerBound(using Context): Boolean = !currentEntry.loBound.isExactlyNothing /** For uninstantiated type variables: Is the upper bound different from Any? */ - def hasUpperBound(using Context): Boolean = !currentEntry.hiBound.finalResultType.isExactlyAny + def hasUpperBound(using Context): Boolean = !currentEntry.hiBound.isTopOfSomeKind /** Unwrap to instance (if instantiated) or origin (if not), until result * is no longer a TypeVar diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 0e1c41ceef74..4d027b8750e0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -187,7 +187,11 @@ object Inferencing { // else hold off instantiating unbounded unconstrained variable else if direction != 0 then instantiate(tvar, fromBelow = direction < 0) - else if variance >= 0 && (force.ifBottom == IfBottom.ok && !tvar.hasUpperBound || tvar.hasLowerBound) then + else if variance >= 0 && tvar.hasLowerBound then + instantiate(tvar, fromBelow = true) + else if (variance > 0 || variance == 0 && !tvar.hasUpperBound) + && force.ifBottom == IfBottom.ok + then // if variance == 0, prefer upper bound if one is given instantiate(tvar, fromBelow = true) else if variance >= 0 && force.ifBottom == IfBottom.fail then fail = true From 232c5f448f49406d6bf68ab4f5b230e4cf6aaf39 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 7 Aug 2023 11:05:44 +0100 Subject: [PATCH 639/657] Show Implicit Candidate & RefAndLevel --- compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala | 7 +++++++ compiler/src/dotty/tools/dotc/printing/Printer.scala | 5 ++++- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 6 ++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index f3540502597c..700b3fbf525f 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -640,6 +640,13 @@ class PlainPrinter(_ctx: Context) extends Printer { else if (pos.source.exists) s"${pos.source.file.name}:${pos.line + 1}" else s"(no source file, offset = ${pos.span.point})" + def toText(cand: Candidate): Text = + "Cand(" + ~ toTextRef(cand.ref) + ~ (if cand.isConversion then " conv" else "") + ~ (if cand.isExtension then " ext" else "") + ~ Str(" L" + cand.level) ~ ")" + def toText(result: SearchResult): Text = result match { case result: SearchSuccess => "SearchSuccess: " ~ toText(result.ref) ~ " via " ~ toText(result.tree) diff --git a/compiler/src/dotty/tools/dotc/printing/Printer.scala b/compiler/src/dotty/tools/dotc/printing/Printer.scala index 697ab063a646..ab0c867ec31f 100644 --- a/compiler/src/dotty/tools/dotc/printing/Printer.scala +++ b/compiler/src/dotty/tools/dotc/printing/Printer.scala @@ -7,7 +7,7 @@ import Texts._, ast.Trees._ import Types.{Type, SingletonType, LambdaParam}, Symbols.Symbol, Scopes.Scope, Constants.Constant, Names.Name, Denotations._, Annotations.Annotation, Contexts.Context -import typer.Implicits.SearchResult +import typer.Implicits.* import util.SourcePosition import typer.ImportInfo @@ -153,6 +153,9 @@ abstract class Printer { /** Textual representation of source position */ def toText(pos: SourcePosition): Text + /** Textual representation of implicit candidates. */ + def toText(cand: Candidate): Text + /** Textual representation of implicit search result */ def toText(result: SearchResult): Text diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index c6795ed25a0e..66f400d7eae0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -49,17 +49,19 @@ object Implicits: } /** Both search candidates and successes are references with a specific nesting level. */ - sealed trait RefAndLevel { + sealed trait RefAndLevel extends Showable { def ref: TermRef def level: Int } /** An eligible implicit candidate, consisting of an implicit reference and a nesting level */ - case class Candidate(implicitRef: ImplicitRef, kind: Candidate.Kind, level: Int) extends RefAndLevel { + case class Candidate(implicitRef: ImplicitRef, kind: Candidate.Kind, level: Int) extends RefAndLevel with Showable { def ref: TermRef = implicitRef.underlyingRef def isExtension = (kind & Candidate.Extension) != 0 def isConversion = (kind & Candidate.Conversion) != 0 + + def toText(printer: Printer): Text = printer.toText(this) } object Candidate { type Kind = Int From 48c994c7e82c2fe4be4e7bfa294ce5afc3148270 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Mon, 24 Jul 2023 15:34:17 +0100 Subject: [PATCH 640/657] Record failures to adapt application arguments --- .../tools/dotc/core/OrderingConstraint.scala | 4 +- .../dotty/tools/dotc/typer/Applications.scala | 2 +- tests/neg-macros/i6762.scala | 2 +- tests/neg/enum-values.check | 8 ++-- tests/neg/enumsAccess.scala | 2 +- tests/neg/i6779.check | 2 +- tests/neg/recursive-lower-constraint.scala | 2 +- tests/neg/syntax-error-recovery.check | 6 --- tests/neg/syntax-error-recovery.scala | 2 +- tests/pos/i18163.orig.scala | 40 +++++++++++++++++++ tests/pos/i18163.scala | 21 ++++++++++ 11 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 tests/pos/i18163.orig.scala create mode 100644 tests/pos/i18163.scala diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index faea30390d2b..0328cea9b3ca 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -344,7 +344,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds, if newSet.isEmpty then deps.remove(referenced) else deps.updated(referenced, newSet) - def traverse(t: Type) = t match + def traverse(t: Type) = try + t match case param: TypeParamRef => if hasBounds(param) then if variance >= 0 then coDeps = update(coDeps, param) @@ -356,6 +357,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, seen += tp traverse(tp.ref) case _ => traverseChildren(t) + catch case ex: Throwable => handleRecursive("adjust", t.show, ex) end Adjuster /** Adjust dependencies to account for the delta of previous entry `prevEntry` diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index fbed4b77d3fe..a9376444a911 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -844,7 +844,7 @@ trait Applications extends Compatibility { var typedArgs = typedArgBuf.toList def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later val app1 = - if (!success) app0.withType(UnspecifiedErrorType) + if (!success || typedArgs.exists(_.tpe.isError)) app0.withType(UnspecifiedErrorType) else { if !sameSeq(args, orderedArgs) && !isJavaAnnotConstr(methRef.symbol) diff --git a/tests/neg-macros/i6762.scala b/tests/neg-macros/i6762.scala index a8df289b26c2..054945e213d6 100644 --- a/tests/neg-macros/i6762.scala +++ b/tests/neg-macros/i6762.scala @@ -2,4 +2,4 @@ import scala.quoted.* type G[X] case class Foo[T](x: T) -def f(word: String)(using Quotes): Expr[Foo[G[String]]] = '{Foo(${Expr(word)})} // error // error +def f(word: String)(using Quotes): Expr[Foo[G[String]]] = '{Foo(${Expr(word)})} // error diff --git a/tests/neg/enum-values.check b/tests/neg/enum-values.check index 37990e8f312e..23337de1b2c4 100644 --- a/tests/neg/enum-values.check +++ b/tests/neg/enum-values.check @@ -24,8 +24,8 @@ | | failed with: | - | Found: Array[example.Tag[?]] - | Required: Array[example.ListLike[?]] + | Found: example.ListLike.type + | Required: Nothing -- [E008] Not Found Error: tests/neg/enum-values.scala:34:52 ----------------------------------------------------------- 34 | val typeCtorsK: Array[TypeCtorsK[?]] = TypeCtorsK.values // error | ^^^^^^^^^^^^^^^^^ @@ -38,8 +38,8 @@ | | failed with: | - | Found: Array[example.Tag[?]] - | Required: Array[example.TypeCtorsK[?[_$1]]] + | Found: example.TypeCtorsK.type + | Required: Nothing -- [E008] Not Found Error: tests/neg/enum-values.scala:36:6 ------------------------------------------------------------ 36 | Tag.valueOf("Int") // error | ^^^^^^^^^^^ diff --git a/tests/neg/enumsAccess.scala b/tests/neg/enumsAccess.scala index 18b91b346b6a..8a8e9af8910f 100644 --- a/tests/neg/enumsAccess.scala +++ b/tests/neg/enumsAccess.scala @@ -63,7 +63,7 @@ object test5 { enum E5[T](x: T) { case C3() extends E5[INT](defaultX)// error: illegal reference // error: illegal reference case C4 extends E5[INT](defaultX) // error: illegal reference // error: illegal reference - case C5 extends E5[E5[_]](E5.this) // error: type mismatch + case C5 extends E5[E5[_]](E5.this) // error: cannot be instantiated // error: conflicting base types // error: type mismatch } object E5 { diff --git a/tests/neg/i6779.check b/tests/neg/i6779.check index 8e05c22eb640..f1e1b9d5557b 100644 --- a/tests/neg/i6779.check +++ b/tests/neg/i6779.check @@ -11,7 +11,7 @@ | value f is not a member of T. | An extension method was tried, but could not be fully constructed: | - | Test.f[G[T]](x)(given_Stuff) + | Test.f[G[T]](x) | | failed with: | diff --git a/tests/neg/recursive-lower-constraint.scala b/tests/neg/recursive-lower-constraint.scala index 8009ab5fce6e..cf45d8b95171 100644 --- a/tests/neg/recursive-lower-constraint.scala +++ b/tests/neg/recursive-lower-constraint.scala @@ -3,5 +3,5 @@ class Bar extends Foo[Bar] class A { def foo[T <: Foo[T], U >: Foo[T] <: T](x: T): T = x - foo(new Bar) // error + foo(new Bar) // error // error } diff --git a/tests/neg/syntax-error-recovery.check b/tests/neg/syntax-error-recovery.check index 0bf626210fed..18d877833d79 100644 --- a/tests/neg/syntax-error-recovery.check +++ b/tests/neg/syntax-error-recovery.check @@ -94,12 +94,6 @@ | Not found: bam | | longer explanation available when compiling with `-explain` --- [E006] Not Found Error: tests/neg/syntax-error-recovery.scala:61:10 ------------------------------------------------- -61 | println(bam) // error - | ^^^ - | Not found: bam - | - | longer explanation available when compiling with `-explain` -- [E129] Potential Issue Warning: tests/neg/syntax-error-recovery.scala:7:2 ------------------------------------------- 6 | 2 7 | } diff --git a/tests/neg/syntax-error-recovery.scala b/tests/neg/syntax-error-recovery.scala index 775abeb97bdb..b6663cc9c70a 100644 --- a/tests/neg/syntax-error-recovery.scala +++ b/tests/neg/syntax-error-recovery.scala @@ -58,5 +58,5 @@ object Test2: def foo5(x: Int) = foo2(foo2(,) // error // error - println(bam) // error + println(bam) // error \ No newline at end of file diff --git a/tests/pos/i18163.orig.scala b/tests/pos/i18163.orig.scala new file mode 100644 index 000000000000..eb0627254156 --- /dev/null +++ b/tests/pos/i18163.orig.scala @@ -0,0 +1,40 @@ +import scala.language.implicitConversions + +// We do have 2 `contramap` functions, one provided via `LoggerSyntax` other via `Contravariant.Ops` +// `ContravariantMonoidal` given instances are not used, and they do not match our type. Code fails when we have at least 2 instances of them +// Removal of `import catsSyntax._` allow to compile code +// Removal of `import odinSyntax.LoggerSyntax` and remaining `catsSyntax` would fail to compile the `def fails` + +trait Foo[A] +trait Bar[A] + +trait WriterT[F[_]: Contravariant, L, V]: + def contramap[Z](fn: Z => V): WriterT[F, L, Z] = ??? +trait Logger[F[_]] +class WriterTLogger[F[_]] extends Logger[[G] =>> WriterT[F, List[String], G]] + +trait ContravariantMonoidal[F[_]] extends Invariant[F] with Contravariant[F] +trait Invariant[F[_]] +object Invariant: + given ContravariantMonoidal[Foo] = ??? + given ContravariantMonoidal[Bar] = ??? + +trait Contravariant[F[_]] extends Invariant[F] +object Contravariant: + trait Ops[F[_], A]: + def contramap[B](f: B => A): F[B] = ??? + +object catsSyntax: + implicit def toContravariantOps[F[_]: Contravariant, A](target: F[A]): Contravariant.Ops[F, A] = ??? + +object odinSyntax: + implicit class LoggerSyntax[F[_]](logger: Logger[F]): + def contramap(f: String => String): Logger[F] = ??? + +import catsSyntax._ +import odinSyntax.LoggerSyntax + +class Test: + def fails = new WriterTLogger[Option].contramap(identity) + def works = LoggerSyntax(new WriterTLogger[Option]).contramap(identity) + diff --git a/tests/pos/i18163.scala b/tests/pos/i18163.scala new file mode 100644 index 000000000000..5c364a50dd57 --- /dev/null +++ b/tests/pos/i18163.scala @@ -0,0 +1,21 @@ +import scala.language.implicitConversions + +trait Foo[A] +trait Bar[B] +trait Qux[C] +class Log[K[_]] + +trait Inv[F[_]] +object Inv: + given monFoo: Inv[Foo] = ??? + given monBar: Inv[Bar] = ??? + +trait InvOps[H[_], D] { def desc(s: String): H[D] = ??? } +trait LogOps[L[_]] { def desc(s: String): Log[L] = ??? } + +class Test: + implicit def LogOps[Q[_]](l: Log[Q]): LogOps[Q] = ??? + implicit def InvOps[J[_], E](j11: J[E])(implicit z: Inv[J]): InvOps[J, E] = ??? + + def fails = new Log[Qux].desc("fails") + def works = LogOps[Qux](new Log[Qux]).desc("works") From c569a4f4c691c8eaf5536cd90e3935553932b8fd Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 1 Aug 2023 14:27:06 +0100 Subject: [PATCH 641/657] Space: Fix intersectUnrelatedAtomicTypes tracing --- compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 7238756454b3..002e3646c663 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -330,7 +330,7 @@ object SpaceEngine { * The types should be atomic (non-decomposable) and unrelated (neither * should be a subtype of the other). */ - def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(sp: Space)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug) { + def intersectUnrelatedAtomicTypes(tp1: Type, tp2: Type)(sp: Space)(using Context): Space = trace(i"atomic intersection: ${AndType(tp1, tp2)}", debug, show) { // Precondition: !isSubType(tp1, tp2) && !isSubType(tp2, tp1). if !ctx.mode.is(Mode.SafeNulls) && (tp1.isNullType || tp2.isNullType) then // Since projections of types don't include null, intersection with null is empty. From 518c02055f3addd2b4ea08ebaa6ac9c3ae65392e Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 1 Aug 2023 14:47:31 +0100 Subject: [PATCH 642/657] Space: Make isDecomposableToChildren ignore type constructors --- .../src/dotty/tools/dotc/transform/patmat/Space.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 002e3646c663..467b36df3805 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -642,7 +642,7 @@ object SpaceEngine { // For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`. parts.map(tp.derivedAppliedType(_, targs)) - case tp if tp.classSymbol.isDecomposableToChildren => + case tp if tp.isDecomposableToChildren => def getChildren(sym: Symbol): List[Symbol] = sym.children.flatMap { child => if child eq sym then List(sym) // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz... @@ -678,8 +678,8 @@ object SpaceEngine { rec(tp, Nil) } - extension (cls: Symbol) - /** A type is decomposable to children if it's sealed, + extension (tp: Type) + /** A type is decomposable to children if it has a simple kind, it's sealed, * abstract (or a trait) - so its not a sealed concrete class that can be instantiated on its own, * has no anonymous children, which we wouldn't be able to name as counter-examples, * but does have children. @@ -688,7 +688,8 @@ object SpaceEngine { * A sealed trait with subclasses that then get removed after `refineUsingParent`, decomposes to the empty list. * So that's why we consider whether a type has children. */ def isDecomposableToChildren(using Context): Boolean = - cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) && !cls.hasAnonymousChild && cls.children.nonEmpty + val cls = tp.classSymbol + tp.hasSimpleKind && cls.is(Sealed) && cls.isOneOf(AbstractOrTrait) && !cls.hasAnonymousChild && cls.children.nonEmpty val ListOfNoType = List(NoType) val ListOfTypNoType = ListOfNoType.map(Typ(_, decomposed = true)) From 86782076c45e88fc16c3abb0ca8646ce4a2dd417 Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 1 Aug 2023 16:00:31 +0100 Subject: [PATCH 643/657] Space: Revert how invariant targs are erased to fix regression The motivating case (i16451) is complicated, because it involves unchecked type arguments. To fix the regression, I'm reverting the fix. --- .../tools/dotc/transform/patmat/Space.scala | 13 ++---------- .../suppressed-type-test-warnings.scala | 2 ++ .../isInstanceOf/enum-approx2.scala | 2 ++ .../neg-custom-args/isInstanceOf/i11178.scala | 1 + .../neg-custom-args/isInstanceOf/i8932.scala | 1 + tests/{ => pending}/neg/i16451.check | 0 tests/{ => pending}/neg/i16451.scala | 4 ---- tests/pos/i17230.bootstrap.scala | 16 +++++++++++++++ tests/pos/i17230.min1.scala | 15 ++++++++++++++ tests/pos/i17230.orig.scala | 20 +++++++++++++++++++ 10 files changed, 59 insertions(+), 15 deletions(-) rename tests/{ => pending}/neg/i16451.check (100%) rename tests/{ => pending}/neg/i16451.scala (93%) create mode 100644 tests/pos/i17230.bootstrap.scala create mode 100644 tests/pos/i17230.min1.scala create mode 100644 tests/pos/i17230.orig.scala diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 467b36df3805..eab65890c227 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -468,17 +468,8 @@ object SpaceEngine { WildcardType case tp @ AppliedType(tycon, args) => - val args2 = - if tycon.isRef(defn.ArrayClass) then - args.map(arg => erase(arg, inArray = true, isValue = false)) - else tycon.typeParams.lazyZip(args).map { (tparam, arg) => - if isValue && tparam.paramVarianceSign == 0 then - // when matching against a value, - // any type argument for an invariant type parameter will be unchecked, - // meaning it won't fail to match against anything; thus the wildcard replacement - WildcardType - else erase(arg, inArray = false, isValue = false) - } + val inArray = tycon.isRef(defn.ArrayClass) + val args2 = args.map(arg => erase(arg, inArray = inArray, isValue = false)) tp.derivedAppliedType(erase(tycon, inArray, isValue = false), args2) case tp @ OrType(tp1, tp2) => diff --git a/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala b/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala index 175096fc6b21..92d86b3307e5 100644 --- a/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala +++ b/tests/neg-custom-args/fatal-warnings/suppressed-type-test-warnings.scala @@ -18,10 +18,12 @@ object Test { def err2[A, B](value: Foo[A, B], a: A => Int): B = value match { case b: Bar[B] => // spurious // error b.x + case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } def fail[A, B](value: Foo[A, B], a: A => Int): B = value match { case b: Bar[Int] => // error b.x + case _ => ??? // avoid fatal inexhaustivity warnings suppressing the uncheckable warning } } diff --git a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala index 5e3bdef7553d..c7c8a6c4e1fb 100644 --- a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala +++ b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala @@ -4,5 +4,7 @@ case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B] class Test { def eval(e: Fun[Int, Int]) = e match { case Fun(x: Fun[Int, Double]) => ??? // error + case Fun(x: Exp[Int => String]) => ??? // error + case _ => } } diff --git a/tests/neg-custom-args/isInstanceOf/i11178.scala b/tests/neg-custom-args/isInstanceOf/i11178.scala index 71bc346e5743..47e8b4c3acab 100644 --- a/tests/neg-custom-args/isInstanceOf/i11178.scala +++ b/tests/neg-custom-args/isInstanceOf/i11178.scala @@ -12,6 +12,7 @@ object Test1 { def test[A](bar: Bar[A]) = bar match { case _: Bar[Boolean] => ??? // error + case _ => ??? } } diff --git a/tests/neg-custom-args/isInstanceOf/i8932.scala b/tests/neg-custom-args/isInstanceOf/i8932.scala index e070fdae518c..84d2f7d4990a 100644 --- a/tests/neg-custom-args/isInstanceOf/i8932.scala +++ b/tests/neg-custom-args/isInstanceOf/i8932.scala @@ -6,6 +6,7 @@ class Dummy extends Bar[Nothing] with Foo[String] def bugReport[A](foo: Foo[A]): Foo[A] = foo match { case bar: Bar[A] => bar // error + case dummy: Dummy => ??? } def test = bugReport(new Dummy: Foo[String]) diff --git a/tests/neg/i16451.check b/tests/pending/neg/i16451.check similarity index 100% rename from tests/neg/i16451.check rename to tests/pending/neg/i16451.check diff --git a/tests/neg/i16451.scala b/tests/pending/neg/i16451.scala similarity index 93% rename from tests/neg/i16451.scala rename to tests/pending/neg/i16451.scala index 685b79477bbe..49997d2bcf92 100644 --- a/tests/neg/i16451.scala +++ b/tests/pending/neg/i16451.scala @@ -1,10 +1,6 @@ // scalac: -Werror enum Color: case Red, Green -//sealed trait Color -//object Color: -// case object Red extends Color -// case object Green extends Color case class Wrapper[A](value: A) diff --git a/tests/pos/i17230.bootstrap.scala b/tests/pos/i17230.bootstrap.scala new file mode 100644 index 000000000000..ef2d98d8f55b --- /dev/null +++ b/tests/pos/i17230.bootstrap.scala @@ -0,0 +1,16 @@ +type Untyped = Type | Null + +class Type +abstract class SearchFailureType extends Type + +abstract class Tree[+T <: Untyped]: + def tpe: T = null.asInstanceOf[T] + +class SearchFailureIdent[+T <: Untyped] extends Tree[T] + +class Test_i17230_bootstrap: + def t1(arg: Tree[Type]) = arg match + case arg: SearchFailureIdent[?] => arg.tpe match + case x: SearchFailureType => + case _ => + case _ => diff --git a/tests/pos/i17230.min1.scala b/tests/pos/i17230.min1.scala new file mode 100644 index 000000000000..e2df63e168c1 --- /dev/null +++ b/tests/pos/i17230.min1.scala @@ -0,0 +1,15 @@ +// scalac: -Werror +trait Foo: + type Bar[_] + +object Foo: + type Aux[B[_]] = Foo { type Bar[A] = B[A] } + +class Test: + def t1[B[_]](self: Option[Foo.Aux[B]]) = self match + case Some(_) => 1 + case None => 2 + + def t2[B[_]](self: Option[Foo.Aux[B]]) = self match + case Some(f) => 1 + case None => 2 diff --git a/tests/pos/i17230.orig.scala b/tests/pos/i17230.orig.scala new file mode 100644 index 000000000000..d72a0082a116 --- /dev/null +++ b/tests/pos/i17230.orig.scala @@ -0,0 +1,20 @@ +// scalac: -Werror +import scala.util.* + +trait Transaction { + type State[_] +} +object Transaction { + type of[S[_]] = Transaction { type State[A] = S[A] } +} +trait DynamicScope[State[_]] + +case class ScopeSearch[State[_]](self: Either[Transaction.of[State], DynamicScope[State]]) { + + def embedTransaction[T](f: Transaction.of[State] => T): T = + self match { + case Left(integrated) => ??? + case Right(ds) => ??? + } +} + From 5d6891fe3de921a825d53b369cc9b3e805275753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Thu, 10 Aug 2023 09:17:12 +0200 Subject: [PATCH 644/657] Add changelog for 3.3.1-RC5 --- changelogs/3.3.1-RC5.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 changelogs/3.3.1-RC5.md diff --git a/changelogs/3.3.1-RC5.md b/changelogs/3.3.1-RC5.md new file mode 100644 index 000000000000..e0bfc2a7fea8 --- /dev/null +++ b/changelogs/3.3.1-RC5.md @@ -0,0 +1,22 @@ +# Backported fixes + +- Heal stage inconsistent prefixes of type projections [#18239](https://github.com/lampepfl/dotty/pull/18239) +- Fix regression #17245: Overloaded methods with ClassTags [#18286](http://github.com/lampepfl/dotty/pull/18286) +- Disallow taking singleton types of packages again [#18232](http://github.com/lampepfl/dotty/pull/18232) +- A slightly more conservative version of #14218 [#18352](http://github.com/lampepfl/dotty/pull/18352) +- Record failures to adapt application arguments [#18269](http://github.com/lampepfl/dotty/pull/18269) +- Fix regression in exhaustivity of HK types [#18303](http://github.com/lampepfl/dotty/pull/18303) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC4..3.3.1-RC5` these are: + +``` + 5 Dale Wijnand + 2 Martin Odersky + 2 Paweł Marks + 1 Jan Chyb + 1 Nicolas Stucki +``` From 059748245f9e0816a8f9d837b0b2625956853aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Thu, 10 Aug 2023 09:19:25 +0200 Subject: [PATCH 645/657] Release 3.3.1-RC5 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index a60932eb9e30..b1c6e63cef9d 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC4" + val baseVersion = "3.3.1-RC5" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.1-RC3" + val previousDottyVersion = "3.3.1-RC4" object CompatMode { final val BinaryCompatible = 0 From 8e9b7182f41d03f412b2f2db0c8414f1411aa70c Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 13 Jul 2023 20:42:43 +0200 Subject: [PATCH 646/657] Refine infoDependsOnPrefix infoDependsOnPrefix now also considers non-final term members. Before 8d65f19 it only considered abstract types. Constructors were classified as non-final, which caused regression. We now exclude constructors specifically. Maybe we should instead classify them as effectively final. Fixes #18160 --- .../src/dotty/tools/dotc/core/Types.scala | 1 + .../tools/dotc/transform/TreeChecker.scala | 2 +- tests/pos/i18160/Test_2.scala | 11 ++++++++ tests/pos/i18160/repro_1.scala | 25 +++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i18160/Test_2.scala create mode 100644 tests/pos/i18160/repro_1.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index bb4fd02816a6..81fc28a32fec 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2537,6 +2537,7 @@ object Types { (symd.isAbstractType || symd.isTerm && !symd.flagsUNSAFE.isOneOf(Module | Final | Param) + && !symd.isConstructor && !symd.maybeOwner.isEffectivelyFinal) && prefix.sameThis(symd.maybeOwner.thisType) && refines(givenSelfTypeOrCompleter(prefix.cls), symd.name) diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index e50fb9d8b09c..34b3183a6b15 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -544,7 +544,7 @@ object TreeChecker { val TypeDef(_, impl @ Template(constr, _, _, _)) = cdef: @unchecked assert(cdef.symbol == cls) assert(impl.symbol.owner == cls) - assert(constr.symbol.owner == cls) + assert(constr.symbol.owner == cls, i"constr ${constr.symbol} in $cdef has wrong owner; should be $cls but is ${constr.symbol.owner}") assert(cls.primaryConstructor == constr.symbol, i"mismatch, primary constructor ${cls.primaryConstructor}, in tree = ${constr.symbol}") checkOwner(impl) checkOwner(impl.constr) diff --git a/tests/pos/i18160/Test_2.scala b/tests/pos/i18160/Test_2.scala new file mode 100644 index 000000000000..9ee40c3d37f9 --- /dev/null +++ b/tests/pos/i18160/Test_2.scala @@ -0,0 +1,11 @@ +class SynchronizedReevaluation +class SynchronizedReevaluationApi[Api <: RescalaInterface](val api: Api){ + import api._ + + def SynchronizedReevaluation[A](evt: Event[A])(implicit + turnSource: CreationTicket + ): (SynchronizedReevaluation, Event[A]) = { + val sync = new SynchronizedReevaluation + (sync, evt.map(identity)(turnSource)) + } +} diff --git a/tests/pos/i18160/repro_1.scala b/tests/pos/i18160/repro_1.scala new file mode 100644 index 000000000000..060f2d325d2d --- /dev/null +++ b/tests/pos/i18160/repro_1.scala @@ -0,0 +1,25 @@ +object core { + final class CreationTicket[State[_]] +} + +trait ReadAs[S[_], +A] { type State[V] = S[V] } + +trait EventCompatBundle { + bundle: Operators => + + trait EventCompat[+T] extends ReadAs[State, Option[T]] { + selfType: Event[T] => + final inline def map[B](inline expression: T => B)(implicit ticket: CreationTicket): Event[B] = ??? + } +} + +trait EventBundle extends EventCompatBundle { self: Operators => + trait Event[+T] extends EventCompat[T]: + final override type State[V] = self.State[V] +} +trait Operators extends EventBundle { + type State[_] + type CreationTicket = core.CreationTicket[State] +} +trait RescalaInterface extends Operators + From d2a0b3cd298ec45ef34942b23829f305cfca6d96 Mon Sep 17 00:00:00 2001 From: odersky Date: Thu, 13 Jul 2023 20:53:37 +0200 Subject: [PATCH 647/657] Make constructors effectively final This is mainly a cleanup. --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 1 + compiler/src/dotty/tools/dotc/transform/init/Util.scala | 6 ++---- compiler/src/dotty/tools/dotc/typer/RefChecks.scala | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 988a37be4388..b8c17ff61e9e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1196,6 +1196,7 @@ object SymDenotations { isOneOf(EffectivelyFinalFlags) || is(Inline, butNot = Deferred) || is(JavaDefinedVal, butNot = Method) + || isConstructor || !owner.isExtensibleClass /** A class is effectively sealed if has the `final` or `sealed` modifier, or it diff --git a/compiler/src/dotty/tools/dotc/transform/init/Util.scala b/compiler/src/dotty/tools/dotc/transform/init/Util.scala index 4e60c1325b09..ba2216504aef 100644 --- a/compiler/src/dotty/tools/dotc/transform/init/Util.scala +++ b/compiler/src/dotty/tools/dotc/transform/init/Util.scala @@ -75,11 +75,9 @@ object Util: case _ => None - def resolve(cls: ClassSymbol, sym: Symbol)(using Context): Symbol = log("resove " + cls + ", " + sym, printer, (_: Symbol).show) { - if (sym.isEffectivelyFinal || sym.isConstructor) sym + def resolve(cls: ClassSymbol, sym: Symbol)(using Context): Symbol = log("resove " + cls + ", " + sym, printer, (_: Symbol).show): + if sym.isEffectivelyFinal then sym else sym.matchingMember(cls.appliedRef) - } - extension (sym: Symbol) def hasSource(using Context): Boolean = !sym.defTree.isEmpty diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index fe28a8b18833..025eae3606af 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1689,7 +1689,7 @@ class RefChecks extends MiniPhase { thisPhase => // if (settings.warnNullaryUnit) // checkNullaryMethodReturnType(sym) // if (settings.warnInaccessible) { - // if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic) + // if (!sym.isEffectivelyFinal && !sym.isSynthetic) // checkAccessibilityOfReferencedTypes(tree) // } // tree match { From 0305d8878a7dbbbc0ff398cdec0fc079f067280f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 25 Jul 2023 15:29:29 +0200 Subject: [PATCH 648/657] Do not compute `protoFormal` if `param.tpt` is empty This was accidentally moved before of the `if (!param.tpt.isEmpty)` guard in https://github.com/lampepfl/dotty/commit/0f7c3abc3706b2054c48f3b16991741edb3a4610#diff-8c9ece1772bd78160fc1c31e988664586c9df566a1d22ff99ef99dd6d5627a90R1534 Fixes #18276 --- .../src/dotty/tools/dotc/typer/Typer.scala | 49 +++++++++---------- tests/pos/i18276a.scala | 15 ++++++ tests/pos/i18276b.scala | 9 ++++ 3 files changed, 48 insertions(+), 25 deletions(-) create mode 100644 tests/pos/i18276a.scala create mode 100644 tests/pos/i18276b.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index cb23262d1410..74be1dee9a9b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1596,32 +1596,31 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if desugared.isEmpty then val inferredParams: List[untpd.ValDef] = for ((param, i) <- params.zipWithIndex) yield - val (formalBounds, isErased) = protoFormal(i) - val param0 = - if (!param.tpt.isEmpty) param - else - val formal = formalBounds.loBound - val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing - val knownFormal = isFullyDefined(formal, ForceDegree.failBottom) - // If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound, - // try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala) - val paramType = - // Strip inferred erased annotation, to avoid accidentally inferring erasedness - val formal0 = if !isErased then formal.stripAnnots(_.symbol != defn.ErasedParamAnnot) else formal - if knownFormal && !isBottomFromWildcard then - formal0 - else - inferredFromTarget(param, formal, calleeType, isErased, paramIndex).orElse( - if knownFormal then formal0 - else errorType(AnonymousFunctionMissingParamType(param, tree, formal), param.srcPos) - ) - val paramTpt = untpd.TypedSplice( - (if knownFormal then InferredTypeTree() else untpd.TypeTree()) - .withType(paramType.translateFromRepeated(toArray = false)) - .withSpan(param.span.endPos) + if (!param.tpt.isEmpty) param + else + val (formalBounds, isErased) = protoFormal(i) + val formal = formalBounds.loBound + val isBottomFromWildcard = (formalBounds ne formal) && formal.isExactlyNothing + val knownFormal = isFullyDefined(formal, ForceDegree.failBottom) + // If the expected formal is a TypeBounds wildcard argument with Nothing as lower bound, + // try to prioritize inferring from target. See issue 16405 (tests/run/16405.scala) + val paramType = + // Strip inferred erased annotation, to avoid accidentally inferring erasedness + val formal0 = if !isErased then formal.stripAnnots(_.symbol != defn.ErasedParamAnnot) else formal + if knownFormal && !isBottomFromWildcard then + formal0 + else + inferredFromTarget(param, formal, calleeType, isErased, paramIndex).orElse( + if knownFormal then formal0 + else errorType(AnonymousFunctionMissingParamType(param, tree, formal), param.srcPos) ) - cpy.ValDef(param)(tpt = paramTpt) - if isErased then param0.withAddedFlags(Flags.Erased) else param0 + val paramTpt = untpd.TypedSplice( + (if knownFormal then InferredTypeTree() else untpd.TypeTree()) + .withType(paramType.translateFromRepeated(toArray = false)) + .withSpan(param.span.endPos) + ) + val param0 = cpy.ValDef(param)(tpt = paramTpt) + if isErased then param0.withAddedFlags(Flags.Erased) else param0 desugared = desugar.makeClosure(inferredParams, fnBody, resultTpt, isContextual, tree.span) typed(desugared, pt) diff --git a/tests/pos/i18276a.scala b/tests/pos/i18276a.scala new file mode 100644 index 000000000000..46c2722fd8be --- /dev/null +++ b/tests/pos/i18276a.scala @@ -0,0 +1,15 @@ +import scala.language.implicitConversions + +case class Assign(left: String, right: String) +class SyntaxAnalyser extends ParsersBase { + val x: Parser[String ~ String] = ??? + val y: Parser[Assign] = x.map(Assign.apply) +} + +class ParsersBase { + trait ~[+T, +U] + abstract class Parser[+T]: + def map[U](f: T => U): Parser[U] = ??? + + given [A, B, X]: Conversion[(A, B) => X, (A ~ B) => X] = ??? +} diff --git a/tests/pos/i18276b.scala b/tests/pos/i18276b.scala new file mode 100644 index 000000000000..a4d905293472 --- /dev/null +++ b/tests/pos/i18276b.scala @@ -0,0 +1,9 @@ +import scala.language.implicitConversions + +def foo(a: Int): Int = ??? +def bar(f: () => Int): Int = ??? + +given f: Conversion[Int => Int, () => Int] = ??? + +def test1: Int = bar(foo) // implicit conversion applied to foo +def test2: Int = bar(f(foo)) From 6bf8ac95d677bda402cb5ef44f15400b82481e69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Mon, 21 Aug 2023 17:20:00 +0200 Subject: [PATCH 649/657] Revert "Normalize match type usage during implicit lookup" This reverts commit 5bafff7cc96f1f31f6e77620ca509dfa55d816b4. --- .../dotty/tools/dotc/core/TypeComparer.scala | 2 +- .../dotty/tools/dotc/core/TypeErrors.scala | 3 --- .../dotty/tools/dotc/typer/Implicits.scala | 7 ------ tests/pos/i17395.scala | 25 ------------------- 4 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 tests/pos/i17395.scala diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index b84af998ffb6..6857e3da38ed 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -3180,7 +3180,7 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { tp case Nil => val casesText = MatchTypeTrace.noMatchesText(scrut, cases) - throw MatchTypeReductionError(em"Match type reduction $casesText") + throw TypeError(em"Match type reduction $casesText") inFrozenConstraint { // Empty types break the basic assumption that if a scrutinee and a diff --git a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala index f59bd08da779..24a207da6836 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErrors.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErrors.scala @@ -46,9 +46,6 @@ object TypeError: def toMessage(using Context) = msg end TypeError -class MatchTypeReductionError(msg: Message)(using Context) extends TypeError: - def toMessage(using Context) = msg - class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])(using Context) extends TypeError: def toMessage(using Context) = em"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}" diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 66f400d7eae0..4bbd6ee080b6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -636,13 +636,6 @@ trait ImplicitRunInfo: case t: TypeLambda => for p <- t.paramRefs do partSeen += p traverseChildren(t) - case t: MatchType => - traverseChildren(t) - traverse(try t.normalized catch case _: MatchTypeReductionError => t) - case MatchType.InDisguise(mt) - if !t.isInstanceOf[LazyRef] // skip recursive applications (eg. Tuple.Map) - => - traverse(mt) case t => traverseChildren(t) diff --git a/tests/pos/i17395.scala b/tests/pos/i17395.scala deleted file mode 100644 index 87c0a45a9ff5..000000000000 --- a/tests/pos/i17395.scala +++ /dev/null @@ -1,25 +0,0 @@ -trait TC[T] - -object TC { - def optionTCForPart[T](implicit tc: TC[ExtractPart[T]]): TC[Option[ExtractPart[T]]] = new TC[Option[ExtractPart[T]]] {} -} - -type ExtractPart[T] = T match { - case PartField[t] => t -} -type PartField[T] = Any { type Part = T } - -class ValuePartHolder { - type Part = Value -} - -class Value -object Value { - implicit val tcValue: TC[Value] = new {} -} - -@main def main(): Unit = { -// import Value.tcValue // explicit import works around the issue, but shouldn't be necessary - val tc = TC.optionTCForPart[ValuePartHolder] - println(tc) -} From 340303fb983405cd6b123458f3573b3cf1505c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 22 Aug 2023 13:18:45 +0200 Subject: [PATCH 650/657] Add changelog for 3.3.1-RC6 --- changelogs/3.3.1-RC6.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 changelogs/3.3.1-RC6.md diff --git a/changelogs/3.3.1-RC6.md b/changelogs/3.3.1-RC6.md new file mode 100644 index 000000000000..f74ab7fe7e18 --- /dev/null +++ b/changelogs/3.3.1-RC6.md @@ -0,0 +1,17 @@ +# Backported fixes + +- Refine `infoDependsOnPrefix` [#18204](https://github.com/lampepfl/dotty/pull/18204) +- FDo not compute `protoFormal` if `param.tpt` is empty [#18288](http://github.com/lampepfl/dotty/pull/18288) +- Revert "Normalize match type usage during implicit lookup" [#18440](http://github.com/lampepfl/dotty/pull/18440) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC4..3.3.1-RC5` these are: + +``` + 3 Paweł Marks + 2 Martin Odersky + 1 Nicolas Stucki +``` From 5f8485e13b66b6d64b8e52161b5de10699900543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 22 Aug 2023 13:14:01 +0200 Subject: [PATCH 651/657] Release 3.3.1-RC6 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index b1c6e63cef9d..047310df0a6b 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC5" + val baseVersion = "3.3.1-RC6" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.1-RC4" + val previousDottyVersion = "3.3.1-RC5" object CompatMode { final val BinaryCompatible = 0 From 88e6725df9fc73d9894d8ae8205a54ca8d47851d Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 28 Aug 2023 16:55:10 +0200 Subject: [PATCH 652/657] Tweak selection from self types Previously, we rejected the case where a symbol of a self type selection was private if was not of the enclosing class. But that symbol could shadow a non-private symbol in a base class, so have to treat that case as well. Fixes #18631 --- compiler/src/dotty/tools/dotc/core/Types.scala | 7 ++++++- tests/pos/i18361.scala | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i18361.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 81fc28a32fec..fb66d133c0ba 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -808,9 +808,14 @@ object Types { // is made to save execution time in the common case. See i9844.scala for test cases. def qualifies(sd: SingleDenotation) = !sd.symbol.is(Private) || sd.symbol.owner == tp.cls - d match + d.match case d: SingleDenotation => if qualifies(d) then d else NoDenotation case d => d.filterWithPredicate(qualifies) + .orElse: + // Only inaccessible private symbols were found. But there could still be + // shadowed non-private symbols, so as a fallback search for those. + // Test case is i18361.scala. + findMember(name, pre, required, excluded | Private) else d else // There is a special case to handle: diff --git a/tests/pos/i18361.scala b/tests/pos/i18361.scala new file mode 100644 index 000000000000..a84d5f0a09db --- /dev/null +++ b/tests/pos/i18361.scala @@ -0,0 +1,15 @@ +package test1: + class Service(val name: String) + class CrudService(name: String) extends Service(name) + + trait Foo { self: CrudService => + val x = self.name + } + +package test2: + abstract class Service[F[_]](val name: String) + abstract class CrudService[F[_]](name: String) extends Service[F](name) + + trait Foo[F[_]] { self: CrudService[?] => + val x = self.name + } From fb6545872968e90798c5a411e69b79670df8e0fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 29 Aug 2023 11:34:58 +0200 Subject: [PATCH 653/657] Revert "Add reflect `defn.FunctionClass` overloads" This reverts commit 9571b42a2ff63e897cb566c09259f5f8d1e7a021. --- .../quoted/runtime/impl/QuotesImpl.scala | 4 --- library/src/scala/quoted/Quotes.scala | 19 -------------- .../stdlibExperimentalDefinitions.scala | 2 -- tests/run-macros/tasty-definitions-1.check | 26 ------------------- .../tasty-definitions-1/quoted_1.scala | 1 - 5 files changed, 52 deletions(-) diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index e178dc81b1a3..db4e3e6c6a05 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2811,10 +2811,6 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler if isErased then throw new Exception("Erased function classes are not supported. Use a refined `scala.runtime.ErasedFunction`") else dotc.core.Symbols.defn.FunctionSymbol(arity, isImplicit) - def FunctionClass(arity: Int): Symbol = - FunctionClass(arity, false, false) - def FunctionClass(arity: Int, isContextual: Boolean): Symbol = - FunctionClass(arity, isContextual, false) def ErasedFunctionClass = dotc.core.Symbols.defn.ErasedFunctionClass def TupleClass(arity: Int): Symbol = dotc.core.Symbols.defn.TupleType(arity).nn.classSymbol.asClass diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index c7d5719b0e1f..b6e5a12da2d8 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -4295,27 +4295,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * - ... * - Nth element is `FunctionN` */ - // TODO: deprecate in 3.4 and stabilize FunctionClass(Int)/FunctionClass(Int,Boolean) - // @deprecated("Use overload of `FunctionClass` with 1 or 2 arguments","3.4") def FunctionClass(arity: Int, isImplicit: Boolean = false, isErased: Boolean = false): Symbol - /** Class symbol of a function class `scala.FunctionN`. - * - * @param arity the arity of the function where `0 <= arity` - * @return class symbol of `scala.FunctionN` where `N == arity` - */ - @experimental - def FunctionClass(arity: Int): Symbol - - /** Class symbol of a context function class `scala.FunctionN` or `scala.ContextFunctionN`. - * - * @param arity the arity of the function where `0 <= arity` - * @param isContextual if it is a `scala.ContextFunctionN` - * @return class symbol of `scala.FunctionN` or `scala.ContextFunctionN` where `N == arity` - */ - @experimental - def FunctionClass(arity: Int, isContextual: Boolean): Symbol - /** The `scala.runtime.ErasedFunction` built-in trait. */ @experimental def ErasedFunctionClass: Symbol diff --git a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala index 644efb54c32e..5ccdb753e9b3 100644 --- a/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-custom-args/tasty-inspector/stdlibExperimentalDefinitions.scala @@ -63,8 +63,6 @@ val experimentalDefinitionInLibrary = Set( "scala.annotation.MacroAnnotation", //// New APIs: Quotes - // Should be stabilized in 3.4.0 - "scala.quoted.Quotes.reflectModule.defnModule.FunctionClass", "scala.quoted.Quotes.reflectModule.FlagsModule.AbsOverride", // Can be stabilized in 3.4.0 (unsure) or later "scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings", diff --git a/tests/run-macros/tasty-definitions-1.check b/tests/run-macros/tasty-definitions-1.check index ce7251d7d3ee..4ac0e6267028 100644 --- a/tests/run-macros/tasty-definitions-1.check +++ b/tests/run-macros/tasty-definitions-1.check @@ -57,57 +57,31 @@ Function23 Function24 Function25 ContextFunction0 -ContextFunction0 -ContextFunction1 ContextFunction1 ContextFunction2 -ContextFunction2 -ContextFunction3 ContextFunction3 ContextFunction4 -ContextFunction4 -ContextFunction5 ContextFunction5 ContextFunction6 -ContextFunction6 ContextFunction7 -ContextFunction7 -ContextFunction8 ContextFunction8 ContextFunction9 -ContextFunction9 ContextFunction10 -ContextFunction10 -ContextFunction11 ContextFunction11 ContextFunction12 -ContextFunction12 ContextFunction13 -ContextFunction13 -ContextFunction14 ContextFunction14 ContextFunction15 -ContextFunction15 -ContextFunction16 ContextFunction16 ContextFunction17 -ContextFunction17 -ContextFunction18 ContextFunction18 ContextFunction19 -ContextFunction19 ContextFunction20 -ContextFunction20 -ContextFunction21 ContextFunction21 ContextFunction22 -ContextFunction22 ContextFunction23 -ContextFunction23 -ContextFunction24 ContextFunction24 ContextFunction25 -ContextFunction25 class java.lang.Exception: Erased function classes are not supported. Use a refined `scala.runtime.ErasedFunction` ErasedFunction Tuple2 diff --git a/tests/run-macros/tasty-definitions-1/quoted_1.scala b/tests/run-macros/tasty-definitions-1/quoted_1.scala index bf9e28288486..ed210706f567 100644 --- a/tests/run-macros/tasty-definitions-1/quoted_1.scala +++ b/tests/run-macros/tasty-definitions-1/quoted_1.scala @@ -60,7 +60,6 @@ object Macros { printout(defn.FunctionClass(i).name) for (i <- 0 to 25) - printout(defn.FunctionClass(i, isContextual = true).name) printout(defn.FunctionClass(i, isImplicit = true).name) // should fail From 24cd50d2d66caeb0930032d3e23bb1a60fe02528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 29 Aug 2023 17:56:55 +0200 Subject: [PATCH 654/657] Add changelog for 3.3.1-RC7 --- changelogs/3.3.1-RC7.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 changelogs/3.3.1-RC7.md diff --git a/changelogs/3.3.1-RC7.md b/changelogs/3.3.1-RC7.md new file mode 100644 index 000000000000..f8f093a18d11 --- /dev/null +++ b/changelogs/3.3.1-RC7.md @@ -0,0 +1,16 @@ +# Backported fixes + +- Tweak selection from self types [#18467](https://github.com/lampepfl/dotty/pull/18467) +- Revert "Add reflect `defn.FunctionClass` overloads" [#18473](http://github.com/lampepfl/dotty/pull/18473) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.1-RC6..3.3.1-RC7` these are: + +``` + 3 Paweł Marks + 1 Martin Odersky + +``` From ca005766359c27ea06adc18409b6e53f79732bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 29 Aug 2023 17:58:04 +0200 Subject: [PATCH 655/657] Release 3.3.1-RC7 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 047310df0a6b..f748ce44d4ca 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC6" + val baseVersion = "3.3.1-RC7" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.1-RC5" + val previousDottyVersion = "3.3.1-RC6" object CompatMode { final val BinaryCompatible = 0 From 9b4ea8eb22dd96c94eb5d5c62cd0c0d277342a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 5 Sep 2023 14:33:24 +0200 Subject: [PATCH 656/657] Add changelog for 3.3.1 Also fix typos in older changelogs --- changelogs/3.3.1-RC1.md | 15 ++- changelogs/3.3.1-RC6.md | 4 +- changelogs/3.3.1.md | 287 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+), 4 deletions(-) create mode 100644 changelogs/3.3.1.md diff --git a/changelogs/3.3.1-RC1.md b/changelogs/3.3.1-RC1.md index 4e52eb874891..e7d9f8f87ea9 100644 --- a/changelogs/3.3.1-RC1.md +++ b/changelogs/3.3.1-RC1.md @@ -42,6 +42,7 @@ - Harden tpd.Apply/TypeApply in case of errors [#16887](https://github.com/lampepfl/dotty/pull/16887) - Try to be more subtle when inferring type parameters of class parents [#16896](https://github.com/lampepfl/dotty/pull/16896) - Include `P` in the implicit scope of `P.this.type` [#17088](https://github.com/lampepfl/dotty/pull/17088) +- Do not compute `protoFormal` if `param.tpt` is empty [#18288](http://github.com/lampepfl/dotty/pull/18288) ## Incremental Compilation @@ -71,7 +72,6 @@ ## Match Types -- Normalize match type usage during implicit lookup [#17457](https://github.com/lampepfl/dotty/pull/17457) - Fix #13757: Explicitly disallow higher-kinded scrutinees of match types. [#17322](https://github.com/lampepfl/dotty/pull/17322) - Fix match type reduction with wildcard type arguments [#17065](https://github.com/lampepfl/dotty/pull/17065) - Fix check whether classtag can be generated for match types [#16708](https://github.com/lampepfl/dotty/pull/16708) @@ -86,6 +86,7 @@ - Check outer class prefixes in type projections when pattern matching [#17136](https://github.com/lampepfl/dotty/pull/17136) - Make unchecked cases non-`@unchecked` and non-unreachable [#16958](https://github.com/lampepfl/dotty/pull/16958) - Fix #16899: Better handle X instanceOf P where X is T1 | T2 [#17382](https://github.com/lampepfl/dotty/pull/17382) +- Fix regression in exhaustivity of HK types [#18303](http://github.com/lampepfl/dotty/pull/18303) ## Pickling @@ -121,6 +122,7 @@ - Only transform the body of the quote with QuoteTransformer [#17451](https://github.com/lampepfl/dotty/pull/17451) - Place staged type captures in Quote AST [#17424](https://github.com/lampepfl/dotty/pull/17424) - Add SplicePattern AST to parse and type quote pattern splices [#17396](https://github.com/lampepfl/dotty/pull/17396) +- Dealias types in `New`` before matching quotes [#17615](https://github.com/lampepfl/dotty/pull/17615) ## Reflection @@ -129,7 +131,6 @@ - Fix reflect.LambdaType type test [#16972](https://github.com/lampepfl/dotty/pull/16972) - Improve `New`/`Select` -Ycheck message [#16746](https://github.com/lampepfl/dotty/pull/16746) - Improve error message for CyclicReference in macros [#16749](https://github.com/lampepfl/dotty/pull/16749) -- Add reflect `defn.FunctionClass` overloads [#16849](https://github.com/lampepfl/dotty/pull/16849) ## REPL @@ -222,6 +223,16 @@ - Fix #16405 ctd - wildcards prematurely resolving to Nothing [#16764](https://github.com/lampepfl/dotty/pull/16764) - Test: add regression test for #7790 [#17473](https://github.com/lampepfl/dotty/pull/17473) - Properly handle `AnyVal`s as refinement members of `Selectable`s [#16286](https://github.com/lampepfl/dotty/pull/16286) +- Fix `accessibleType` for package object prefixes [#18057](https://github.com/lampepfl/dotty/pull/18057) +- Add clause for protected visibility from package objects [#18134](https://github.com/lampepfl/dotty/pull/18134) +- Revert "Include top-level symbols from same file in outer ambiguity error" [#17438](https://github.com/lampepfl/dotty/pull/17438) +- Heal stage inconsistent prefixes of type projections [#18239](https://github.com/lampepfl/dotty/pull/18239) +- Fix regression #17245: Overloaded methods with ClassTags [#18286](http://github.com/lampepfl/dotty/pull/18286) +- Disallow taking singleton types of packages again [#18232](http://github.com/lampepfl/dotty/pull/18232) +- A slightly more conservative version of #14218 [#18352](http://github.com/lampepfl/dotty/pull/18352) +- Record failures to adapt application arguments [#18269](http://github.com/lampepfl/dotty/pull/18269) +- Refine `infoDependsOnPrefix` [#18204](httpsF://github.com/lampepfl/dotty/pull/18204) +- Tweak selection from self types [#18467](https://github.com/lampepfl/dotty/pull/18467) # Contributors diff --git a/changelogs/3.3.1-RC6.md b/changelogs/3.3.1-RC6.md index f74ab7fe7e18..96181855f1a0 100644 --- a/changelogs/3.3.1-RC6.md +++ b/changelogs/3.3.1-RC6.md @@ -1,14 +1,14 @@ # Backported fixes - Refine `infoDependsOnPrefix` [#18204](https://github.com/lampepfl/dotty/pull/18204) -- FDo not compute `protoFormal` if `param.tpt` is empty [#18288](http://github.com/lampepfl/dotty/pull/18288) +- Do not compute `protoFormal` if `param.tpt` is empty [#18288](http://github.com/lampepfl/dotty/pull/18288) - Revert "Normalize match type usage during implicit lookup" [#18440](http://github.com/lampepfl/dotty/pull/18440) # Contributors Thank you to all the contributors who made this release possible 🎉 -According to `git shortlog -sn --no-merges 3.3.1-RC4..3.3.1-RC5` these are: +According to `git shortlog -sn --no-merges 3.3.1-RC5..3.3.1-RC6` these are: ``` 3 Paweł Marks diff --git a/changelogs/3.3.1.md b/changelogs/3.3.1.md new file mode 100644 index 000000000000..5bbd6eb2861c --- /dev/null +++ b/changelogs/3.3.1.md @@ -0,0 +1,287 @@ +# Highlights of the release + +- Support records in JavaParsers [#16762](https://github.com/lampepfl/dotty/pull/16762) +- Port JVM backend refactor from Scala 2 [#15322](https://github.com/lampepfl/dotty/pull/15322) + +# Other changes and fixes + +## Backend + +- Disallow mixins where super calls bind to vals [#16908](https://github.com/lampepfl/dotty/pull/16908) +- Fix #15107: Avoid re-emitting a LineNumber after only LabelNodes. [#16813](https://github.com/lampepfl/dotty/pull/16813) + +## Coverage + +- Fix #17042: Preserve the shape of secondary ctors in instrumentCoverage. [#17111](https://github.com/lampepfl/dotty/pull/17111) + +## Default parameters + +- Dupe fix when finding default arg getters [#17058](https://github.com/lampepfl/dotty/pull/17058) + +## Documentation + +- Fix: ensure syntax blocks for ebnf are marked as such [#16837](https://github.com/lampepfl/dotty/pull/16837) + +## Erasure + +- Handle `@companionClass` and `@companionMethod` meta-annotations [#17091](https://github.com/lampepfl/dotty/pull/17091) + +## Extension Methods + +- Support extension methods imported from different objects [#17050](https://github.com/lampepfl/dotty/pull/17050) + +## GADTs + +- Fix tuple member selection so it works with GADT healing [#16766](https://github.com/lampepfl/dotty/pull/16766) +- Fix upper bound constraints, that are higher-kinded [#16744](https://github.com/lampepfl/dotty/pull/16744) +- Split out immutable GadtConstraint [#16602](https://github.com/lampepfl/dotty/pull/16602) + +## Implicits + +- Improve subtyping check for not yet eta-expanded higher kinded types [#17139](https://github.com/lampepfl/dotty/pull/17139) +- Harden tpd.Apply/TypeApply in case of errors [#16887](https://github.com/lampepfl/dotty/pull/16887) +- Try to be more subtle when inferring type parameters of class parents [#16896](https://github.com/lampepfl/dotty/pull/16896) +- Include `P` in the implicit scope of `P.this.type` [#17088](https://github.com/lampepfl/dotty/pull/17088) + +## Incremental Compilation + +- Fix under-compilation when the method type in a SAM changes [#16996](https://github.com/lampepfl/dotty/pull/16996) + +## Infrastructure + +- Set reference version to 3.3.0-RC6 [#17504](https://github.com/lampepfl/dotty/pull/17504) +- Fix #17119: Download Coursier from GitHub directly [#17141](https://github.com/lampepfl/dotty/pull/17141) + +## Inline + +- Remove NamedArg from inlined arguments [#17228](https://github.com/lampepfl/dotty/pull/17228) +- Don't generate a Select for a TermRef with NoPrefix [#16754](https://github.com/lampepfl/dotty/pull/16754) +- Prepare bodies of inline forwarders eagerly [#16757](https://github.com/lampepfl/dotty/pull/16757) +- Do not remove inline method implementations until PruneErasedDefs [#17408](https://github.com/lampepfl/dotty/pull/17408) + +## Java Interop + +- ClassfileParser: allow missing param names (for JDK 21) [#17536](https://github.com/lampepfl/dotty/pull/17536) + +## Linting + +- Improve -Wunused: locals, privates with unset vars warning #16639 [#17160](https://github.com/lampepfl/dotty/pull/17160) +- Fix wunused false positive when deriving alias type [#17157](https://github.com/lampepfl/dotty/pull/17157) +- Port `-Wnonunit-statement` setting for dotty [#16936](https://github.com/lampepfl/dotty/pull/16936) + +## Match Types + +- Normalize match type usage during implicit lookup [#17457](https://github.com/lampepfl/dotty/pull/17457) +- Fix #13757: Explicitly disallow higher-kinded scrutinees of match types. [#17322](https://github.com/lampepfl/dotty/pull/17322) +- Fix match type reduction with wildcard type arguments [#17065](https://github.com/lampepfl/dotty/pull/17065) +- Fix check whether classtag can be generated for match types [#16708](https://github.com/lampepfl/dotty/pull/16708) + +## Parser + +- Allow lines starting with `.` to fall outside previous indentation widths [#17056](https://github.com/lampepfl/dotty/pull/17056) + +## Pattern Matching + +- Fix #11541: Specialize ClassTag[T] in exhaustivity check [#17385](https://github.com/lampepfl/dotty/pull/17385) +- Check outer class prefixes in type projections when pattern matching [#17136](https://github.com/lampepfl/dotty/pull/17136) +- Make unchecked cases non-`@unchecked` and non-unreachable [#16958](https://github.com/lampepfl/dotty/pull/16958) +- Fix #16899: Better handle X instanceOf P where X is T1 | T2 [#17382](https://github.com/lampepfl/dotty/pull/17382) + +## Pickling + +- ClassfileParser: Avoid cycle when accessing companion in inner class lookup [#16882](https://github.com/lampepfl/dotty/pull/16882) + +## Polyfunctions + +- Fix type aliases in beta-reduction of polyfunctions [#17054](https://github.com/lampepfl/dotty/pull/17054) + +## Quotes + +- Register `paramProxy` and `thisProxy` in `Quote` type [#17541](https://github.com/lampepfl/dotty/pull/17541) +- Only check newVal/newMethod privateWithin on -Xcheck-macros [#17437](https://github.com/lampepfl/dotty/pull/17437) +- Unencode quote and splice trees [#17342](https://github.com/lampepfl/dotty/pull/17342) +- Correctly type Expr.ofTupleFromSeq for arity > 22 [#17261](https://github.com/lampepfl/dotty/pull/17261) +- Use TermRef to distinguish distinct Type[T] instances [#17205](https://github.com/lampepfl/dotty/pull/17205) +- Check level consistency of SingletonTypeTree as a type [#17209](https://github.com/lampepfl/dotty/pull/17209) +- Fix splice type variable pattern detection [#17048](https://github.com/lampepfl/dotty/pull/17048) +- Avoid creation of `@SplicedType` quote local refrences [#17051](https://github.com/lampepfl/dotty/pull/17051) +- Dealias type references when healing types in quotes [#17049](https://github.com/lampepfl/dotty/pull/17049) +- Replace quoted type variables in signature of HOAS pattern result [#16951](https://github.com/lampepfl/dotty/pull/16951) +- Beta-reduce directly applied PolymorphicFunction [#16623](https://github.com/lampepfl/dotty/pull/16623) +- Use `Object.toString` for `quoted.{Expr, Type}` [#16663](https://github.com/lampepfl/dotty/pull/16663) +- Fix Splicer.isEscapedVariable [#16838](https://github.com/lampepfl/dotty/pull/16838) +- Fix references to class members defined in quotes [#17107](https://github.com/lampepfl/dotty/pull/17107) +- Handle pickled forward references in pickled expressions [#16855](https://github.com/lampepfl/dotty/pull/16855) +- Fix #16615 - crashes of path dependent types in spliced Type.of [#16773](https://github.com/lampepfl/dotty/pull/16773) +- Disallow local term references in staged types [#16362](https://github.com/lampepfl/dotty/pull/16362) +- Refactor level checking / type healing logic [#17082](https://github.com/lampepfl/dotty/pull/17082) +- Dealias quoted types when staging [#17059](https://github.com/lampepfl/dotty/pull/17059) +- Fix quotes with references to path dependent types [#17081](https://github.com/lampepfl/dotty/pull/17081) +- Make arguments order in quote hole deterministic [#17405](https://github.com/lampepfl/dotty/pull/17405) +- Only transform the body of the quote with QuoteTransformer [#17451](https://github.com/lampepfl/dotty/pull/17451) +- Place staged type captures in Quote AST [#17424](https://github.com/lampepfl/dotty/pull/17424) +- Add SplicePattern AST to parse and type quote pattern splices [#17396](https://github.com/lampepfl/dotty/pull/17396) + +## Reflection + +- -Xcheck-macros: add hint when a symbol in created twice [#16733](https://github.com/lampepfl/dotty/pull/16733) +- Assert that symbols created using reflect API have correct privateWithin symbols [#17352](https://github.com/lampepfl/dotty/pull/17352) +- Fix reflect.LambdaType type test [#16972](https://github.com/lampepfl/dotty/pull/16972) +- Improve `New`/`Select` -Ycheck message [#16746](https://github.com/lampepfl/dotty/pull/16746) +- Improve error message for CyclicReference in macros [#16749](https://github.com/lampepfl/dotty/pull/16749) +- Add reflect `defn.FunctionClass` overloads [#16849](https://github.com/lampepfl/dotty/pull/16849) + +## REPL + +- Always load REPL classes in macros including the output directory [#16866](https://github.com/lampepfl/dotty/pull/16866) + +## Reporting + +- Improve missing argument list error [#17126](https://github.com/lampepfl/dotty/pull/17126) +- Improve implicit parameter error message with aliases [#17125](https://github.com/lampepfl/dotty/pull/17125) +- Improve "constructor proxy shadows outer" handling [#17154](https://github.com/lampepfl/dotty/pull/17154) +- Clarify ambiguous reference error message [#16137](https://github.com/lampepfl/dotty/pull/16137) +- Hint about forbidden combination of implicit values and conversions [#16735](https://github.com/lampepfl/dotty/pull/16735) +- Attach explanation message to diagnostic message [#16787](https://github.com/lampepfl/dotty/pull/16787) +- Propagate implicit search errors from implicit macros [#16840](https://github.com/lampepfl/dotty/pull/16840) +- Detail UnapplyInvalidReturnType error message [#17167](https://github.com/lampepfl/dotty/pull/17167) +- Add way to debug -Xcheck-macros tree checking [#16973](https://github.com/lampepfl/dotty/pull/16973) +- Enrich and finesse compiler crash reporting [#17031](https://github.com/lampepfl/dotty/pull/17031) +- Allow @implicitNotFound messages as explanations [#16893](https://github.com/lampepfl/dotty/pull/16893) +- Include top-level symbols from same file in outer ambiguity error [#17033](https://github.com/lampepfl/dotty/pull/17033) +- Do not issue deprecation warnings when declaring deprecated case classes [#17165](https://github.com/lampepfl/dotty/pull/17165) + +## Scala-JS + +- Fix #17344: Make implicit references to this above dynamic imports explicit. [#17357](https://github.com/lampepfl/dotty/pull/17357) +- Fix #12621: Better error message for JS trait ctor param. [#16811](https://github.com/lampepfl/dotty/pull/16811) +- Fix #16801: Handle Closure's of s.r.FunctionXXL. [#16809](https://github.com/lampepfl/dotty/pull/16809) +- Fix #17549: Unify how Memoize and Constructors decide what fields need storing. [#17560](https://github.com/lampepfl/dotty/pull/17560) + +## Scaladoc + +- Feat: Add a blog configuration with yaml [#17214](https://github.com/lampepfl/dotty/pull/17214) +- Don't render the "$" for module [#17302](https://github.com/lampepfl/dotty/pull/17302) +- Fix: Add scrollbar to the sidebar [#17203](https://github.com/lampepfl/dotty/pull/17203) +- Scaladoc: fix crash when processing extends call [#17260](https://github.com/lampepfl/dotty/pull/17260) +- Fix: Modify the CSS so that the logo of the generated documentation is adaptive [#17172](https://github.com/lampepfl/dotty/pull/17172) +- Fix: Remove the duplicate parameter when generating the scaladoc. [#17097](https://github.com/lampepfl/dotty/pull/17097) +- Fix: padding top in mobile version [#17019](https://github.com/lampepfl/dotty/pull/17019) +- Fix: tap target of the menu in Mobile version [#17018](https://github.com/lampepfl/dotty/pull/17018) +- Scaladoc: Fix expand icon not changing on anchor link [#17053](https://github.com/lampepfl/dotty/pull/17053) +- Scaladoc: fix inkuire generation for PolyTypes [#17129](https://github.com/lampepfl/dotty/pull/17129) +- Re port scroll bar [#17463](https://github.com/lampepfl/dotty/pull/17463) +- Handle empty files and truncated YAML front matter [#17527](https://github.com/lampepfl/dotty/pull/17527) + +## SemanticDB + +- Make sure symbol exists before calling owner [#16860](https://github.com/lampepfl/dotty/pull/16860) +- Support LambdaType (convert from HKTypeLambda) [#16056](https://github.com/lampepfl/dotty/pull/16056) + +## Specification + +- Apply `class-shadowing.md` to the Spec [#16839](https://github.com/lampepfl/dotty/pull/16839) +- Adding base for future Spec into the compiler repo [#16825](https://github.com/lampepfl/dotty/pull/16825) + +## Standard Library + +- Optimization: avoid NotGiven allocations [#17090](https://github.com/lampepfl/dotty/pull/17090) + +## Tooling + +- Disable `ExtractSemanticDB` phase when writing to output directory defined as JAR. [#16790](https://github.com/lampepfl/dotty/pull/16790) +- Print owner of bind symbol with -Yprint-debug-owners [#16854](https://github.com/lampepfl/dotty/pull/16854) +- Small fixes to allow using Metals with scaladoc with sbt [#16816](https://github.com/lampepfl/dotty/pull/16816) + +## Transform + +- Move CrossVersionChecks before FirstTransform [#17301](https://github.com/lampepfl/dotty/pull/17301) +- Fix needsOuterIfReferenced [#17159](https://github.com/lampepfl/dotty/pull/17159) +- Drop incorrect super accessor in trait subclass [#17062](https://github.com/lampepfl/dotty/pull/17062) +- Generate toString only for synthetic companions of case classes [#16890](https://github.com/lampepfl/dotty/pull/16890) +- Check trait constructor for accessibility even if not called at Typer [#17094](https://github.com/lampepfl/dotty/pull/17094) +- Fix #17435: A simpler fix [#17436](https://github.com/lampepfl/dotty/pull/17436) + +## Typer + +- Preserve type bounds for inlined definitions in posttyper [#17190](https://github.com/lampepfl/dotty/pull/17190) +- Change logic to find members of recursive types [#17386](https://github.com/lampepfl/dotty/pull/17386) +- Recognize named arguments in isFunctionWithUnknownParamType [#17161](https://github.com/lampepfl/dotty/pull/17161) +- Better comparisons for type projections [#17092](https://github.com/lampepfl/dotty/pull/17092) +- Allow selectDynamic and applyDynamic to be extension methods [#17106](https://github.com/lampepfl/dotty/pull/17106) +- Fix use of accessibleFrom when finding default arg getters [#16977](https://github.com/lampepfl/dotty/pull/16977) +- Map class literal constant types [#16988](https://github.com/lampepfl/dotty/pull/16988) +- Always use adapted type in withDenotation [#16901](https://github.com/lampepfl/dotty/pull/16901) +- Restrict captureWildcards to only be used if needed [#16799](https://github.com/lampepfl/dotty/pull/16799) +- Don't capture wildcards if in closure or by-name [#16732](https://github.com/lampepfl/dotty/pull/16732) +- Infer: Don't minimise to Nothing if there's an upper bound [#16786](https://github.com/lampepfl/dotty/pull/16786) +- Perform Matchable check only if type test is needed [#16824](https://github.com/lampepfl/dotty/pull/16824) +- Don't eta expand unary varargs methods [#16892](https://github.com/lampepfl/dotty/pull/16892) +- Fix beta-reduction with `Nothing` and `null` args [#16938](https://github.com/lampepfl/dotty/pull/16938) +- Generate kind-correct wildcards when selecting from a wildcard [#17025](https://github.com/lampepfl/dotty/pull/17025) +- Fix #16405 ctd - wildcards prematurely resolving to Nothing [#16764](https://github.com/lampepfl/dotty/pull/16764) +- Test: add regression test for #7790 [#17473](https://github.com/lampepfl/dotty/pull/17473) +- Properly handle `AnyVal`s as refinement members of `Selectable`s [#16286](https://github.com/lampepfl/dotty/pull/16286) + +# Contributors + +Thank you to all the contributors who made this release possible 🎉 + +According to `git shortlog -sn --no-merges 3.3.0..3.3.1` these are: + +``` + 152 Nicolas Stucki + 73 Martin Odersky + 54 Dale Wijnand + 51 Szymon Rodziewicz + 49 Quentin Bernet + 38 Chris Kipp + 31 Paweł Marks + 19 David Hua + 18 Lucas + 18 ysthakur + 15 Fengyun Liu + 14 Guillaume Martres + 14 Jamie Thompson + 11 Sébastien Doeraene + 9 Timothée Andres + 8 Kacper Korban + 7 Matt Bovel + 7 Som Snytt + 6 Julien Richard-Foy + 6 Lucas Leblanc + 5 Michał Pałka + 4 Anatolii Kmetiuk + 4 Guillaume Raffin + 4 Jan Chyb + 4 Paul Coral + 4 Wojciech Mazur + 4 Yichen Xu + 3 Decel + 2 Adrien Piquerez + 2 Arman Bilge + 2 Carl + 2 Florian3k + 2 Kenji Yoshida + 2 Michael Pilquist + 2 Natsu Kagami + 2 Seth Tisue + 2 Tomasz Godzik + 2 Vasil Vasilev + 2 Yadu Krishnan + 1 Bersier + 1 Flavio Brasil + 1 Jan-Pieter van den Heuvel + 1 Lukas Rytz + 1 Miles Yucht + 1 Mohammad Yousuf Minhaj Zia + 1 Ondra Pelech + 1 Philippus + 1 Rikito Taniguchi + 1 Simon R + 1 brandonspark + 1 github-actions[bot] + 1 liang3zy22 + 1 s.bazarsadaev + 1 Łukasz Wroński +``` From 721e7c87ee95b811984b7b992728729d7094c4c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Marks?= Date: Tue, 5 Sep 2023 14:35:21 +0200 Subject: [PATCH 657/657] Release 3.3.1 --- project/Build.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index f748ce44d4ca..f3ec6bb54548 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -82,7 +82,7 @@ object Build { val referenceVersion = "3.3.0" - val baseVersion = "3.3.1-RC7" + val baseVersion = "3.3.1" // Versions used by the vscode extension to create a new project // This should be the latest published releases. @@ -98,7 +98,7 @@ object Build { * set to 3.1.3. If it is going to be 3.1.0, it must be set to the latest * 3.0.x release. */ - val previousDottyVersion = "3.3.1-RC6" + val previousDottyVersion = "3.3.0" object CompatMode { final val BinaryCompatible = 0

  • a@s{PF5LiM~Zls zo|S*1Quoi`F@h4c>30CmUvd~DS87>xT3yJa#vyf3-9xnF9ZZO*2-t?|24gTu<}?Uo;ZMmPfv(_O^9k8S&%;Ole`22W(C6g zIToYU1eNI*#+#92K#U7^G&zHx_Y1zgIDP7BM=v3MO@(DVOG`NW_7Q}!S7VKl4AZ`A zQ#0W&mu#D#H@-*PC0_==Z;s4Iwh8Kjtui}(lBR4yZhun)&*5*l7tjWZw~c!EE{qoc z?6y;Luw6Ksg%iHX@>6HEP|i|o;pC%))!YeT1`D90=`=xu2RGapi9r%k4Jn`e3w&X0UKXDm$x*q98yu5h(EnW^oV*5yl>WKA-+C!l-~ivk4p%)+hSWH4V-o( zE0Lhi?LQGox=~(QX~*(>=en0@HU5(YR~bcZJo@D_m2AIq^tpf{f>87LX#ia25=g+m zq2ySFbZTvhyd)(agnAWa8o_pr>a8*Q`zQ%o!ekxoeofrvy{{l-3M7xJs%ZC=5!dUX z(N~bjQm6TL%Q=2&Lx`XAULSe|*1GT{jWvJVopblxtsgRzem3^Wq-%IC zq_Us$+DqJa{u82qr#be~>4`#B27CKejm9%n>c#!gqS4*Q&~Q{xNZHM`6*`i)j<=L+ zo!MhJkSku7%LYGbN2!6eqnjFPI{Qglqg5>yq?g+qa~@%+_H^!1BXXK+yuq`2I+Wk! zo!1=6To-a#eRqQ>k(*EpYp37}cQCDk(N&TU)tgUnuO3>8@*VW4SP5SU zrW@RHUMNZ)Qf9PzSlL@@n+HeF>FOg6Q@(u*eXLx|;7gS$qGL2=);@@5F!Sr-`2moP zx&D~GcIbJfPGmfS56n!Pts9Xny~UrtvPin2wK{B;2tEMCM9AcjVG>mi6?(nERcI7D z7?m?@>VmkSo<7o%A@T(RZsvXAO(^$1Pxf;jA#j4gm?E6jlP@%!!;poUE@Du_)|4+fHm|E1<-}IyMJ8XF%Xk?a8 zKDPfiH30AD9MuXQ*|rBUxGv~gC*-;#VzlUT5@i_v)tbK31vkZ2Rsc0dHh{A)rwWhJ zVW1KHFYitRm5i7nT$qfg>Iv4f^KET;so173C7K}p{EaXTKCMHQ$6*upjgu?NZqFpU zsJr0K*~vH~ycTs=l}0K`z`E?1i0(%s0>xH{5EZoCE$9ae(*!?(cU-0-tT5Q1CK8o6~VFK+TV! z`%!>;3ZOiPFrHbJQksi|rH^^#ukM7U)L_7bE6G3S1yF8BXavJi0dbo5B*3cRGl*&Q zfUlg5@~2aHP|A-e!z%#72ZDF(z$$kF#@LZ!V7o*aUgR8RKD)0IEcIW6_#XcY@1br> zrOq88BJ)2>nuMrPzxiB>v(DKq$)?x$jen1qrp6oZ2IUmrYF$W=nGrv)!{;677(C}v zEl}sIP-UzoHfX@cGiYkjEIm_nc z4S5>X!GqoaItZ{Et1L}yObJ+ZJ2SS>{x`tX*Qx%*k;uwh@(5ezrQk9ysBf}jlB*tA zA=#7F4mnBttLu6w<2AP<#_RK;FV&)4Zm>V4-~Eh~#$Xbq+0o@%KKe}MJ-yFV$e?QG ztWUOh*JNBSq8*m?_hrE9W>jf5`@U__xDDbI$giOB;rrSpjGL(ts>@u7Vqsw1&; z!RLY{{nW054qs_SQ8bF!@fCqrbJHBfHQI~voBNsX1T@eZ-JDx3rl55u-0!)2fS|`Z zuY1(?z`aE$P;A35VobJL~;Wh zqS56e=lw_kYeE)|Ioept%vrp9<4vK}UV*+y?{x&Hh`)-SG_dfYOB@s}u>2OiNSOb1 z*vb%bESkQm`#>5=G3Qvp{H*!M-NwlM zB|7!pjZC1AN$pQ*oO8FC;2FirYLW!jc>n}{>2k?MeJIA5vD z1k)|dPOPxroy>QOgXsZ2bZrICsLk$^Jgc|pAmU<|v3$f2QA1=V?;l-@QoGdTu(~9U zae01C5EX3Wdzi7uS00UageUvLh;xgJFy)7;03u;%f1vwF>eTGWYLqxe@#K1x@Htk; zZT0*}(THyerfXvUyo^P{_eW=*6zRnusoFGejfo;vpMXbf!0!lk+*`E<^H^1Sm3D^P z&#)omgN|G}Ha)UxbQLWAv*qKJNZ*siF(}lG!_}l}yMU>&2iGenf{5+Qqdd&S%l=hn z-2L^TvZvh5hBo|kzF+`2R!6zi_lKq7Mz{@DL~*?KAC-3aWL;Gx;m;p=D2~AxzE&6!GeEq&7G9q7^aP7hWHZL)>uP3Y^84a za7wZC8xFsPY+;Hg(b`BR`651`%=qyRxPrTsW&91gn^|ovc;M`F`9^Z%tgZhzyvR{a z$9X>49n$>+>JR*W?-nbKP>(dRaopO(Sj@N`)4hClOeYmv&nnqhYz7Kgv>XOFqV(Dg zv-K#3*eGLB8M}z)$(>-Zko4IltR0Xrx_YZ8!8<}w6??*(EecNa4!b| zVD^wL19;de4@9-0XWLj8X0Jnit?cb;@w-#tz3Z;8GDS!-M^UaEX|(0K5~(KdqKf?* zxTy70o$#(Je!K^aMe`sSWL{lg_{y^@p|r%h{bgTxx<-`UN+j^`?wMS1F(y2cZvOU1^86=t3$RGVHf$2dWa?rlXfb)DH}%}b;YXZIIT3Q1HchL27-M=MSn*RNK;YEk zq*xCclQ&S9o%VJ!U=4M^r)IiknzCXz1w4#uB=5~p&`hq#+YF<=l5o`^N&$spZ6V*- zfm;y~Vq%$sf9zxK?*U}&OnqVY=6J+A?j-K9DBeg;YCpr_`V$X5)xP5eo4v_@Fs{oP zv$e#?uid|M4(X;e&sHo9UUhQ}Izziq=U2sdM)>g80 zr)<~i)uwxAXff!|7rp*D0@U?nO1nTtr*I_;4UVeIZ6lX1J0cxr?)xc-jRL8C7I>?X z5rv-32Df1{gp4mxHMcAG$la^S;&R%=g7)q;0*!oiTV!l31o>k0a z&V3#X29pPYl)xa^aNXwLFtmKX=lBaS^+pyfK)u7}NxXl9j_*p_j0!y?3sKl-W$NVo z3-{5^R0D6UzEn_+}`(bNq@;a4qIK%CCoFB5|5pOpLOK~51^GxAvy z?7fz^xy{yc>Q5n5=zuWUsz+!lFFkgmnDt}DjJzKdCsfu;am=9%D>InO51RJGhr^*M z9;dgF2ldqh)o6=z*U7-2u)*Eilt|Bzk%Visj!R#8>RG&HF~wTsc!h2x5hSEnFUb_O zk1jnS^=-pQ&^Yb|p`|z`O_aiEfEx<$$4>I)OPl@an$m0Hsf&4GqdIFLfI5CyRTKx0 zspZCqJ`v(_4wzLG#3^jgxD{UIR&)sx$6Q7X9WH|#OH+&|mi##sQC?bckE$!?Bs{w2 zNdS&lwt5taEXfi#7YQ*Y#xIOFRW7ATD(=oc*d)nZoAZMUv(0sG{Kl3wMqOL%n!n@N zFU6%ge>K{%nYK@)-jyMW$(~yQtrr&-T z=vm|4P-uJJIQ3Z<8Fd~n=uWtdP4q3aANs~a`CZJ@B3+GNtGVjA@CJHFU5n94EJy{u zv({56Ob*uY|N3o_V}AAC=q+0S{cG>*v+iECu^datNfgJppUFg_&N|w^R3~%%^6$BF zzXn3qDaKy*z@-HxTFICf4R2m5AcLN5^>VebCiSBo)p_vOuyRh+m&5`ft3Zf@mb;l4 z;;bmU4a^qxK3dsEvUEW(Z*^0DUgSNOW(gPEL$`r6LaHUmHLsRIGL1fDt)l4GySlkk zsE0_m=|{)IiIM7w#Fs$z9O#MsPJG$TG|(U3f}XAQ%u)vlp-|s-qaTi( zbgx(XGO`~O3~4>UNXVFrVhXmFV|~Bf=WMe$+N>q2&rq^J2tZ^SliutBaC!AFy(p4G zPQdCT#PeSVnb`?TO^Z|3GkaGSIYtNG`EIStogDySSo`=XaGM`Sd&Ez>*lZzRTj5O4 zCim{^rC+a>WXhg)H#>*~1xijuSrCHF9V@B^2xN|frca9`GbORE7agU*-J*wx9(W*k z1KP2_C{Cv(AheGM%GioqPoT$6V+r|a*lCdObqp*yijnAHd*iHHnZ^KNB+|}68?<-= zh>sNC`hhCB$D$Zog#9rsb`=%D>K5-#Y}<>3%pAaTnOX2dohpbUF%nvzqbFKUt{ZKs zu*XO5eAkuRw6iWZxW_t5VQN{4?`!yVkiAEG;};R~A=(!b&8Fu60nwBp?|d7-#=^rj^4=E|+rHT}uGPkM3K78BsBZFVXU4M`JF7G_ir% z=TD@t)$Ct@lv0MJXYb_O7$2>AC^vG^TddD(1a`eG5(*0P)0m=ct>=~qs-c2EdJTz( z5xX%C{n={K&aT4lejyoIN~Dk6i+sE2%Hiyncnql_f8L@tv+!LELdpBYqa~sdC7^<} zbFWc%zhAiEXkQkfPBTfm2#O&(2Fd^Fh#N{;erk+#jhSsu;`R8O=hME!MukoXP@szw zF$I9%@I?^T%#Dd;lm@EjQsbaeEMSX6{eTWL{XpKE=`iMJV#kg!qU|3{#}#LX3I}|D zq=OffsLr&TdQC#d%a*QoBcUyfFC?}|fpdGKJOt<@kT|ub3E)Z}u+-64;0+f3nWq7X zdH9r%QK)U!`Q+SVDO<^43n#O@w6U6L-H~`AoL46w{xUv%p?$g|ttk`H9V5aY_lMPJ zZ#k)TD`^>w^VrF=5RezTMF#(43WF5GvwOe;^o-0-p_E(|9)0QN+m&{iFT+jwfDN?~ zKgKMc`~(d$*r~w}Rmc}lcc*b~a`60gg^CrjGEUhDO;Jx1q(P9Yvg-Uq-+GpVk7#7jO!T!_6=rLV|AwlwwqY2!RknY% z8wYkuOfI+%%`M=gozwy8F`#R$(B61=BDQ;d(4{InmnthvzCCw>*4BO@Z&lX?C!$0m z#gk${7Ms`w5rp4-a9*s4K6nx9n20E@69 zqrc;3@<>_yCNf8NJ5HHP+FfgNTJ%hf3K;}Nb`C(Zz~i~s(2n$p39-@^DCbhZG#pxo zD852=e1dleyW7${gS* zaphVhQ-|b;)kR4r0Nx-2kh|HRL3w=^zi#xFuLdpUBUawP^CZJN)~Ua>m?w^5FQfwv z0$~I8vz~_$gSeLu(U@hB4G(NSG$>o`O9Cim4glbXlcUlsLr6n#)Xs!U!uH`^M`_Vc z$>ic-oEo<)f0U1_sKq@v%8A@BtgmvZOaNBi_=x2k74qOITdjwhB=!d2uT?;7l#U2F zYF}XcTa61ej0#Pvp9wX*YPtZYWdj%>>tee_vKp6Tn9}2tH{|vVd#e0(4Mvx zeguD&K>O^#Rm=xCP!h}%>U|;VtgrBB9vXu&x4#ypf>Z#gU<)yv1eQzs@h8nICLOn= z8BPT25?_1HM36BwrC7rF7JVtSs;p0x6_7c-JWTOdQX>&ga^!#h4+9AODhGj`rds9mQrY z4*e6wi3dovfuTU@$-BTHkY#P&Z`hP9wBC)om@t;&toadJtl%ni^dirYXw;q7#D_&j zh$PNabe;4%_Y^FoV`V055v60bfYCh@&ij;h89)hpYY1{R;#zF9{Vv6VMi`uUflz0( zfv5vaTg!+MZ+?tV>Ta;Yqv6)J{IBePuzH>79kx7G7aSmm4`2UwqP4zH8oeF%V~Jc& z>7H6Gv7dp6I)8&9>LDYM^wXW5cybudXgIl+aMV7=Nv6K{DL+QsP8u7VS#$?D+Pl5P zYWN4dx}y*zsx7}S`niR|V7fSDb zJaDvhx-ZGa0siIvG~_mSRgwwt{x^~J6pE<$Vyd9d6G$JjJ4(Oo=fwN z^USu6po7#!pt-u-@nPFOQGU>WwFskMO8xd(fja`LkUNoevWK_VerxJ_4cd{{MRE?% z32;Tt$Kb_28HZO2=qba@vXV5aqPYX&8Jeup~aB`dq{hI%-0AnmdnlF375 z|6sn>f+v=Z2hD==4-;iPY5ZNGRnEFu0J(WKK_LVhZ{nJ zCoz_lCIgk{o0;Hv5Lgj8|LJ}%OYa$SMOMI0EV@o7&tdL+1o;uTss|OcKh8Rsh)o?z*+vJRKP|8a=Zi8k&c`k+QRRB71>|-Jq~ha;(vR!nx=~SJqS2D z^BU}mqt3=62g{VVBLXan{$C3a29AONxQqlFCKbPDWBOkz^7$WL(T<%KG&$ev3^PC;Ao?b#yu7Tf86iXhD zKuo_90S!>8;G(5w8}PgKr7W;H1H=zubHK5KqdTJAeTaMupB;*5L&cpkTv4QsMMVa+ zZyICyG1EWdt&^Hdq-uws))xMp8Y#MbLbDDIA`;`)$B0r-!uRV_E=kodm1xnYPrt&7 z=p?sB+P03U-i9%&SK_Mm^rO`5DQl}>myYjq^GdN;Y%x__e!SmIY_YDkwELCCQ1=6i zz#J(LlR<*oEy(cK&!NF~zla9Tb}U4Pp5Xh-J{|IpnZsjzh_+wSRr`cmoT2#0x#Gjh zK64S>O%q+(SwAmdU1;nQtkv{O;zTP*h`$xyeUz%d^>k$#E& z!f4dXgiWF>8Gh`wuYu>ipWwCo>f!uSsr3tU+5>YfRnvWsc?YQ1hPN3XGtT6dB;t!p z-rh}gJ=r>)Yh$NACHI)W@53L5Or>%kyR|J&6!~i>V~Z8F7EN``;>5E2743!w9x)y0 zegrve&w_E$6HUX3Jple8DM66GmPzDI&1P@W?{iwRBUn~U{ZRj~U&&IG zb8%^viya0wTRkvrAe+(ZH+_I?EqkNqo638Bgayk}&*1ENX!5kFKdP{`YTFhQjfK?d z5bJ%x?~EL?5;_YTDk(HKTKu&@$oH_mYhnT#mHEJ7S0& zR`AKnPSlPGcJPSn`8!z{yX5DadcNQfLaTPM5GL9;@nKiv4LgdLc+QW^OOz!cJ5eOU zg4@%R1)dM>>=KOIJ%?|{URzqdn!l!X*<_G&wRj*?7mg|+p@n1072K>~%tevjId2Pf zDybgk{keKs47jqlY}!<`&b@7DOYQ%){xMjvmKIuyRN4Ff_c zeDz{e{3xY~1Neo9;N(E}iSXxsIceS9C8V;r5-FGj4Z7GRzkEb`_=wXD_Q=3b;<7oa zU>|%WyYLsW^zg~E$*ikR=3@i8QPAkj%|>gxt+Tui%i4%p7`A@5r^p;*Hwwd5aV3jA zd$1ar1+Wi?5ec@zP>hLM_~8<@En(>}xYgp;Q9`QypHP6YKU7enWzZZ$kyFkO3?k1&0yIf$_FDS_d)9=?R@uXc!rWsxc7#n z_5#1_G28e~L)=mcVPwt0YtZM8xE@qbvr1~K^@ve8n&TeNG8a@zX#5fbty&{4Mi(o( zwj|CsDmWZ9sE30ZN)_8m+K0#oVasMKfzAPcJBnu`9&cY}^VVi7#NiZr=wYPrUUaP} z$V`t2??@I?XRchr6^w`S=j9^%OL%*&Pf&9g`@ogV#1TeqUIu4gje2cK6BiOPMQ=l) zzqS@+43ToE7pSu7LsreM`Rz&@4uh$brIzrqE|u#@R3{}T4@4e|JcV=B>W&OaxL#iE z%|e4jF&&(CLZuwU1M`jo&DR94-z87Wp*JsOXtGs%GBy=efsjZuEG8_^zG)vo{6G) z7t6A;MT$s>nuhJ;D58KWl7JZA{zVfUW3z#{h4D9QoBxxONKOB&aoKwtNV+C=MA@<( z{G6@pgV`X1?Xe-gKhakbLjjKr0m-X&VjtSu_ZRmqCS@v&gOkr7azHBk1jEiJ!VZ}1 zv+&VVK&9 zv44}2s{e_Uto!%F|IcJXSxb=5R{|b$IO~zEdDP6||9d7S{Qu6LHtR2c97!G2t(4;@ zp~Zc%*Rn0t*9`f}5{e3C?R`0&1Yo{Hq)g?Q{;~ryvx_ylS!>|MfB!MIuLvfXA3iT2 ST-$`aYk)Swf|ItD{i> literal 0 HcmV?d00001 diff --git a/docs/_spec/public/images/github-logo@2x.png b/docs/_spec/public/images/github-logo@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..285b0fee2f32c760121718e28cc489c6eb2ca884 GIT binary patch literal 1753 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%u1Od5hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|80+w{G(j&jGsViy$k^G<#l*G&eP`1g19yq1PCvUQklVEdbi=l3J8mmYU*Ll%J~r_OewbZnqfVG!Lpb z1-Dxaaq86vIz}H9u}BdO69T3l5EGtkfgE_kPt60S_99@i-f;Hl9$;?q^>lFzskpUf zy0=GYph(;MuWM(l*OpYv;1Wh}Z97|oif!##Yc~M3ZzlPT7%udh43R=Mn z+0q^?P@kgY%5Tc?Q zr#2OBkZo~bj9^kZAn}5E?gCjG#>ev(bu~zcG*uaheas8`u6Ckd)LtT$?b3nD0M?)> z^-OyhZ4)N!OI^S*rI9DJTH-d)6>i~Jay5u;0RSi>0v#Ek& z-?^n{qb9KWt=f_AIBRx;cG{Ci)`vfp-&;Q0uSnw(-xHG`$F^Pla(&g0>1!EgPRq2b z;S_pVpD`_5e+T=o18YA0XPkd9_CoTlJK^gO|5RJ{bK!sSg4z6OXW6eyMef-;ga1yX z>~7uXd+G;y<1sH$o)EM=e{nS_>qO#(X zcpUf9nTHQ;Sf$YJzp9^UlFuH+xXmsO6Ixx0yL^9YDDbHDE~r;ya;w;X$K=ox@36ZS{|-9n=yv@3q_5#$tLYX2a#0 zl~?V0{)xN@e=2)s+mU%)i);jzEmB(*!yfiwpHR;c!=jVi{kvo?pUp}ul6fv$D&cvp zsKxrRquu%Bx(kc?BC{`To8N5ltjLF5;Kz+o_l5T!p3`W=&*XH*@X)F2XK(UWh(4$hF3l=El6>jguDa*k zf(xdvcq1&#{i34j^_~<%`{ma*9gNFcswfw?^N+3kv$ET)^BThpUa7u_XJljGYQA-< Te@gmWP-Wxk>gTe~DWM4f?xCf( literal 0 HcmV?d00001 diff --git a/docs/_spec/public/images/scala-spiral-white.png b/docs/_spec/public/images/scala-spiral-white.png new file mode 100644 index 0000000000000000000000000000000000000000..46aaf80824c1b820733039260d6e37733a276a32 GIT binary patch literal 1442 zcmeAS@N?(olHy`uVBq!ia0vp^azJdr!3HGRrk#2Mq$EpRBT9nv(@M${i&7aJQ}UBi z6+Ckj(^G>|6H_V+Po~;1Ffc1+hD4M^`1)8S=jZArg4F0$^BXQ!4Z zB&DWj=GiK}-@RW+Av48RDcsc8z_-9TH6zobswg$M$}c3jDm&RSMakYy!KT6rXh3di zNuokUZcbjYRfVk**jy_h8zii+qySb@l5ML5aa4qFfP!;=QL2Keo`G(%fti7VnW3Jc zv5C34xsHO7fuVuEfswwUk*=Y+m9dePfq?=PC;@FNN=dT{a&d#&1?1T(Wt5Z@Sn2DR zmzV368|&p4rRy77T3Uk4Ff!5ws?aU2%qvN((9J7WhMC}!TAW;zSx}OhpQivaF)=B> zw8T~k=u(Imatq+b<`qMO2^e7d6^RAATTyF! zZ>Ia|Gds#0^9@=kbR}BDNm=EH{<3m zb;TBGN6#x&Zu30Vgi}}VzU@&qDL!rc&42aAn`c)VEn3EPcH7$}_YZMIux*P!#1g?= zIor2F)k&N^*dfmSOu9>Jlf*8G?*5Nf@1E>GQSzh ztaB3>N`fW+8O}9m2(-I$e4qFh0~;SljxEh@7mg%!eyDu@@{9L1uVV*I!j~~$*}1V% zWs616?ulCuPLu3dt+{QLq8)?h{v-Mc@^6})3tml)aD8N$Y?%C}W^G&ir2eUY* z=}z>EkK}naw@ZFT*!MYSSyL_9UoLs~)XbVQ?TOv`?#(?eCzVf5lWboG)I4D)ulGlx z$2mI=dE}&Y95G3}5onbtz{}HZcIniL?xK}lf7VXhc=Jren@78~-(N_ZcdK{GrzZmH z)2jlltJIqIS_%9wFT9n%(dN~frl}MBjVhh)ut+;`oL;?jn&6DI2g?<_cK+?t<+|jv zdVRL(VlVr3J#AWfQ)jvEj4z$XTD~PRCBWwK)9(Kw2@EFeJ;v9I8=FC;rl+f)%Q~lo FCIHvI5+DEo literal 0 HcmV?d00001 diff --git a/docs/_spec/public/octicons/LICENSE.txt b/docs/_spec/public/octicons/LICENSE.txt new file mode 100644 index 000000000000..259b43d14de3 --- /dev/null +++ b/docs/_spec/public/octicons/LICENSE.txt @@ -0,0 +1,9 @@ +(c) 2012-2014 GitHub + +When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) + +Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) +Applies to all font files + +Code License: MIT (http://choosealicense.com/licenses/mit/) +Applies to all other files diff --git a/docs/_spec/public/octicons/octicons.css b/docs/_spec/public/octicons/octicons.css new file mode 100644 index 000000000000..a5dcd153a856 --- /dev/null +++ b/docs/_spec/public/octicons/octicons.css @@ -0,0 +1,235 @@ +@font-face { + font-family: 'octicons'; + src: url('octicons.eot?#iefix') format('embedded-opentype'), + url('octicons.woff') format('woff'), + url('octicons.ttf') format('truetype'), + url('octicons.svg#octicons') format('svg'); + font-weight: normal; + font-style: normal; +} + +/* + +.octicon is optimized for 16px. +.mega-octicon is optimized for 32px but can be used larger. + +*/ +.octicon, .mega-octicon { + font: normal normal normal 16px/1 octicons; + display: inline-block; + text-decoration: none; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.mega-octicon { font-size: 32px; } + + +.octicon-alert:before { content: '\f02d'} /*  */ +.octicon-alignment-align:before { content: '\f08a'} /*  */ +.octicon-alignment-aligned-to:before { content: '\f08e'} /*  */ +.octicon-alignment-unalign:before { content: '\f08b'} /*  */ +.octicon-arrow-down:before { content: '\f03f'} /*  */ +.octicon-arrow-left:before { content: '\f040'} /*  */ +.octicon-arrow-right:before { content: '\f03e'} /*  */ +.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ +.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ +.octicon-arrow-small-right:before { content: '\f071'} /*  */ +.octicon-arrow-small-up:before { content: '\f09f'} /*  */ +.octicon-arrow-up:before { content: '\f03d'} /*  */ +.octicon-beer:before { content: '\f069'} /*  */ +.octicon-book:before { content: '\f007'} /*  */ +.octicon-bookmark:before { content: '\f07b'} /*  */ +.octicon-briefcase:before { content: '\f0d3'} /*  */ +.octicon-broadcast:before { content: '\f048'} /*  */ +.octicon-browser:before { content: '\f0c5'} /*  */ +.octicon-bug:before { content: '\f091'} /*  */ +.octicon-calendar:before { content: '\f068'} /*  */ +.octicon-check:before { content: '\f03a'} /*  */ +.octicon-checklist:before { content: '\f076'} /*  */ +.octicon-chevron-down:before { content: '\f0a3'} /*  */ +.octicon-chevron-left:before { content: '\f0a4'} /*  */ +.octicon-chevron-right:before { content: '\f078'} /*  */ +.octicon-chevron-up:before { content: '\f0a2'} /*  */ +.octicon-circle-slash:before { content: '\f084'} /*  */ +.octicon-circuit-board:before { content: '\f0d6'} /*  */ +.octicon-clippy:before { content: '\f035'} /*  */ +.octicon-clock:before { content: '\f046'} /*  */ +.octicon-cloud-download:before { content: '\f00b'} /*  */ +.octicon-cloud-upload:before { content: '\f00c'} /*  */ +.octicon-code:before { content: '\f05f'} /*  */ +.octicon-color-mode:before { content: '\f065'} /*  */ +.octicon-comment-add:before, +.octicon-comment:before { content: '\f02b'} /*  */ +.octicon-comment-discussion:before { content: '\f04f'} /*  */ +.octicon-credit-card:before { content: '\f045'} /*  */ +.octicon-dash:before { content: '\f0ca'} /*  */ +.octicon-dashboard:before { content: '\f07d'} /*  */ +.octicon-database:before { content: '\f096'} /*  */ +.octicon-device-camera:before { content: '\f056'} /*  */ +.octicon-device-camera-video:before { content: '\f057'} /*  */ +.octicon-device-desktop:before { content: '\f27c'} /*  */ +.octicon-device-mobile:before { content: '\f038'} /*  */ +.octicon-diff:before { content: '\f04d'} /*  */ +.octicon-diff-added:before { content: '\f06b'} /*  */ +.octicon-diff-ignored:before { content: '\f099'} /*  */ +.octicon-diff-modified:before { content: '\f06d'} /*  */ +.octicon-diff-removed:before { content: '\f06c'} /*  */ +.octicon-diff-renamed:before { content: '\f06e'} /*  */ +.octicon-ellipsis:before { content: '\f09a'} /*  */ +.octicon-eye-unwatch:before, +.octicon-eye-watch:before, +.octicon-eye:before { content: '\f04e'} /*  */ +.octicon-file-binary:before { content: '\f094'} /*  */ +.octicon-file-code:before { content: '\f010'} /*  */ +.octicon-file-directory:before { content: '\f016'} /*  */ +.octicon-file-media:before { content: '\f012'} /*  */ +.octicon-file-pdf:before { content: '\f014'} /*  */ +.octicon-file-submodule:before { content: '\f017'} /*  */ +.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ +.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ +.octicon-file-text:before { content: '\f011'} /*  */ +.octicon-file-zip:before { content: '\f013'} /*  */ +.octicon-flame:before { content: '\f0d2'} /*  */ +.octicon-fold:before { content: '\f0cc'} /*  */ +.octicon-gear:before { content: '\f02f'} /*  */ +.octicon-gift:before { content: '\f042'} /*  */ +.octicon-gist:before { content: '\f00e'} /*  */ +.octicon-gist-secret:before { content: '\f08c'} /*  */ +.octicon-git-branch-create:before, +.octicon-git-branch-delete:before, +.octicon-git-branch:before { content: '\f020'} /*  */ +.octicon-git-commit:before { content: '\f01f'} /*  */ +.octicon-git-compare:before { content: '\f0ac'} /*  */ +.octicon-git-merge:before { content: '\f023'} /*  */ +.octicon-git-pull-request-abandoned:before, +.octicon-git-pull-request:before { content: '\f009'} /*  */ +.octicon-globe:before { content: '\f0b6'} /*  */ +.octicon-graph:before { content: '\f043'} /*  */ +.octicon-heart:before { content: '\2665'} /* ♥ */ +.octicon-history:before { content: '\f07e'} /*  */ +.octicon-home:before { content: '\f08d'} /*  */ +.octicon-horizontal-rule:before { content: '\f070'} /*  */ +.octicon-hourglass:before { content: '\f09e'} /*  */ +.octicon-hubot:before { content: '\f09d'} /*  */ +.octicon-inbox:before { content: '\f0cf'} /*  */ +.octicon-info:before { content: '\f059'} /*  */ +.octicon-issue-closed:before { content: '\f028'} /*  */ +.octicon-issue-opened:before { content: '\f026'} /*  */ +.octicon-issue-reopened:before { content: '\f027'} /*  */ +.octicon-jersey:before { content: '\f019'} /*  */ +.octicon-jump-down:before { content: '\f072'} /*  */ +.octicon-jump-left:before { content: '\f0a5'} /*  */ +.octicon-jump-right:before { content: '\f0a6'} /*  */ +.octicon-jump-up:before { content: '\f073'} /*  */ +.octicon-key:before { content: '\f049'} /*  */ +.octicon-keyboard:before { content: '\f00d'} /*  */ +.octicon-law:before { content: '\f0d8'} /* */ +.octicon-light-bulb:before { content: '\f000'} /*  */ +.octicon-link:before { content: '\f05c'} /*  */ +.octicon-link-external:before { content: '\f07f'} /*  */ +.octicon-list-ordered:before { content: '\f062'} /*  */ +.octicon-list-unordered:before { content: '\f061'} /*  */ +.octicon-location:before { content: '\f060'} /*  */ +.octicon-gist-private:before, +.octicon-mirror-private:before, +.octicon-git-fork-private:before, +.octicon-lock:before { content: '\f06a'} /*  */ +.octicon-logo-github:before { content: '\f092'} /*  */ +.octicon-mail:before { content: '\f03b'} /*  */ +.octicon-mail-read:before { content: '\f03c'} /*  */ +.octicon-mail-reply:before { content: '\f051'} /*  */ +.octicon-mark-github:before { content: '\f00a'} /*  */ +.octicon-markdown:before { content: '\f0c9'} /*  */ +.octicon-megaphone:before { content: '\f077'} /*  */ +.octicon-mention:before { content: '\f0be'} /*  */ +.octicon-microscope:before { content: '\f089'} /*  */ +.octicon-milestone:before { content: '\f075'} /*  */ +.octicon-mirror-public:before, +.octicon-mirror:before { content: '\f024'} /*  */ +.octicon-mortar-board:before { content: '\f0d7'} /* */ +.octicon-move-down:before { content: '\f0a8'} /*  */ +.octicon-move-left:before { content: '\f074'} /*  */ +.octicon-move-right:before { content: '\f0a9'} /*  */ +.octicon-move-up:before { content: '\f0a7'} /*  */ +.octicon-mute:before { content: '\f080'} /*  */ +.octicon-no-newline:before { content: '\f09c'} /*  */ +.octicon-octoface:before { content: '\f008'} /*  */ +.octicon-organization:before { content: '\f037'} /*  */ +.octicon-package:before { content: '\f0c4'} /*  */ +.octicon-paintcan:before { content: '\f0d1'} /*  */ +.octicon-pencil:before { content: '\f058'} /*  */ +.octicon-person-add:before, +.octicon-person-follow:before, +.octicon-person:before { content: '\f018'} /*  */ +.octicon-pin:before { content: '\f041'} /*  */ +.octicon-playback-fast-forward:before { content: '\f0bd'} /*  */ +.octicon-playback-pause:before { content: '\f0bb'} /*  */ +.octicon-playback-play:before { content: '\f0bf'} /*  */ +.octicon-playback-rewind:before { content: '\f0bc'} /*  */ +.octicon-plug:before { content: '\f0d4'} /*  */ +.octicon-repo-create:before, +.octicon-gist-new:before, +.octicon-file-directory-create:before, +.octicon-file-add:before, +.octicon-plus:before { content: '\f05d'} /*  */ +.octicon-podium:before { content: '\f0af'} /*  */ +.octicon-primitive-dot:before { content: '\f052'} /*  */ +.octicon-primitive-square:before { content: '\f053'} /*  */ +.octicon-pulse:before { content: '\f085'} /*  */ +.octicon-puzzle:before { content: '\f0c0'} /*  */ +.octicon-question:before { content: '\f02c'} /*  */ +.octicon-quote:before { content: '\f063'} /*  */ +.octicon-radio-tower:before { content: '\f030'} /*  */ +.octicon-repo-delete:before, +.octicon-repo:before { content: '\f001'} /*  */ +.octicon-repo-clone:before { content: '\f04c'} /*  */ +.octicon-repo-force-push:before { content: '\f04a'} /*  */ +.octicon-gist-fork:before, +.octicon-repo-forked:before { content: '\f002'} /*  */ +.octicon-repo-pull:before { content: '\f006'} /*  */ +.octicon-repo-push:before { content: '\f005'} /*  */ +.octicon-rocket:before { content: '\f033'} /*  */ +.octicon-rss:before { content: '\f034'} /*  */ +.octicon-ruby:before { content: '\f047'} /*  */ +.octicon-screen-full:before { content: '\f066'} /*  */ +.octicon-screen-normal:before { content: '\f067'} /*  */ +.octicon-search-save:before, +.octicon-search:before { content: '\f02e'} /*  */ +.octicon-server:before { content: '\f097'} /*  */ +.octicon-settings:before { content: '\f07c'} /*  */ +.octicon-log-in:before, +.octicon-sign-in:before { content: '\f036'} /*  */ +.octicon-log-out:before, +.octicon-sign-out:before { content: '\f032'} /*  */ +.octicon-split:before { content: '\f0c6'} /*  */ +.octicon-squirrel:before { content: '\f0b2'} /*  */ +.octicon-star-add:before, +.octicon-star-delete:before, +.octicon-star:before { content: '\f02a'} /*  */ +.octicon-steps:before { content: '\f0c7'} /*  */ +.octicon-stop:before { content: '\f08f'} /*  */ +.octicon-repo-sync:before, +.octicon-sync:before { content: '\f087'} /*  */ +.octicon-tag-remove:before, +.octicon-tag-add:before, +.octicon-tag:before { content: '\f015'} /*  */ +.octicon-telescope:before { content: '\f088'} /*  */ +.octicon-terminal:before { content: '\f0c8'} /*  */ +.octicon-three-bars:before { content: '\f05e'} /*  */ +.octicon-tools:before { content: '\f031'} /*  */ +.octicon-trashcan:before { content: '\f0d0'} /*  */ +.octicon-triangle-down:before { content: '\f05b'} /*  */ +.octicon-triangle-left:before { content: '\f044'} /*  */ +.octicon-triangle-right:before { content: '\f05a'} /*  */ +.octicon-triangle-up:before { content: '\f0aa'} /*  */ +.octicon-unfold:before { content: '\f039'} /*  */ +.octicon-unmute:before { content: '\f0ba'} /*  */ +.octicon-versions:before { content: '\f064'} /*  */ +.octicon-remove-close:before, +.octicon-x:before { content: '\f081'} /*  */ +.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/docs/_spec/public/octicons/octicons.eot b/docs/_spec/public/octicons/octicons.eot new file mode 100644 index 0000000000000000000000000000000000000000..22881a8b6c4342720d2d4318ef5887c8abbfc8a2 GIT binary patch literal 31440 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 + + + +(c) 2012-2014 GitHub + +When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) + +Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) +Applies to all font files + +Code License: MIT (http://choosealicense.com/licenses/mit/) +Applies to all other files + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/_spec/public/octicons/octicons.ttf b/docs/_spec/public/octicons/octicons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..189ca2813d4920f3be1bd1a43b19a09740d1eb2c GIT binary patch literal 31272 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 F{||dHN2&k- literal 0 HcmV?d00001 diff --git a/docs/_spec/public/octicons/octicons.woff b/docs/_spec/public/octicons/octicons.woff new file mode 100644 index 0000000000000000000000000000000000000000..2b770e429f38d820c1916bb395f7e72ddafa60c5 GIT binary patch literal 17492 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/gm,">")}function t(v){return v.nodeName.toLowerCase()}function h(w,x){var v=w&&w.exec(x);return v&&v.index==0}function r(w){var v=(w.className+" "+(w.parentNode?w.parentNode.className:"")).split(/\s+/);v=v.map(function(x){return x.replace(/^lang(uage)?-/,"")});return v.filter(function(x){return i(x)||/no(-?)highlight/.test(x)})[0]}function o(x,y){var v={};for(var w in x){v[w]=x[w]}if(y){for(var w in y){v[w]=y[w]}}return v}function u(x){var v=[];(function w(y,z){for(var A=y.firstChild;A;A=A.nextSibling){if(A.nodeType==3){z+=A.nodeValue.length}else{if(A.nodeType==1){v.push({event:"start",offset:z,node:A});z=w(A,z);if(!t(A).match(/br|hr|img|input/)){v.push({event:"stop",offset:z,node:A})}}}}return z})(x,0);return v}function q(w,y,C){var x=0;var F="";var z=[];function B(){if(!w.length||!y.length){return w.length?w:y}if(w[0].offset!=y[0].offset){return(w[0].offset"}function E(G){F+=""}function v(G){(G.event=="start"?A:E)(G.node)}while(w.length||y.length){var D=B();F+=j(C.substr(x,D[0].offset-x));x=D[0].offset;if(D==w){z.reverse().forEach(E);do{v(D.splice(0,1)[0]);D=B()}while(D==w&&D.length&&D[0].offset==x);z.reverse().forEach(A)}else{if(D[0].event=="start"){z.push(D[0].node)}else{z.pop()}v(D.splice(0,1)[0])}}return F+j(C.substr(x))}function m(y){function v(z){return(z&&z.source)||z}function w(A,z){return RegExp(v(A),"m"+(y.cI?"i":"")+(z?"g":""))}function x(D,C){if(D.compiled){return}D.compiled=true;D.k=D.k||D.bK;if(D.k){var z={};var E=function(G,F){if(y.cI){F=F.toLowerCase()}F.split(" ").forEach(function(H){var I=H.split("|");z[I[0]]=[G,I[1]?Number(I[1]):1]})};if(typeof D.k=="string"){E("keyword",D.k)}else{Object.keys(D.k).forEach(function(F){E(F,D.k[F])})}D.k=z}D.lR=w(D.l||/\b[A-Za-z0-9_]+\b/,true);if(C){if(D.bK){D.b="\\b("+D.bK.split(" ").join("|")+")\\b"}if(!D.b){D.b=/\B|\b/}D.bR=w(D.b);if(!D.e&&!D.eW){D.e=/\B|\b/}if(D.e){D.eR=w(D.e)}D.tE=v(D.e)||"";if(D.eW&&C.tE){D.tE+=(D.e?"|":"")+C.tE}}if(D.i){D.iR=w(D.i)}if(D.r===undefined){D.r=1}if(!D.c){D.c=[]}var B=[];D.c.forEach(function(F){if(F.v){F.v.forEach(function(G){B.push(o(F,G))})}else{B.push(F=="self"?D:F)}});D.c=B;D.c.forEach(function(F){x(F,D)});if(D.starts){x(D.starts,C)}var A=D.c.map(function(F){return F.bK?"\\.?("+F.b+")\\.?":F.b}).concat([D.tE,D.i]).map(v).filter(Boolean);D.t=A.length?w(A.join("|"),true):{exec:function(F){return null}}}x(y)}function c(T,L,J,R){function v(V,W){for(var U=0;U";V+=aa+'">';return V+Y+Z}function N(){if(!I.k){return j(C)}var U="";var X=0;I.lR.lastIndex=0;var V=I.lR.exec(C);while(V){U+=j(C.substr(X,V.index-X));var W=E(I,V);if(W){H+=W[1];U+=w(W[0],j(V[0]))}else{U+=j(V[0])}X=I.lR.lastIndex;V=I.lR.exec(C)}return U+j(C.substr(X))}function F(){if(I.sL&&!f[I.sL]){return j(C)}var U=I.sL?c(I.sL,C,true,S):e(C);if(I.r>0){H+=U.r}if(I.subLanguageMode=="continuous"){S=U.top}return w(U.language,U.value,false,true)}function Q(){return I.sL!==undefined?F():N()}function P(W,V){var U=W.cN?w(W.cN,"",true):"";if(W.rB){D+=U;C=""}else{if(W.eB){D+=j(V)+U;C=""}else{D+=U;C=V}}I=Object.create(W,{parent:{value:I}})}function G(U,Y){C+=U;if(Y===undefined){D+=Q();return 0}var W=v(Y,I);if(W){D+=Q();P(W,Y);return W.rB?0:Y.length}var X=z(I,Y);if(X){var V=I;if(!(V.rE||V.eE)){C+=Y}D+=Q();do{if(I.cN){D+=""}H+=I.r;I=I.parent}while(I!=X.parent);if(V.eE){D+=j(Y)}C="";if(X.starts){P(X.starts,"")}return V.rE?0:Y.length}if(A(Y,I)){throw new Error('Illegal lexeme "'+Y+'" for mode "'+(I.cN||"")+'"')}C+=Y;return Y.length||1}var M=i(T);if(!M){throw new Error('Unknown language: "'+T+'"')}m(M);var I=R||M;var S;var D="";for(var K=I;K!=M;K=K.parent){if(K.cN){D=w(K.cN,"",true)+D}}var C="";var H=0;try{var B,y,x=0;while(true){I.t.lastIndex=x;B=I.t.exec(L);if(!B){break}y=G(L.substr(x,B.index-x),B[0]);x=B.index+y}G(L.substr(x));for(var K=I;K.parent;K=K.parent){if(K.cN){D+=""}}return{r:H,value:D,language:T,top:I}}catch(O){if(O.message.indexOf("Illegal")!=-1){return{r:0,value:j(L)}}else{throw O}}}function e(y,x){x=x||b.languages||Object.keys(f);var v={r:0,value:j(y)};var w=v;x.forEach(function(z){if(!i(z)){return}var A=c(z,y,false);A.language=z;if(A.r>w.r){w=A}if(A.r>v.r){w=v;v=A}});if(w.language){v.second_best=w}return v}function g(v){if(b.tabReplace){v=v.replace(/^((<[^>]+>|\t)+)/gm,function(w,z,y,x){return z.replace(/\t/g,b.tabReplace)})}if(b.useBR){v=v.replace(/\n/g,"
    ")}return v}function p(A){var B=r(A);if(/no(-?)highlight/.test(B)){return}var y;if(b.useBR){y=document.createElementNS("http://www.w3.org/1999/xhtml","div");y.innerHTML=A.innerHTML.replace(/\n/g,"").replace(//g,"\n")}else{y=A}var z=y.textContent;var v=B?c(B,z,true):e(z);var x=u(y);if(x.length){var w=document.createElementNS("http://www.w3.org/1999/xhtml","div");w.innerHTML=v.value;v.value=q(x,u(w),z)}v.value=g(v.value);A.innerHTML=v.value;A.className+=" hljs "+(!B&&v.language||"");A.result={language:v.language,re:v.r};if(v.second_best){A.second_best={language:v.second_best.language,re:v.second_best.r}}}var b={classPrefix:"hljs-",tabReplace:null,useBR:false,languages:undefined};function s(v){b=o(b,v)}function l(){if(l.called){return}l.called=true;var v=document.querySelectorAll("pre code");Array.prototype.forEach.call(v,p)}function a(){addEventListener("DOMContentLoaded",l,false);addEventListener("load",l,false)}var f={};var n={};function d(v,x){var w=f[v]=x(this);if(w.aliases){w.aliases.forEach(function(y){n[y]=v})}}function k(){return Object.keys(f)}function i(v){return f[v]||f[n[v]]}this.highlight=c;this.highlightAuto=e;this.fixMarkup=g;this.highlightBlock=p;this.configure=s;this.initHighlighting=l;this.initHighlightingOnLoad=a;this.registerLanguage=d;this.listLanguages=k;this.getLanguage=i;this.inherit=o;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE]};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE]};this.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/};this.CLCM={cN:"comment",b:"//",e:"$",c:[this.PWM]};this.CBCM={cN:"comment",b:"/\\*",e:"\\*/",c:[this.PWM]};this.HCM={cN:"comment",b:"#",e:"$",c:[this.PWM]};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.CSSNM={cN:"number",b:this.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0};this.RM={cN:"regexp",b:/\//,e:/\/[gim]*/,i:/\n/,c:[this.BE,{b:/\[/,e:/\]/,r:0,c:[this.BE]}]};this.TM={cN:"title",b:this.IR,r:0};this.UTM={cN:"title",b:this.UIR,r:0}}();hljs.registerLanguage("scala",function(d){var b={cN:"annotation",b:"@[A-Za-z]+"};var c={cN:"string",b:'u?r?"""',e:'"""',r:10};var a={cN:"symbol",b:"'\\w[\\w\\d_]*(?!')"};var e={cN:"type",b:"\\b[A-Z][A-Za-z0-9_]*",r:0};var h={cN:"title",b:/[^0-9\n\t "'(),.`{}\[\]:;][^\n\t "'(),.`{}\[\]:;]+|[^0-9\n\t "'(),.`{}\[\]:;=]/,r:0};var i={cN:"class",bK:"class object trait type",e:/[:={\[(\n;]/,c:[{cN:"keyword",bK:"extends with",r:10},h]};var g={cN:"function",bK:"def val",e:/[:={\[(\n;]/,c:[h]};var f={cN:"javadoc",b:"/\\*\\*",e:"\\*/",c:[{cN:"javadoctag",b:"@[A-Za-z]+"}],r:10};return{k:{literal:"true false null",keyword:"type yield lazy override def with val var sealed abstract private trait object if forSome for while throw finally protected extends import final return else break new catch super class case package default try this match continue throws implicit"},c:[d.CLCM,d.CBCM,c,d.QSM,a,e,g,i,d.CNM,b]}}); \ No newline at end of file diff --git a/docs/_spec/public/scripts/main.js b/docs/_spec/public/scripts/main.js new file mode 100644 index 000000000000..9ade9c770f1e --- /dev/null +++ b/docs/_spec/public/scripts/main.js @@ -0,0 +1,71 @@ +function currentChapter() { + var path = document.location.pathname; + var idx = path.lastIndexOf("/") + 1; + var chap = path.substring(idx, idx + 2); + return parseInt(chap, 10); +} + +function heading(i, heading, $heading) { + var currentLevel = parseInt(heading.tagName.substring(1)); + var result = ""; + if (currentLevel === this.headerLevel) { + this.headerCounts[this.headerLevel] += 1; + return "" + this.headerCounts[this.headerLevel] + " " + $heading.text(); + } else if (currentLevel < this.headerLevel) { + while(currentLevel < this.headerLevel) { + this.headerCounts[this.headerLevel] = 1; + this.headerLevel -= 1; + } + this.headerCounts[this.headerLevel] += 1; + return "" + this.headerCounts[this.headerLevel]+ " " + $heading.text(); + } else { + while(currentLevel > this.headerLevel) { + this.headerLevel += 1; + this.headerCounts[this.headerLevel] = 1; + } + return "" + this.headerCounts[this.headerLevel]+ " " + $heading.text(); + } +} + +// ignore when using wkhtmltopdf, or it won't work... +if(window.jekyllEnv !== 'spec-pdf') { + $('#toc').toc( + { + 'selectors': 'h1,h2,h3', + 'smoothScrolling': false, + 'chapter': currentChapter(), + 'headerLevel': 1, + 'headerCounts': [-1, currentChapter() - 1, 1, 1], + 'headerText': heading + } + ); +} + +// no language auto-detect so that EBNF isn't detected as scala +hljs.configure({ + languages: [] +}); + +// KaTeX configuration +document.addEventListener("DOMContentLoaded", function() { + renderMathInElement(document.body, { + delimiters: [ + {left: "´", right: "´", display: false}, // "display: false" -> inline + {left: "$$", right: "$$", display: true} + ], + ignoredTags: ['script', 'noscript', 'style', 'textarea'], + }); + // syntax highlighting after KaTeX is loaded, + // so that math can be used in code blocks + hljs.initHighlighting(); + $("pre nobr").addClass("fixws"); + // point when all necessary js is done, so PDF to be rendered + window.status = "loaded"; +}); + +$("#chapters a").each(function (index) { + if (document.location.pathname.endsWith($(this).attr("href"))) + $(this).addClass("chapter-active"); + else + $(this).removeClass("chapter-active"); +}); diff --git a/docs/_spec/public/scripts/toc.js b/docs/_spec/public/scripts/toc.js new file mode 100644 index 000000000000..5b0bded12cfc --- /dev/null +++ b/docs/_spec/public/scripts/toc.js @@ -0,0 +1,128 @@ +/*! + * toc - jQuery Table of Contents Plugin + * v0.3.2 + * http://projects.jga.me/toc/ + * copyright Greg Allen 2014 + * MIT License +*/ +(function($) { +var verboseIdCache = {}; +$.fn.toc = function(options) { + var self = this; + var opts = $.extend({}, jQuery.fn.toc.defaults, options); + + var container = $(opts.container); + var headings = $(opts.selectors, container); + var headingOffsets = []; + var activeClassName = opts.activeClass; + + var scrollTo = function(e, callback) { + $('li', self).removeClass(activeClassName); + $(e.target).parent().addClass(activeClassName); + }; + + //highlight on scroll + var timeout; + var highlightOnScroll = function(e) { + if (timeout) { + clearTimeout(timeout); + } + timeout = setTimeout(function() { + var top = $(window).scrollTop(), + highlighted, closest = Number.MAX_VALUE, index = 0; + + for (var i = 0, c = headingOffsets.length; i < c; i++) { + var currentClosest = Math.abs(headingOffsets[i] - top); + if (currentClosest < closest) { + index = i; + closest = currentClosest; + } + } + + $('li', self).removeClass(activeClassName); + highlighted = $('li:eq('+ index +')', self).addClass(activeClassName); + opts.onHighlight(highlighted); + }, 50); + }; + if (opts.highlightOnScroll) { + $(window).on('scroll', highlightOnScroll); + highlightOnScroll(); + } + + return this.each(function() { + //build TOC + var el = $(this); + var ul = $(opts.listType); + + headings.each(function(i, heading) { + var $h = $(heading); + headingOffsets.push($h.offset().top - opts.highlightOffset); + + var anchorName = opts.anchorName(i, heading, opts.prefix); + + //add anchor + if(heading.id !== anchorName) { + var anchor = $('').attr('id', anchorName).insertBefore($h); + } + + //build TOC item + var a = $('
    ') + .text(opts.headerText(i, heading, $h)) + .attr('href', '#' + anchorName) + .on('click', function(e) { + $(window).off('scroll', highlightOnScroll); + scrollTo(e, function() { + $(window).on('scroll', highlightOnScroll); + }); + el.trigger('selected', $(this).attr('href')); + }); + + var li = $('