From bfae7cf0683bb1f613a18c12871664913a0fab39 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 13 Feb 2023 14:46:01 +0100 Subject: [PATCH] Support type variable definitions in quoted patterns 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`] => ``` In combination with #16907 it would also allow ```scala case '[type f[X]; `f`] => case '[type f <: AnyKind; `f`] => ``` and therefore solve #10864 and #11738 --- .../dotty/tools/dotc/parsing/Parsers.scala | 31 +++++++++++++------ .../src/dotty/tools/dotc/typer/Typer.scala | 1 + docs/_docs/reference/syntax.md | 4 ++- tests/pos-macros/i7264.scala | 2 ++ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 479ae1fa9095..9c63452b1302 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -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 @@ -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() } } @@ -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( @@ -3179,7 +3192,7 @@ object Parsers { * UsingClsTermParamClause::= ‘(’ ‘using’ [‘erased’] (ClsParams | ContextTypes) ‘)’ * ClsParams ::= ClsParam {‘,’ ClsParam} * ClsParam ::= {Annotation} - * + * * TypelessClause ::= DefTermParamClause * | UsingParamClause * @@ -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] */ @@ -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 @@ -3613,7 +3626,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/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 5752409c51c1..34670e053f9f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1111,6 +1111,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer if ctx.mode.is(Mode.SafeNulls) && !exprCtx.mode.is(Mode.SafeNulls) && pt.isValueType + && !tree.isType && !inContext(exprCtx.addMode(Mode.SafeNulls))(expr1.tpe <:< pt) then expr1 = expr1.cast(pt) diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index 8da41c7e6d0c..006cdb3e4b52 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -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 @@ -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 diff --git a/tests/pos-macros/i7264.scala b/tests/pos-macros/i7264.scala index c87409561bee..ff9fcece483f 100644 --- a/tests/pos-macros/i7264.scala +++ b/tests/pos-macros/i7264.scala @@ -3,5 +3,7 @@ class Foo { def f[T2](t: Type[T2])(using Quotes) = t match { case '[ *:[Int, t2] ] => Type.of[ *:[Int, t2] ] + case '[ type t <: Tuple; *:[Int, `t`] ] => + case '[ type t <: Tuple; *:[`t`, `t`] ] => } }