Skip to content

Commit

Permalink
Support type variable definitions in quoted patterns
Browse files Browse the repository at this point in the history
Support explicit type variable definition in quoted patterns.
This allows users to set explicit bounds or use the binding twice.
Previously this was only possible on quoted expression patterns case '{ ... }.

```scala
case '[type x; `x`] =>
case '[type x; Map[`x`, `x`]] =>
case '[type x <: List[Any]; `x`] =>
case '[type f[X]; `f`] =>
case '[type f <: AnyKind; `f`] =>
```

Fixes #10864
Fixes #11738
  • Loading branch information
nicolasstucki committed Feb 21, 2023
1 parent 1821107 commit ae801c0
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 14 deletions.
31 changes: 22 additions & 9 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,19 @@ object Parsers {
t
end typ

/** TypeBlock ::= {TypeBlockStat semi} Type
* TypeBlockStat ::= ‘type’ {nl} TypeDcl
*/
def typeBlock(): Tree =
val tDefs = new ListBuffer[Tree]
while in.token == TYPE do
val mods = defAnnotsMods(modifierTokens)
tDefs += typeDefOrDcl(in.offset, in.skipToken(mods))
acceptStatSep()
val tpt = typ()
if tDefs.isEmpty then tpt else Block(tDefs.toList, tpt)


private def makeKindProjectorTypeDef(name: TypeName): TypeDef = {
val isVarianceAnnotated = name.startsWith("+") || name.startsWith("-")
// We remove the variance marker from the name without passing along the specified variance at all
Expand Down Expand Up @@ -2447,7 +2460,7 @@ object Parsers {
atSpan(in.skipToken()) {
withinStaged(StageKind.Quoted | (if (location.inPattern) StageKind.QuotedPattern else 0)) {
Quote {
if (in.token == LBRACKET) inBrackets(typ())
if (in.token == LBRACKET) inBrackets(typeBlock())
else stagedBlock()
}
}
Expand Down Expand Up @@ -3080,8 +3093,8 @@ object Parsers {
/* -------- PARAMETERS ------------------------------------------- */

/** DefParamClauses ::= DefParamClause { DefParamClause } -- and two DefTypeParamClause cannot be adjacent
* DefParamClause ::= DefTypeParamClause
* | DefTermParamClause
* DefParamClause ::= DefTypeParamClause
* | DefTermParamClause
* | UsingParamClause
*/
def typeOrTermParamClauses(
Expand Down Expand Up @@ -3179,7 +3192,7 @@ object Parsers {
* UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’
* ClsParams ::= ClsParam {‘,’ ClsParam}
* ClsParam ::= {Annotation}
*
*
* TypelessClause ::= DefTermParamClause
* | UsingParamClause
*
Expand Down Expand Up @@ -3557,13 +3570,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]
*/
Expand Down Expand Up @@ -3602,8 +3615,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
Expand All @@ -3613,7 +3626,7 @@ object Parsers {
joinParams(tparams, vparamss)

var tpt = fromWithinReturnType { typedOpt() }

if (migrateTo3) newLineOptWhenFollowedBy(LBRACE)
val rhs =
if in.token == EQUALS then
Expand Down
11 changes: 7 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,11 @@ trait QuotesAndSplices {
* )
* ```
*/
private def splitQuotePattern(quoted: Tree)(using Context): (Map[Symbol, Bind], Tree, List[Tree]) = {
private def splitQuotePattern(quoted: Tree)(using Context): (Map[Symbol, Tree], Tree, List[Tree]) = {
val ctx0 = ctx

val typeBindings: collection.mutable.Map[Symbol, Bind] = collection.mutable.Map.empty
def getBinding(sym: Symbol): Bind =
val typeBindings: collection.mutable.Map[Symbol, Tree] = collection.mutable.Map.empty
def getBinding(sym: Symbol): Tree =
typeBindings.getOrElseUpdate(sym, {
val bindingBounds = sym.info
val bsym = newPatternBoundSymbol(sym.name.toString.stripPrefix("$").toTypeName, bindingBounds, quoted.span)
Expand Down Expand Up @@ -395,7 +395,10 @@ trait QuotesAndSplices {
val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt)))
val quoteCtx = quoteContext.addMode(Mode.QuotedPattern).retractMode(Mode.Pattern)
val quoted1 =
if quoted.isType then typedType(quoted0, WildcardType)(using quoteCtx)
if quoted.isType then typedType(quoted0, WildcardType)(using quoteCtx) match
case quoted1 @ Block(stats, Typed(tpt, _)) => cpy.Block(quoted1)(stats, tpt)
case quoted1 => quoted1

else typedExpr(quoted0, WildcardType)(using quoteCtx)

val (typeBindings, shape, splices) = splitQuotePattern(quoted1)
Expand Down
4 changes: 3 additions & 1 deletion docs/_docs/reference/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ ColonArgument ::= colon [LambdaStart]
LambdaStart ::= FunParams (‘=>’ | ‘?=>’)
| HkTypeParamClause ‘=>’
Quoted ::= ‘'’ ‘{’ Block ‘}’
| ‘'’ ‘[’ Type ‘]’
| ‘'’ ‘[’ TypeBlock ‘]’
ExprSplice ::= spliceId -- if inside quoted block
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
| ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern
Expand All @@ -293,6 +293,8 @@ BlockStat ::= Import
| Extension
| Expr1
| EndMarker
TypeBlock ::= {TypeBlockStat semi} Type
TypeBlockStat ::= ‘type’ {nl} TypeDcl
ForExpr ::= ‘for’ ‘(’ Enumerators0 ‘)’ {nl} [‘do‘ | ‘yield’] Expr
| ‘for’ ‘{’ Enumerators0 ‘}’ {nl} [‘do‘ | ‘yield’] Expr
Expand Down
15 changes: 15 additions & 0 deletions tests/pos-macros/i10864/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import scala.quoted._

case class T(t: Type[_])

object T {
def impl[T <: AnyKind](using tt: Type[T])(using Quotes): Expr[Unit] = {
val t = T(tt)
t.t match
case '[type x <: AnyKind; `x`] => // ok
case _ => quotes.reflect.report.error("not ok :(")
'{}
}

inline def run[T <: AnyKind] = ${ impl[T] }
}
4 changes: 4 additions & 0 deletions tests/pos-macros/i10864/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def test =
T.run[List]
T.run[Map]
T.run[Tuple22]
19 changes: 19 additions & 0 deletions tests/pos-macros/i10864a/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import scala.quoted._

case class T(t: Type[_])

object T {
def impl[T <: AnyKind](using tt: Type[T])(using Quotes): Expr[Unit] = {
val t = T(tt)
t.t match
case '[type x; `x`] =>
assert(Type.show[x] == "scala.Int", Type.show[x])
case '[type f[X]; `f`] =>
assert(Type.show[f] == "[A >: scala.Nothing <: scala.Any] => scala.collection.immutable.List[A]", Type.show[f])
case '[type f <: AnyKind; `f`] =>
assert(Type.show[f] == "[K >: scala.Nothing <: scala.Any, V >: scala.Nothing <: scala.Any] => scala.collection.immutable.Map[K, V]", Type.show[f])
'{}
}

inline def run[T <: AnyKind] = ${ impl[T] }
}
5 changes: 5 additions & 0 deletions tests/pos-macros/i10864a/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@main
def run =
T.run[Int]
T.run[List]
T.run[Map]
8 changes: 8 additions & 0 deletions tests/pos-macros/i11738.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import scala.quoted.*

def blah[A](using Quotes, Type[A]): Expr[Unit] =
Type.of[A] match
case '[h *: t] => println(s"h = ${Type.show[h]}, t = ${Type.show[t]}") // ok
case '[type f[X]; `f`[a]] => println(s"f = ${Type.show[f]}, a = ${Type.show[a]}") // error
case _ =>
'{()}
1 change: 1 addition & 0 deletions tests/pos-macros/i7264.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ class Foo {
def f[T2](t: Type[T2])(using Quotes) = t match {
case '[ *:[Int, t2] ] =>
Type.of[ *:[Int, t2] ]
case '[ type t <: Tuple; *:[`t`, `t`] ] =>
}
}

0 comments on commit ae801c0

Please sign in to comment.