Skip to content

Commit

Permalink
Merge branch 'lampepfl:main' into show-context-for-inline-safe-init
Browse files Browse the repository at this point in the history
  • Loading branch information
Xavientois committed Mar 9, 2022
2 parents 18300d0 + c04f566 commit 25cf4ed
Show file tree
Hide file tree
Showing 34 changed files with 432 additions and 456 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Expand Up @@ -247,3 +247,6 @@
[submodule "community-build/community-projects/scalacheck-forward-compat"]
path = community-build/community-projects/scalacheck-forward-compat
url = https://github.com/dotty-staging/scalacheck
[submodule "community-build/community-projects/http4s"]
path = community-build/community-projects/http4s
url = https://github.com/dotty-staging/http4s.git
1 change: 1 addition & 0 deletions community-build/community-projects/http4s
Submodule http4s added at fc0a18
12 changes: 11 additions & 1 deletion community-build/src/scala/dotty/communitybuild/projects.scala
Expand Up @@ -791,6 +791,14 @@ object projects:
dependencies = () => List(cats, disciplineMunit)
)

lazy val http4s = SbtCommunityProject(
project = "http4s",
sbtTestCommand = "tests/test; server/test; client/test; ember-core/test; ember-server/test; ember-client/test; circe/test",
sbtPublishCommand = "publishLocal",
scalacOptions = SbtCommunityProject.scalacOptions.filter(_ != "-Ysafe-init"),
dependencies = () => List(cats, catsEffect3, fs2, disciplineMunit, scalacheckEffect)
)

end projects

lazy val forwardCompatMapping = Map[CommunityProject, CommunityProject](
Expand Down Expand Up @@ -892,7 +900,9 @@ def allProjects = List(
projects.jacksonModuleScala,
projects.specs2,
projects.coop,
projects.coopForwardCompat
projects.coopForwardCompat,
projects.spire,
projects.http4s
)

lazy val projectMap = allProjects.groupBy(_.project)
Expand Up @@ -52,6 +52,7 @@ class CommunityBuildTestB:
@Test def scodecBits = projects.scodecBits.run()
@Test def simulacrumScalafixAnnotations = projects.simulacrumScalafixAnnotations.run()
@Test def spire = projects.spire.run()
@Test def http4s = projects.http4s.run()
end CommunityBuildTestB

@Category(Array(classOf[TestCategory]))
Expand Down
9 changes: 1 addition & 8 deletions compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala
Expand Up @@ -4563,14 +4563,7 @@ class JSCodeGen()(using genCtx: Context) {
val module = annot.argumentConstantString(0).getOrElse {
unexpected("could not read the module argument as a string literal")
}
val path = annot.argumentConstantString(1).fold {
if (annot.arguments.sizeIs < 2)
parsePath(sym.defaultJSName)
else
Nil
} { pathName =>
parsePath(pathName)
}
val path = annot.argumentConstantString(1).fold[List[String]](Nil)(parsePath)
val importSpec = Import(module, path)
annot.argumentConstantString(2).fold[js.JSNativeLoadSpec] {
importSpec
Expand Down
33 changes: 24 additions & 9 deletions compiler/src/dotty/tools/dotc/interactive/Completion.scala
Expand Up @@ -78,6 +78,22 @@ object Completion {
Mode.None
}

/** When dealing with <errors> in varios palces we check to see if they are
* due to incomplete backticks. If so, we ensure we get the full prefix
* including the backtick.
*
* @param content The source content that we'll check the positions for the prefix
* @param start The start position we'll start to look for the prefix at
* @param end The end position we'll look for the prefix at
* @return Either the full prefix including the ` or an empty string
*/
private def checkBacktickPrefix(content: Array[Char], start: Int, end: Int): String =
content.lift(start) match
case Some(char) if char == '`' =>
content.slice(start, end).mkString
case _ =>
""

/**
* Inspect `path` to determine the completion prefix. Only symbols whose name start with the
* returned prefix should be considered.
Expand All @@ -92,15 +108,14 @@ object Completion {
completionPrefix(selector :: Nil, pos)
}.getOrElse("")

// We special case Select here because we want to determine if the name
// is an error due to an unclosed backtick.
case (select: untpd.Select) :: _ if (select.name == nme.ERROR) =>
val content = select.source.content()
content.lift(select.nameSpan.start) match
case Some(char) if char == '`' =>
content.slice(select.nameSpan.start, select.span.end).mkString
case _ =>
""
// Foo.`se<TAB> will result in Select(Ident(Foo), <error>)
case (select: untpd.Select) :: _ if select.name == nme.ERROR =>
checkBacktickPrefix(select.source.content(), select.nameSpan.start, select.span.end)

// import scala.util.chaining.`s<TAB> will result in a Ident(<error>)
case (ident: untpd.Ident) :: _ if ident.name == nme.ERROR =>
checkBacktickPrefix(ident.source.content(), ident.span.start, ident.span.end)

case (ref: untpd.RefTree) :: _ =>
if (ref.name == nme.ERROR) ""
else ref.name.toString.take(pos.span.point - ref.span.point)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
Expand Up @@ -594,7 +594,7 @@ object JavaParsers {
val vparams = formalParams()
if (!isVoid) rtpt = optArrayBrackets(rtpt)
optThrows()
val bodyOk = !inInterface || mods.isOneOf(Flags.DefaultMethod | Flags.JavaStatic)
val bodyOk = !inInterface || mods.isOneOf(Flags.DefaultMethod | Flags.JavaStatic | Flags.Private)
val body =
if (bodyOk && in.token == LBRACE)
methodBody()
Expand Down
33 changes: 7 additions & 26 deletions compiler/src/dotty/tools/dotc/transform/sjs/PrepJSInterop.scala
Expand Up @@ -14,8 +14,7 @@ import Contexts._
import Decorators._
import DenotTransformers._
import Flags._
import NameKinds.{DefaultGetterName, ModuleClassName}
import NameOps._
import NameKinds.DefaultGetterName
import StdNames._
import Symbols._
import SymUtils._
Expand Down Expand Up @@ -560,14 +559,9 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP
case Some(annot) if annot.symbol == jsdefn.JSGlobalAnnot =>
checkJSGlobalLiteral(annot)
val pathName = annot.argumentConstantString(0).getOrElse {
val symTermName = sym.name.exclude(NameKinds.ModuleClassName).toTermName
if (symTermName == nme.apply) {
if ((enclosingOwner is OwnerKind.ScalaMod) && !sym.owner.isPackageObject) {
report.error(
"Native JS definitions named 'apply' must have an explicit name in @JSGlobal",
annot.tree)
} else if (symTermName.isSetterName) {
report.error(
"Native JS definitions with a name ending in '_=' must have an explicit name in @JSGlobal",
"Native JS members inside non-native objects must have an explicit name in @JSGlobal",
annot.tree)
}
sym.defaultJSName
Expand All @@ -576,18 +570,6 @@ class PrepJSInterop extends MacroTransform with IdentityDenotTransformer { thisP

case Some(annot) if annot.symbol == jsdefn.JSImportAnnot =>
checkJSImportLiteral(annot)
if (annot.arguments.sizeIs < 2) {
val symTermName = sym.name.exclude(NameKinds.ModuleClassName).toTermName
if (symTermName == nme.apply) {
report.error(
"Native JS definitions named 'apply' must have an explicit name in @JSImport",
annot.tree)
} else if (symTermName.isSetterName) {
report.error(
"Native JS definitions with a name ending in '_=' must have an explicit name in @JSImport",
annot.tree)
}
}
annot.argumentConstantString(2).foreach { globalPathName =>
checkGlobalRefPath(globalPathName)
}
Expand Down Expand Up @@ -1125,19 +1107,18 @@ object PrepJSInterop {
*/
private def checkJSImportLiteral(annot: Annotation)(using Context): Unit = {
val args = annot.arguments
val argCount = args.size
assert(argCount >= 1 && argCount <= 3,
i"@JSImport annotation $annot does not have between 1 and 3 arguments")
assert(args.size == 2 || args.size == 3,
i"@JSImport annotation $annot does not have exactly 2 or 3 arguments")

val firstArgIsValid = annot.argumentConstantString(0).isDefined
if (!firstArgIsValid)
report.error("The first argument to @JSImport must be a literal string.", args.head)

val secondArgIsValid = argCount < 2 || annot.argumentConstantString(1).isDefined || args(1).symbol == jsdefn.JSImportNamespaceModule
val secondArgIsValid = annot.argumentConstantString(1).isDefined || args(1).symbol == jsdefn.JSImportNamespaceModule
if (!secondArgIsValid)
report.error("The second argument to @JSImport must be literal string or the JSImport.Namespace object.", args(1))

val thirdArgIsValid = argCount < 3 || annot.argumentConstantString(2).isDefined
val thirdArgIsValid = args.size < 3 || annot.argumentConstantString(2).isDefined
if (!thirdArgIsValid)
report.error("The third argument to @JSImport, when present, must be a literal string.", args(2))
}
Expand Down
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/repl/JLineTerminal.scala
Expand Up @@ -152,6 +152,14 @@ final class JLineTerminal extends java.io.Closeable {
// using dummy values, resulting parsed input is probably unused
defaultParsedLine

// In the situation where we have a partial command that we want to
// complete we need to ensure that the :<partial-word> isn't split into
// 2 tokens, but rather the entire thing is treated as the "word", in
// order to insure the : is replaced in the completion.
case ParseContext.COMPLETE if
ParseResult.commands.exists(command => command._1.startsWith(input)) =>
parsedLine(input, cursor)

case ParseContext.COMPLETE =>
// Parse to find completions (typically after a Tab).
def isCompletable(token: Token) = isIdentifier(token) || isKeyword(token)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/repl/ParseResult.scala
Expand Up @@ -127,7 +127,7 @@ object ParseResult {
stats
}

private val commands: List[(String, String => ParseResult)] = List(
private[repl] val commands: List[(String, String => ParseResult)] = List(
Quit.command -> (_ => Quit),
Quit.alias -> (_ => Quit),
Help.command -> (_ => Help),
Expand Down
34 changes: 20 additions & 14 deletions compiler/src/dotty/tools/repl/ReplDriver.scala
Expand Up @@ -205,7 +205,7 @@ class ReplDriver(settings: Array[String],
label

/** Extract possible completions at the index of `cursor` in `expr` */
protected final def completions(cursor: Int, expr: String, state0: State): List[Candidate] = {
protected final def completions(cursor: Int, expr: String, state0: State): List[Candidate] =
def makeCandidate(label: String) = {

new Candidate(
Expand All @@ -218,20 +218,26 @@ class ReplDriver(settings: Array[String],
/* complete = */ false // if true adds space when completing
)
}
implicit val state = newRun(state0)
compiler
.typeCheck(expr, errorsAllowed = true)
.map { tree =>
val file = SourceFile.virtual("<completions>", expr, maybeIncomplete = true)
val unit = CompilationUnit(file)(using state.context)
unit.tpdTree = tree
given Context = state.context.fresh.setCompilationUnit(unit)
val srcPos = SourcePosition(file, Span(cursor))
val (_, completions) = Completion.completions(srcPos)
completions.map(_.label).distinct.map(makeCandidate)

if expr.startsWith(":") then
ParseResult.commands.collect {
case command if command._1.startsWith(expr) => makeCandidate(command._1)
}
.getOrElse(Nil)
}
else
given state: State = newRun(state0)
compiler
.typeCheck(expr, errorsAllowed = true)
.map { tree =>
val file = SourceFile.virtual("<completions>", expr, maybeIncomplete = true)
val unit = CompilationUnit(file)(using state.context)
unit.tpdTree = tree
given Context = state.context.fresh.setCompilationUnit(unit)
val srcPos = SourcePosition(file, Span(cursor))
val (_, completions) = Completion.completions(srcPos)
completions.map(_.label).distinct.map(makeCandidate)
}
.getOrElse(Nil)
end completions

private def interpret(res: ParseResult)(implicit state: State): State = {
res match {
Expand Down
33 changes: 33 additions & 0 deletions compiler/test/dotty/tools/repl/TabcompleteTests.scala
Expand Up @@ -195,4 +195,37 @@ class TabcompleteTests extends ReplTest {
|Foo.`bac"""stripMargin))
}

@Test def backtickedImport = initially {
assertEquals(
List(
"`scalaUtilChainingOps`",
"`synchronized`"
),
tabComplete("import scala.util.chaining.`s"))
}

@Test def commands = initially {
assertEquals(
List(
":doc",
":exit",
":help",
":imports",
":load",
":quit",
":reset",
":settings",
":type"
),
tabComplete(":")
)
}

@Test def commandPreface = initially {
// This looks odd, but if we return :doc here it will result in ::doc in the REPL
assertEquals(
List(":doc"),
tabComplete(":d")
)
}
}
2 changes: 1 addition & 1 deletion docs/_docs/reference/changed-features/pattern-matching.md
Expand Up @@ -94,7 +94,7 @@ A usage of a variadic extractor is irrefutable if one of the following condition
## Boolean Match

- `U =:= Boolean`
- Pattern-matching on exactly `0` pattern
- Pattern-matching on exactly `0` patterns

For example:

Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/reference/contextual/derivation.md
Expand Up @@ -330,7 +330,7 @@ given eqProduct[A](using inst: K0.ProductInstances[Eq, A]): Eq[A] with
)

inline def derived[A](using gen: K0.Generic[A]): Eq[A] =
gen.derive(eqSum, eqProduct)
gen.derive(eqProduct, eqSum)
```

The framework described here enables all three of these approaches without mandating any of them.
Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/reference/contextual/using-clauses.md
Expand Up @@ -43,7 +43,7 @@ def maximum[T](xs: List[T])(using Ord[T]): T =
xs.reduceLeft(max)
```

`maximum` takes a context parameter of type `Ord` only to pass it on as an
`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.
Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/reference/metaprogramming/staging.md
Expand Up @@ -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, whereas `run` is just a normal method.
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.

Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/reference/new-types/match-types.md
Expand Up @@ -67,7 +67,7 @@ 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(9))
case x: Array[t] => leafElem(x(0))
case x: Iterable[t] => leafElem(x.head)
case x: AnyVal => x
```
Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/reference/new-types/union-types.md
Expand Up @@ -32,7 +32,7 @@ scala> val name = UserName("Eve")
val name: UserName = UserName(Eve)

scala> if true then name else password
val res2: Object & Product = UserName(Eve)
val res2: Object = UserName(Eve)

scala> val either: Password | UserName = if true then name else password
val either: Password | UserName = UserName(Eve)
Expand Down
5 changes: 3 additions & 2 deletions docs/_docs/reference/other-new-features/matchable.md
Expand Up @@ -119,10 +119,10 @@ For instance, consider the definitions

```scala
opaque type Meter = Double
def Meter(x: Double) = x
def Meter(x: Double): Meter = x

opaque type Second = Double
def Second(x: Double) = x
def Second(x: Double): Second = x
```

Here, universal `equals` will return true for
Expand All @@ -134,6 +134,7 @@ Here, universal `equals` will return true for
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)
```

Expand Down
Expand Up @@ -42,7 +42,7 @@ appear in the inferred type.

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 interopability is no longer needed.
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:
Expand Down
2 changes: 1 addition & 1 deletion docs/_docs/reference/syntax.md
Expand Up @@ -115,7 +115,7 @@ 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
: = <- => <: :> #
: = <- => <: >: #
@ =>> ?=>
```

Expand Down

0 comments on commit 25cf4ed

Please sign in to comment.