Skip to content

Commit 95d9171

Browse files
Merge pull request #15012 from dotty-staging/tighten-quoted-and-spliced-types
Disallow old uses of quoted and spliced types
2 parents 9d68778 + 9306274 commit 95d9171

File tree

13 files changed

+91
-89
lines changed

13 files changed

+91
-89
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,8 +1911,6 @@ object desugar {
19111911
new UntypedTreeTraverser {
19121912
def traverse(tree: untpd.Tree)(using Context): Unit = tree match {
19131913
case Splice(expr) => collect(expr)
1914-
case TypSplice(expr) =>
1915-
report.error(TypeSpliceInValPattern(expr), tree.srcPos)
19161914
case _ => traverseChildren(tree)
19171915
}
19181916
}.traverse(expr)

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
110110
case class Splice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree {
111111
def isInBraces: Boolean = span.end != expr.span.end
112112
}
113-
case class TypSplice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree
114113
case class ForYield(enums: List[Tree], expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
115114
case class ForDo(enums: List[Tree], body: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
116115
case class GenFrom(pat: Tree, expr: Tree, checkMode: GenCheckMode)(implicit @constructorOnly src: SourceFile) extends Tree
@@ -613,10 +612,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
613612
case tree: Splice if expr eq tree.expr => tree
614613
case _ => finalize(tree, untpd.Splice(expr)(tree.source))
615614
}
616-
def TypSplice(tree: Tree)(expr: Tree)(using Context): Tree = tree match {
617-
case tree: TypSplice if expr eq tree.expr => tree
618-
case _ => finalize(tree, untpd.TypSplice(expr)(tree.source))
619-
}
620615
def ForYield(tree: Tree)(enums: List[Tree], expr: Tree)(using Context): TermTree = tree match {
621616
case tree: ForYield if (enums eq tree.enums) && (expr eq tree.expr) => tree
622617
case _ => finalize(tree, untpd.ForYield(enums, expr)(tree.source))
@@ -695,8 +690,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
695690
cpy.Quote(tree)(transform(t))
696691
case Splice(expr) =>
697692
cpy.Splice(tree)(transform(expr))
698-
case TypSplice(expr) =>
699-
cpy.TypSplice(tree)(transform(expr))
700693
case ForYield(enums, expr) =>
701694
cpy.ForYield(tree)(transform(enums), transform(expr))
702695
case ForDo(enums, body) =>
@@ -754,8 +747,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
754747
this(x, t)
755748
case Splice(expr) =>
756749
this(x, expr)
757-
case TypSplice(expr) =>
758-
this(x, expr)
759750
case ForYield(enums, expr) =>
760751
this(this(x, enums), expr)
761752
case ForDo(enums, body) =>

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -452,8 +452,6 @@ object Parsers {
452452
makeParameter(nme.ERROR, tree, mods)
453453
case Typed(id @ Ident(name), tpt) =>
454454
makeParameter(name.asTermName, tpt, mods, isBackquoted = isBackquoted(id)).withSpan(tree.span)
455-
case Typed(Splice(Ident(name)), tpt) =>
456-
makeParameter(("$" + name).toTermName, tpt, mods).withSpan(tree.span)
457455
case _ =>
458456
syntaxError(s"not a legal $expected", tree.span)
459457
makeParameter(nme.ERROR, tree, mods)
@@ -1572,13 +1570,15 @@ object Parsers {
15721570
/** The block in a quote or splice */
15731571
def stagedBlock() = inBraces(block(simplify = true))
15741572

1575-
/** SimpleExpr ::= spliceId | ‘$’ ‘{’ Block ‘}’) unless inside quoted pattern
1576-
* SimpleType ::= spliceId | ‘$’ ‘{’ Block ‘}’) unless inside quoted pattern
1577-
*
1578-
* SimpleExpr ::= spliceId | ‘$’ ‘{’ Pattern ‘}’) when inside quoted pattern
1579-
* SimpleType ::= spliceId | ‘$’ ‘{’ Pattern ‘}’) when inside quoted pattern
1573+
/** ExprSplice ::= ‘$’ spliceId -- if inside quoted block
1574+
* | ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
1575+
* | ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern
1576+
* TypeSplice ::= ‘$’ spliceId -- if inside quoted type
1577+
* | ‘$’ ‘{’ Block ‘}’ -- unless inside quoted type pattern
1578+
* | ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted type pattern
15801579
*/
15811580
def splice(isType: Boolean): Tree =
1581+
val start = in.offset
15821582
atSpan(in.offset) {
15831583
val expr =
15841584
if (in.name.length == 1) {
@@ -1591,13 +1591,22 @@ object Parsers {
15911591
in.nextToken()
15921592
id
15931593
}
1594-
if (isType) TypSplice(expr) else Splice(expr)
1594+
if isType then
1595+
val msg = "Type splicing with `$` in quotes not supported anymore"
1596+
val inPattern = (staged & StageKind.QuotedPattern) != 0
1597+
val hint =
1598+
if inPattern then "Use lower cased variable name without the `$` instead"
1599+
else "To use a given Type[T] in a quote just write T directly"
1600+
syntaxError(s"$msg\n\nHint: $hint", Span(start, in.lastOffset))
1601+
Ident(nme.ERROR.toTypeName)
1602+
else
1603+
Splice(expr)
15951604
}
15961605

15971606
/** SimpleType ::= SimpleLiteral
15981607
* | ‘?’ SubtypeBounds
15991608
* | SimpleType1
1600-
* | SimpeType ‘(’ Singletons ‘)’ -- under language.experimental.dependent, checked in Typer
1609+
* | SimpleType ‘(’ Singletons ‘)’ -- under language.experimental.dependent, checked in Typer
16011610
* Singletons ::= Singleton {‘,’ Singleton}
16021611
*/
16031612
def simpleType(): Tree =
@@ -1635,7 +1644,7 @@ object Parsers {
16351644
* | Singleton `.' type
16361645
* | ‘(’ ArgTypes ‘)’
16371646
* | Refinement
1638-
* | ‘$’ ‘{’ Block ‘}’
1647+
* | TypeSplice -- deprecated syntax (since 3.0.0)
16391648
* | SimpleType1 TypeArgs
16401649
* | SimpleType1 `#' id
16411650
*/
@@ -2237,7 +2246,7 @@ object Parsers {
22372246
/** SimpleExpr ::= ‘new’ ConstrApp {`with` ConstrApp} [TemplateBody]
22382247
* | ‘new’ TemplateBody
22392248
* | BlockExpr
2240-
* | ‘$’ ‘{’ Block ‘}’
2249+
* | ExprSplice
22412250
* | Quoted
22422251
* | quoteId
22432252
* | SimpleExpr1 [`_`]

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -691,8 +691,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
691691
keywordStr("'{") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
692692
case Splice(tree) =>
693693
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
694-
case TypSplice(tree) =>
695-
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
696694
case Thicket(trees) =>
697695
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
698696
case MacroTree(call) =>

compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
1919
case TypeMismatchID // errorNumber: 7
2020
case NotAMemberID // errorNumber: 8
2121
case EarlyDefinitionsNotSupportedID // errorNumber: 9
22-
case TopLevelImplicitClassID extends ErrorMessageID(isActive = false) // errorNumber: 10
23-
case ImplicitCaseClassID // errorNumber: 11
22+
case TopLevelImplicitClassID extends ErrorMessageID(isActive = false) // errorNumber: 10
23+
case ImplicitCaseClassID // errorNumber: 11
2424
case ImplicitClassPrimaryConstructorArityID // errorNumber: 12
2525
case ObjectMayNotHaveSelfTypeID // errorNumber: 13
2626
case TupleTooLongID extends ErrorMessageID(isActive = false) // errorNumber: 14
@@ -97,7 +97,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
9797
case FunctionTypeNeedsNonEmptyParameterListID // errorNumber: 85
9898
case WrongNumberOfParametersID // errorNumber: 86
9999
case DuplicatePrivateProtectedQualifierID // errorNumber: 87
100-
case ExpectedStartOfTopLevelDefinitionID // errorNumber: 88
100+
case ExpectedStartOfTopLevelDefinitionID // errorNumber: 88
101101
case MissingReturnTypeWithReturnStatementID // errorNumber: 89
102102
case NoReturnFromInlineableID // errorNumber: 90
103103
case ReturnOutsideMethodDefinitionID // errorNumber: 91
@@ -164,7 +164,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
164164
case ExtensionCanOnlyHaveDefsID // errorNumber: 152
165165
case UnexpectedPatternForSummonFromID // errorNumber: 153
166166
case AnonymousInstanceCannotBeEmptyID // errorNumber: 154
167-
case TypeSpliceInValPatternID // errorNumber: 155
167+
case TypeSpliceInValPatternID extends ErrorMessageID(isActive = false) // errorNumber: 155
168168
case ModifierNotAllowedForDefinitionID // errorNumber: 156
169169
case CannotExtendJavaEnumID // errorNumber: 157
170170
case InvalidReferenceInImplicitNotFoundAnnotationID // errorNumber: 158

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2447,15 +2447,6 @@ import transform.SymUtils._
24472447
|""".stripMargin
24482448
}
24492449

2450-
class TypeSpliceInValPattern(expr: untpd.Tree)(using Context)
2451-
extends SyntaxMsg(TypeSpliceInValPatternID) {
2452-
def msg = "Type splices cannot be used in val patterns. Consider using `match` instead."
2453-
def explain =
2454-
em"""|Type splice: `$$${expr.show}` cannot be used in a `val` pattern. Consider rewriting the `val` pattern
2455-
|as a `match` with a corresponding `case` to replace the `val`.
2456-
|""".stripMargin
2457-
}
2458-
24592450
class ModifierNotAllowedForDefinition(flag: Flag)(using Context)
24602451
extends SyntaxMsg(ModifierNotAllowedForDefinitionID) {
24612452
def msg = em"Modifier ${hl(flag.flagsString)} is not allowed for this definition"

compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ trait QuotesAndSplices {
3838
tree.quoted match {
3939
case untpd.Splice(innerExpr) if tree.isTerm && !ctx.mode.is(Mode.Pattern) =>
4040
report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos)
41-
case untpd.TypSplice(innerType) if tree.isType =>
42-
report.warning("Canceled splice directly inside a quote. '[ ${ XYZ } ] is equivalent to XYZ.", tree.srcPos)
4341
case _ =>
4442
}
4543
val qctx = inferImplicitArg(defn.QuotesClass.typeRef, tree.span)
@@ -52,11 +50,12 @@ trait QuotesAndSplices {
5250
if ctx.mode.is(Mode.Pattern) then
5351
typedQuotePattern(tree, pt, qctx).withSpan(tree.span)
5452
else if tree.quoted.isType then
55-
val msg = em"Consider using canonical type constructor scala.quoted.Type.of[${tree.quoted}] instead"
56-
if sourceVersion.isAtLeast(`future-migration`) then report.error(msg, tree.srcPos)
57-
else report.warning(msg, tree.srcPos)
58-
val typeOfTree = untpd.TypeApply(untpd.ref(defn.QuotedTypeModule_of.termRef), tree.quoted :: Nil).withSpan(tree.span)
59-
makeInlineable(typedTypeApply(typeOfTree, pt)(using quoteContext).select(nme.apply).appliedTo(qctx).withSpan(tree.span))
53+
val msg = em"""Quoted types `'[..]` can only be used in patterns.
54+
|
55+
|Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead.
56+
|""".stripMargin
57+
report.error(msg, tree.srcPos)
58+
EmptyTree
6059
else
6160
val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.quoted)
6261
makeInlineable(typedApply(exprQuoteTree, pt)(using pushQuotes(qctx)).select(nme.apply).appliedTo(qctx).withSpan(tree.span))
@@ -135,7 +134,7 @@ trait QuotesAndSplices {
135134
case arg: untpd.Ident =>
136135
typedExpr(arg)
137136
case arg =>
138-
report.error("Open patttern exprected an identifier", arg.srcPos)
137+
report.error("Open pattern expected an identifier", arg.srcPos)
139138
EmptyTree
140139
}
141140
if args.isEmpty then
@@ -145,30 +144,6 @@ trait QuotesAndSplices {
145144
ref(defn.QuotedRuntimePatterns_patternHigherOrderHole).appliedToType(pt).appliedTo(typedPat, SeqLiteral(typedArgs, TypeTree(defn.AnyType)))
146145
}
147146

148-
/** Translate ${ t: Type[T] }` into type `t.splice` while tracking the quotation level in the context */
149-
def typedTypSplice(tree: untpd.TypSplice, pt: Type)(using Context): Tree = {
150-
record("typedTypSplice")
151-
checkSpliceOutsideQuote(tree)
152-
tree.expr match {
153-
case untpd.Quote(innerType) if innerType.isType =>
154-
report.warning("Canceled quote directly inside a splice. ${ '[ XYZ ] } is equivalent to XYZ.", tree.srcPos)
155-
case _ =>
156-
}
157-
158-
if ctx.mode.is(Mode.QuotedPattern) && level == 1 then
159-
report.error(
160-
"""`$` for quote pattern variable is not supported anymore.
161-
|Use lower cased variable name without the `$` instead.""".stripMargin,
162-
tree.srcPos)
163-
ref(defn.NothingType)
164-
else
165-
val tree1 = typedSelect(untpd.Select(tree.expr, tpnme.Underlying), pt)(using spliceContext).withSpan(tree.span)
166-
val msg = em"Consider using canonical type reference ${tree1.tpe} instead"
167-
if sourceVersion.isAtLeast(`future-migration`) then report.error(msg, tree.srcPos)
168-
else report.warning(msg, tree.srcPos)
169-
tree1
170-
}
171-
172147
/** Type a pattern variable name `t` in quote pattern as `${given t$giveni: Type[t @ _]}`.
173148
* The resulting pattern is the split in `splitQuotePattern`.
174149
*/

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2898,7 +2898,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
28982898
case untpd.EmptyTree => tpd.EmptyTree
28992899
case tree: untpd.Quote => typedQuote(tree, pt)
29002900
case tree: untpd.Splice => typedSplice(tree, pt)
2901-
case tree: untpd.TypSplice => typedTypSplice(tree, pt)
29022901
case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here
29032902
case tree: untpd.Hole => typedHole(tree, pt)
29042903
case _ => typedUnadapted(desugar(tree), pt, locked)

docs/_docs/internals/syntax.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and U
2727
lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll”
2828
letter ::= upper | lower “… and Unicode categories Lo, Lt, Lm, Nl”
2929
digit ::= ‘0’ | … | ‘9’
30-
paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ | ‘'(’ | ‘'[’ | ‘'{’
30+
paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’
3131
delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’
3232
opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ |
3333
‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’
@@ -45,6 +45,7 @@ id ::= plainid
4545
| ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’
4646
idrest ::= {letter | digit} [‘_’ op]
4747
quoteId ::= ‘'’ alphaid
48+
spliceId ::= ‘$’ alphaid ;
4849
4950
integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’]
5051
decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit]
@@ -183,8 +184,7 @@ SimpleType1 ::= id
183184
| Singleton ‘.’ ‘type’ SingletonTypeTree(p)
184185
| ‘(’ Types ‘)’ Tuple(ts)
185186
| Refinement RefinedTypeTree(EmptyTree, refinement)
186-
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
187-
| ‘$’ ‘{’ Pattern ‘}’ -- only inside quoted pattern
187+
| TypeSplice -- deprecated syntax
188188
| SimpleType1 TypeArgs AppliedTypeTree(t, args)
189189
| SimpleType1 ‘#’ id Select(t, name)
190190
Singleton ::= SimpleRef
@@ -243,8 +243,7 @@ SimpleExpr ::= SimpleRef
243243
| Literal
244244
| ‘_’
245245
| BlockExpr
246-
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
247-
| ‘$’ ‘{’ Pattern ‘}’ -- only inside quoted pattern
246+
| ExprSplice
248247
| Quoted
249248
| quoteId -- only inside splices
250249
| ‘new’ ConstrApp {‘with’ ConstrApp} [TemplateBody] New(constr | templ)
@@ -259,8 +258,14 @@ SimpleExpr ::= SimpleRef
259258
| SimpleExpr ‘_’ PostfixOp(expr, _) (to be dropped)
260259
| XmlExpr -- to be dropped
261260
IndentedExpr ::= indent CaseClauses | Block outdent
262-
Quoted ::= ‘'’ ‘{’ Block ‘}’
261+
Quoted ::= ‘'’ ‘{’ Block ‘}’
263262
| ‘'’ ‘[’ Type ‘]’
263+
ExprSplice ::= spliceId -- if inside quoted block
264+
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
265+
| ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern
266+
TypeSplice ::= spliceId -- if inside quoted type -- deprecated syntax
267+
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted type pattern -- deprecated syntax
268+
| ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted type pattern -- deprecated syntax
264269
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
265270
ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here
266271
| Expr

tests/neg-macros/type-splice-in-val-pattern.check

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

tests/neg-macros/type-splice-in-val-pattern.scala

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

tests/neg/i15009a.check

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
-- Error: tests/neg/i15009a.scala:4:9 ----------------------------------------------------------------------------------
2+
4 | '[List[${Type.of[Int]}]] // error
3+
| ^^^^^^^^^^^^^^^
4+
| Type splicing with `$` in quotes not supported anymore
5+
|
6+
| Hint: To use a given Type[T] in a quote just write T directly
7+
-- Error: tests/neg/i15009a.scala:7:16 ---------------------------------------------------------------------------------
8+
7 | case '[List[$a]] => // error
9+
| ^^
10+
| Type splicing with `$` in quotes not supported anymore
11+
|
12+
| Hint: Use lower cased variable name without the `$` instead
13+
-- Error: tests/neg/i15009a.scala:10:16 --------------------------------------------------------------------------------
14+
10 | '{ List.empty[$int] } // error
15+
| ^^^^
16+
| Type splicing with `$` in quotes not supported anymore
17+
|
18+
| Hint: To use a given Type[T] in a quote just write T directly
19+
-- Error: tests/neg/i15009a.scala:11:9 ---------------------------------------------------------------------------------
20+
11 | val t: ${int} = ??? // error
21+
| ^^^^^^
22+
| Type splicing with `$` in quotes not supported anymore
23+
|
24+
| Hint: To use a given Type[T] in a quote just write T directly
25+
-- Error: tests/neg/i15009a.scala:3:2 ----------------------------------------------------------------------------------
26+
3 | '[Int] // error
27+
| ^^^^^^
28+
| Quoted types `'[..]` can only be used in patterns.
29+
|
30+
| Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead.
31+
-- [E006] Not Found Error: tests/neg/i15009a.scala:12:2 ----------------------------------------------------------------
32+
12 | $int // error: Not found: $int
33+
| ^^^^
34+
| Not found: $int
35+
|
36+
| longer explanation available when compiling with `-explain`

0 commit comments

Comments
 (0)