Skip to content

Commit

Permalink
Support type variable definitions in quoted patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolasstucki committed Feb 15, 2023
1 parent 20ffb87 commit 0fae78d
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 17 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
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]
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`] ] =>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ val experimentalDefinitionInLibrary = Set(
"scala.annotation.MacroAnnotation",

//// New APIs: Quotes
// Can be stabilized in 3.4.0
"scala.quoted.runtime.QuoteMatching",
"scala.quoted.runtime.QuoteMatching$",
// Can be stabilized in 3.3.0 (unsure) or later
"scala.quoted.Quotes.reflectModule.CompilationInfoModule.XmacroSettings",
"scala.quoted.Quotes.reflectModule.FlagsModule.JavaAnnotation",
Expand Down

0 comments on commit 0fae78d

Please sign in to comment.