Skip to content

Commit 455908a

Browse files
committed
Merge remote-tracking branch 'upstream/master' into batch-files
2 parents 4b5a2bb + f787d11 commit 455908a

File tree

13 files changed

+115
-28
lines changed

13 files changed

+115
-28
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ class CompilationTests extends ParallelTesting {
220220
Array("-Ycheck-reentrant", "-Yemit-tasty-in-class")
221221
)
222222

223-
val libraryDirs = List(Paths.get("library/src"), Paths.get("library/src-scala3"))
223+
val libraryDirs = List(Paths.get("library/src"), Paths.get("library/src-bootstrapped"))
224224
val librarySources = libraryDirs.flatMap(d => sources(Files.walk(d)))
225225

226226
val lib =

doc-tool/src/dotty/tools/dottydoc/staticsite/Site.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,9 @@ case class Site(
127127
}
128128

129129
/** Copy static files to `outDir` */
130-
def copyStaticFiles(outDir: JFile = new JFile(root.getAbsolutePath + "/_site"))(implicit ctx: Context): this.type =
130+
private[this] val defaultOutDir = new JFile(root.getAbsolutePath + JFile.separator + "_site")
131+
132+
def copyStaticFiles(outDir: JFile = defaultOutDir)(implicit ctx: Context): this.type =
131133
createOutput (outDir) {
132134
// Copy user-defined static assets
133135
staticAssets.foreach { asset =>
@@ -188,7 +190,7 @@ case class Site(
188190
}
189191

190192
/** Generate HTML for the API documentation */
191-
def generateApiDocs(outDir: JFile = new JFile(root.getAbsolutePath + "/_site"))(implicit ctx: Context): this.type =
193+
def generateApiDocs(outDir: JFile = defaultOutDir)(implicit ctx: Context): this.type =
192194
createOutput(outDir) {
193195
def genDoc(e: model.Entity): Unit = {
194196
ctx.docbase.echo(s"Generating doc page for: ${e.path.mkString(".")}")
@@ -198,7 +200,11 @@ case class Site(
198200
if (e.kind == "package") ("/index.html", -1)
199201
else (".html", 0)
200202

201-
val target = mkdirs(fs.getPath(outDir.getAbsolutePath + "/api/" + e.path.mkString("/") + suffix))
203+
val path = if (scala.util.Properties.isWin)
204+
e.path.map(_.replace("<", "_").replace(">", "_"))
205+
else
206+
e.path
207+
val target = mkdirs(fs.getPath(outDir.getAbsolutePath + "/api/" + path.mkString("/") + suffix))
202208
val params = defaultParams(target.toFile, -1).withPosts(blogInfo).withEntity(Some(e)).toMap
203209
val page = new HtmlPage("_layouts/api-page.html", layouts("api-page").content, params, includes)
204210

@@ -230,7 +236,7 @@ case class Site(
230236
}
231237

232238
/** Generate HTML files from markdown and .html sources */
233-
def generateHtmlFiles(outDir: JFile = new JFile(root.getAbsolutePath + "/_site"))(implicit ctx: Context): this.type =
239+
def generateHtmlFiles(outDir: JFile = defaultOutDir)(implicit ctx: Context): this.type =
234240
createOutput(outDir) {
235241
compilableFiles.foreach { asset =>
236242
val pathFromRoot = stripRoot(asset)
@@ -250,7 +256,7 @@ case class Site(
250256
}
251257

252258
/** Generate blog from files in `blog/_posts` and output in `outDir` */
253-
def generateBlog(outDir: JFile = new JFile(root.getAbsolutePath + "/_site"))(implicit ctx: Context): this.type =
259+
def generateBlog(outDir: JFile = defaultOutDir)(implicit ctx: Context): this.type =
254260
createOutput(outDir) {
255261
blogposts.foreach { file =>
256262
val BlogPost.extract(year, month, day, name, ext) = file.getName

docs/docs/reference/principled-meta-programming.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,13 @@ prints it again in an error message if it evaluates to `false`.
2929
inline def assert(expr: => Boolean): Unit =
3030
~ assertImpl('(expr))
3131

32-
def assertImpl(expr: Expr[Boolean]) =
33-
'{ if !(~expr) then throw new AssertionError(s"failed assertion: ${~showExpr(expr)}") }
32+
def assertImpl(expr: Expr[Boolean]) = '{
33+
if !(~expr) then
34+
throw new AssertionError(s"failed assertion: ${~showExpr(expr)}")
35+
}
36+
37+
def showExpr(expr: Expr[Boolean]): Expr[String] =
38+
'("<some source code>") // Better implementation later in this document
3439

3540
If `e` is an expression, then `'(e)` or `'{e}` represent the typed
3641
abstract syntax tree representing `e`. If `T` is a type, then `'[T]`
@@ -587,9 +592,12 @@ analogue of lifting.
587592

588593
Using lifting, we can now give the missing definition of `showExpr` in the introductory example:
589594

590-
def showExpr[T](expr: Expr[T]): Expr[String] = expr.toString
595+
def showExpr[T](expr: Expr[T]): Expr[String] = {
596+
val code = expr.show
597+
code.toExpr
598+
}
591599

592-
That is, the `showExpr` method converts its `Expr` argument to a string, and lifts
600+
That is, the `showExpr` method converts its `Expr` argument to a string (`code`), and lifts
593601
the result back to an `Expr[String]` using the implicit `toExpr` conversion.
594602

595603
## Implementation
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package scala
2+
3+
package object quoted {
4+
5+
implicit class LiftExprOps[T](val x: T) extends AnyVal {
6+
def toExpr(implicit ev: Liftable[T]): Expr[T] = ev.toExpr(x)
7+
}
8+
9+
implicit class ListOfExprOps[T](val list: List[Expr[T]]) extends AnyVal {
10+
def toExprOfList(implicit ev: Type[T]): Expr[List[T]] = {
11+
def rec(list: List[Expr[T]]): Expr[List[T]] = list match {
12+
case x :: xs => '{ (~x) :: (~rec(xs)) }
13+
case Nil => '(Nil)
14+
}
15+
rec(list)
16+
}
17+
}
18+
}

library/src/scala/quoted/package.scala

Lines changed: 0 additions & 9 deletions
This file was deleted.

project/Build.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -775,14 +775,14 @@ object Build {
775775
lazy val dottyLibrarySettings = Seq(
776776
libraryDependencies += "org.scala-lang" % "scala-library" % scalacVersion,
777777
// Add version-specific source directories:
778-
// - files in src-scala3 will only be compiled by dotty
779-
// - files in src-scala2 will only be compiled by scalac
778+
// - files in src-non-bootstrapped will only be compiled by the reference compiler (scalac)
779+
// - files in src-bootstrapped will only be compiled by the current dotty compiler (non-bootstrapped and bootstrapped)
780780
unmanagedSourceDirectories in Compile += {
781781
val baseDir = baseDirectory.value
782782
if (isDotty.value)
783-
baseDir / "src-scala3"
783+
baseDir / "src-bootstrapped"
784784
else
785-
baseDir / "src-scala2"
785+
baseDir / "src-non-bootstrapped"
786786
}
787787
)
788788

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import scala.quoted._
2+
import scala.tasty.Reflection
3+
4+
import scala.language.implicitConversions
5+
6+
object FQuote {
7+
8+
implicit class SCOps(ctx: StringContext) {
9+
inline def ff(args: => Any*): String = ~impl('(this), '(args))
10+
}
11+
12+
/*private*/ def impl(receiver: Expr[SCOps], args: Expr[Seq[Any]])(implicit reflect: Reflection): Expr[String] = {
13+
import reflect._
14+
15+
def liftListOfAny(lst: List[Term]): Expr[List[Any]] = lst match {
16+
case x :: xs =>
17+
val head = x.seal[Any]
18+
val tail = liftListOfAny(xs)
19+
'{ ~head :: ~tail }
20+
case Nil => '(Nil)
21+
}
22+
23+
def isStringConstant(tree: Term) = tree match {
24+
case Term.Literal(_) => true
25+
case _ => false
26+
}
27+
28+
def isSCOpsConversion(tree: Term) =
29+
tree.symbol.fullName == "FQuote$.SCOps"
30+
31+
def isStringContextApply(tree: Term) =
32+
tree.symbol.fullName == "scala.StringContext$.apply"
33+
34+
// FQuote.SCOps(StringContext.apply([p0, ...]: String*)
35+
val parts = receiver.unseal.underlyingArgument match {
36+
case Term.Apply(conv, List(Term.Apply(fun, List(Term.Typed(Term.Repeated(values), _)))))
37+
if isSCOpsConversion(conv) &&
38+
isStringContextApply(fun) &&
39+
values.forall(isStringConstant) =>
40+
values.collect { case Term.Literal(Constant.String(value)) => value }
41+
case tree =>
42+
throw new QuoteError(s"String literal expected, but ${tree.show} found")
43+
}
44+
45+
// [a0, ...]: Any*
46+
val Term.Typed(Term.Repeated(allArgs), _) = args.unseal.underlyingArgument
47+
48+
for ((arg, part) <- allArgs.zip(parts.tail)) {
49+
if (part.startsWith("%d") && !(arg.tpe <:< definitions.IntType)) {
50+
return '(s"`${~arg.showCode.toExpr}` is not of type Int")
51+
}
52+
53+
}
54+
55+
val string = parts.mkString("")
56+
'{ new collection.immutable.StringOps(~string.toExpr).format(~args: _*) }
57+
}
58+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import FQuote._
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
val one: Int = 1
6+
assert(ff"Hello $one%d!" == "Hello 1!")
7+
val world: String = "world"
8+
assert(ff"Hello $world%d!" == "`world` is not of type Int")
9+
}
10+
}

tests/run-separate-compilation/xml-interpolation-2/XmlQuote_1.scala

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,7 @@ object XmlQuote {
5656
// [a0, ...]: Any*
5757
val args2: Expr[List[Any]] = args.unseal.underlyingArgument match {
5858
case Typed(Repeated(args0), _) => // statically known args, make list directly
59-
def liftListOfAny(lst: List[Expr[Any]]): Expr[List[Any]] = lst match {
60-
case x :: xs => '{ ~x :: ~liftListOfAny(xs) }
61-
case Nil => '(Nil)
62-
}
63-
liftListOfAny(args0.map(_.seal[Any]))
59+
args0.map(_.seal[Any]).toExprOfList
6460
case _ =>
6561
'((~args).toList)
6662

0 commit comments

Comments
 (0)