Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow old uses of quoted and spliced types #15012

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1911,8 +1911,6 @@ object desugar {
new UntypedTreeTraverser {
def traverse(tree: untpd.Tree)(using Context): Unit = tree match {
case Splice(expr) => collect(expr)
case TypSplice(expr) =>
report.error(TypeSpliceInValPattern(expr), tree.srcPos)
case _ => traverseChildren(tree)
}
}.traverse(expr)
Expand Down
9 changes: 0 additions & 9 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class Splice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree {
def isInBraces: Boolean = span.end != expr.span.end
}
case class TypSplice(expr: Tree)(implicit @constructorOnly src: SourceFile) extends TypTree
case class ForYield(enums: List[Tree], expr: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
case class ForDo(enums: List[Tree], body: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree
case class GenFrom(pat: Tree, expr: Tree, checkMode: GenCheckMode)(implicit @constructorOnly src: SourceFile) extends Tree
Expand Down Expand Up @@ -613,10 +612,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case tree: Splice if expr eq tree.expr => tree
case _ => finalize(tree, untpd.Splice(expr)(tree.source))
}
def TypSplice(tree: Tree)(expr: Tree)(using Context): Tree = tree match {
case tree: TypSplice if expr eq tree.expr => tree
case _ => finalize(tree, untpd.TypSplice(expr)(tree.source))
}
def ForYield(tree: Tree)(enums: List[Tree], expr: Tree)(using Context): TermTree = tree match {
case tree: ForYield if (enums eq tree.enums) && (expr eq tree.expr) => tree
case _ => finalize(tree, untpd.ForYield(enums, expr)(tree.source))
Expand Down Expand Up @@ -695,8 +690,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
cpy.Quote(tree)(transform(t))
case Splice(expr) =>
cpy.Splice(tree)(transform(expr))
case TypSplice(expr) =>
cpy.TypSplice(tree)(transform(expr))
case ForYield(enums, expr) =>
cpy.ForYield(tree)(transform(enums), transform(expr))
case ForDo(enums, body) =>
Expand Down Expand Up @@ -754,8 +747,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
this(x, t)
case Splice(expr) =>
this(x, expr)
case TypSplice(expr) =>
this(x, expr)
case ForYield(enums, expr) =>
this(this(x, enums), expr)
case ForDo(enums, body) =>
Expand Down
31 changes: 20 additions & 11 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,6 @@ object Parsers {
makeParameter(nme.ERROR, tree, mods)
case Typed(id @ Ident(name), tpt) =>
makeParameter(name.asTermName, tpt, mods, isBackquoted = isBackquoted(id)).withSpan(tree.span)
case Typed(Splice(Ident(name)), tpt) =>
makeParameter(("$" + name).toTermName, tpt, mods).withSpan(tree.span)
case _ =>
syntaxError(s"not a legal $expected", tree.span)
makeParameter(nme.ERROR, tree, mods)
Expand Down Expand Up @@ -1572,13 +1570,15 @@ object Parsers {
/** The block in a quote or splice */
def stagedBlock() = inBraces(block(simplify = true))

/** SimpleExpr ::= spliceId | ‘$’ ‘{’ Block ‘}’) unless inside quoted pattern
* SimpleType ::= spliceId | ‘$’ ‘{’ Block ‘}’) unless inside quoted pattern
*
* SimpleExpr ::= spliceId | ‘$’ ‘{’ Pattern ‘}’) when inside quoted pattern
* SimpleType ::= spliceId | ‘$’ ‘{’ Pattern ‘}’) when inside quoted pattern
/** ExprSplice ::= ‘$’ spliceId -- if inside quoted block
* | ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
* | ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern
* TypeSplice ::= ‘$’ spliceId -- if inside quoted type
* | ‘$’ ‘{’ Block ‘}’ -- unless inside quoted type pattern
* | ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted type pattern
*/
def splice(isType: Boolean): Tree =
val start = in.offset
atSpan(in.offset) {
val expr =
if (in.name.length == 1) {
Expand All @@ -1591,13 +1591,22 @@ object Parsers {
in.nextToken()
id
}
if (isType) TypSplice(expr) else Splice(expr)
if isType then
val msg = "Type splicing with `$` in quotes not supported anymore"
val inPattern = (staged & StageKind.QuotedPattern) != 0
val hint =
if inPattern then "Use lower cased variable name without the `$` instead"
else "To use a given Type[T] in a quote just write T directly"
syntaxError(s"$msg\n\nHint: $hint", Span(start, in.lastOffset))
Ident(nme.ERROR.toTypeName)
else
Splice(expr)
}

/** SimpleType ::= SimpleLiteral
* | ‘?’ SubtypeBounds
* | SimpleType1
* | SimpeType ‘(’ Singletons ‘)’ -- under language.experimental.dependent, checked in Typer
* | SimpleType ‘(’ Singletons ‘)’ -- under language.experimental.dependent, checked in Typer
* Singletons ::= Singleton {‘,’ Singleton}
*/
def simpleType(): Tree =
Expand Down Expand Up @@ -1635,7 +1644,7 @@ object Parsers {
* | Singleton `.' type
* | ‘(’ ArgTypes ‘)’
* | Refinement
* | ‘$’ ‘{’ Block ‘}’
* | TypeSplice -- deprecated syntax (since 3.0.0)
* | SimpleType1 TypeArgs
* | SimpleType1 `#' id
*/
Expand Down Expand Up @@ -2237,7 +2246,7 @@ object Parsers {
/** SimpleExpr ::= ‘new’ ConstrApp {`with` ConstrApp} [TemplateBody]
* | ‘new’ TemplateBody
* | BlockExpr
* | ‘$’ ‘{’ Block ‘}’
* | ExprSplice
* | Quoted
* | quoteId
* | SimpleExpr1 [`_`]
Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -691,8 +691,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
keywordStr("'{") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
case Splice(tree) =>
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
case TypSplice(tree) =>
keywordStr("${") ~ toTextGlobal(dropBlock(tree)) ~ keywordStr("}")
case Thicket(trees) =>
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
case MacroTree(call) =>
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case TypeMismatchID // errorNumber: 7
case NotAMemberID // errorNumber: 8
case EarlyDefinitionsNotSupportedID // errorNumber: 9
case TopLevelImplicitClassID extends ErrorMessageID(isActive = false) // errorNumber: 10
case ImplicitCaseClassID // errorNumber: 11
case TopLevelImplicitClassID extends ErrorMessageID(isActive = false) // errorNumber: 10
case ImplicitCaseClassID // errorNumber: 11
case ImplicitClassPrimaryConstructorArityID // errorNumber: 12
case ObjectMayNotHaveSelfTypeID // errorNumber: 13
case TupleTooLongID extends ErrorMessageID(isActive = false) // errorNumber: 14
Expand Down Expand Up @@ -97,7 +97,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case FunctionTypeNeedsNonEmptyParameterListID // errorNumber: 85
case WrongNumberOfParametersID // errorNumber: 86
case DuplicatePrivateProtectedQualifierID // errorNumber: 87
case ExpectedStartOfTopLevelDefinitionID // errorNumber: 88
case ExpectedStartOfTopLevelDefinitionID // errorNumber: 88
case MissingReturnTypeWithReturnStatementID // errorNumber: 89
case NoReturnFromInlineableID // errorNumber: 90
case ReturnOutsideMethodDefinitionID // errorNumber: 91
Expand Down Expand Up @@ -164,7 +164,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
case ExtensionCanOnlyHaveDefsID // errorNumber: 152
case UnexpectedPatternForSummonFromID // errorNumber: 153
case AnonymousInstanceCannotBeEmptyID // errorNumber: 154
case TypeSpliceInValPatternID // errorNumber: 155
case TypeSpliceInValPatternID extends ErrorMessageID(isActive = false) // errorNumber: 155
case ModifierNotAllowedForDefinitionID // errorNumber: 156
case CannotExtendJavaEnumID // errorNumber: 157
case InvalidReferenceInImplicitNotFoundAnnotationID // errorNumber: 158
Expand Down
9 changes: 0 additions & 9 deletions compiler/src/dotty/tools/dotc/reporting/messages.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2447,15 +2447,6 @@ import transform.SymUtils._
|""".stripMargin
}

class TypeSpliceInValPattern(expr: untpd.Tree)(using Context)
extends SyntaxMsg(TypeSpliceInValPatternID) {
def msg = "Type splices cannot be used in val patterns. Consider using `match` instead."
def explain =
em"""|Type splice: `$$${expr.show}` cannot be used in a `val` pattern. Consider rewriting the `val` pattern
|as a `match` with a corresponding `case` to replace the `val`.
|""".stripMargin
}

class ModifierNotAllowedForDefinition(flag: Flag)(using Context)
extends SyntaxMsg(ModifierNotAllowedForDefinitionID) {
def msg = em"Modifier ${hl(flag.flagsString)} is not allowed for this definition"
Expand Down
39 changes: 7 additions & 32 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ trait QuotesAndSplices {
tree.quoted match {
case untpd.Splice(innerExpr) if tree.isTerm && !ctx.mode.is(Mode.Pattern) =>
report.warning("Canceled splice directly inside a quote. '{ ${ XYZ } } is equivalent to XYZ.", tree.srcPos)
case untpd.TypSplice(innerType) if tree.isType =>
report.warning("Canceled splice directly inside a quote. '[ ${ XYZ } ] is equivalent to XYZ.", tree.srcPos)
case _ =>
}
val qctx = inferImplicitArg(defn.QuotesClass.typeRef, tree.span)
Expand All @@ -52,11 +50,12 @@ trait QuotesAndSplices {
if ctx.mode.is(Mode.Pattern) then
typedQuotePattern(tree, pt, qctx).withSpan(tree.span)
else if tree.quoted.isType then
val msg = em"Consider using canonical type constructor scala.quoted.Type.of[${tree.quoted}] instead"
if sourceVersion.isAtLeast(`future-migration`) then report.error(msg, tree.srcPos)
else report.warning(msg, tree.srcPos)
val typeOfTree = untpd.TypeApply(untpd.ref(defn.QuotedTypeModule_of.termRef), tree.quoted :: Nil).withSpan(tree.span)
makeInlineable(typedTypeApply(typeOfTree, pt)(using quoteContext).select(nme.apply).appliedTo(qctx).withSpan(tree.span))
val msg = em"""Quoted types `'[..]` can only be used in patterns.
|
|Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead.
|""".stripMargin
report.error(msg, tree.srcPos)
EmptyTree
else
val exprQuoteTree = untpd.Apply(untpd.ref(defn.QuotedRuntime_exprQuote.termRef), tree.quoted)
makeInlineable(typedApply(exprQuoteTree, pt)(using pushQuotes(qctx)).select(nme.apply).appliedTo(qctx).withSpan(tree.span))
Expand Down Expand Up @@ -135,7 +134,7 @@ trait QuotesAndSplices {
case arg: untpd.Ident =>
typedExpr(arg)
case arg =>
report.error("Open patttern exprected an identifier", arg.srcPos)
report.error("Open pattern expected an identifier", arg.srcPos)
EmptyTree
}
if args.isEmpty then
Expand All @@ -145,30 +144,6 @@ trait QuotesAndSplices {
ref(defn.QuotedRuntimePatterns_patternHigherOrderHole).appliedToType(pt).appliedTo(typedPat, SeqLiteral(typedArgs, TypeTree(defn.AnyType)))
}

/** Translate ${ t: Type[T] }` into type `t.splice` while tracking the quotation level in the context */
def typedTypSplice(tree: untpd.TypSplice, pt: Type)(using Context): Tree = {
record("typedTypSplice")
checkSpliceOutsideQuote(tree)
tree.expr match {
case untpd.Quote(innerType) if innerType.isType =>
report.warning("Canceled quote directly inside a splice. ${ '[ XYZ ] } is equivalent to XYZ.", tree.srcPos)
case _ =>
}

if ctx.mode.is(Mode.QuotedPattern) && level == 1 then
report.error(
"""`$` for quote pattern variable is not supported anymore.
|Use lower cased variable name without the `$` instead.""".stripMargin,
tree.srcPos)
ref(defn.NothingType)
else
val tree1 = typedSelect(untpd.Select(tree.expr, tpnme.Underlying), pt)(using spliceContext).withSpan(tree.span)
val msg = em"Consider using canonical type reference ${tree1.tpe} instead"
if sourceVersion.isAtLeast(`future-migration`) then report.error(msg, tree.srcPos)
else report.warning(msg, tree.srcPos)
tree1
}

/** Type a pattern variable name `t` in quote pattern as `${given t$giveni: Type[t @ _]}`.
* The resulting pattern is the split in `splitQuotePattern`.
*/
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2898,7 +2898,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
case untpd.EmptyTree => tpd.EmptyTree
case tree: untpd.Quote => typedQuote(tree, pt)
case tree: untpd.Splice => typedSplice(tree, pt)
case tree: untpd.TypSplice => typedTypSplice(tree, pt)
case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here
case tree: untpd.Hole => typedHole(tree, pt)
case _ => typedUnadapted(desugar(tree), pt, locked)
Expand Down
17 changes: 11 additions & 6 deletions docs/_docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ upper ::= ‘A’ | … | ‘Z’ | ‘\$’ | ‘_’ “… and U
lower ::= ‘a’ | … | ‘z’ “… and Unicode category Ll”
letter ::= upper | lower “… and Unicode categories Lo, Lt, Lm, Nl”
digit ::= ‘0’ | … | ‘9’
paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’ | ‘'(’ | ‘'[’ | ‘'{’
paren ::= ‘(’ | ‘)’ | ‘[’ | ‘]’ | ‘{’ | ‘}’
delim ::= ‘`’ | ‘'’ | ‘"’ | ‘.’ | ‘;’ | ‘,’
opchar ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ |
‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’
Expand All @@ -45,6 +45,7 @@ id ::= plainid
| ‘`’ { charNoBackQuoteOrNewline | UnicodeEscape | charEscapeSeq } ‘`’
idrest ::= {letter | digit} [‘_’ op]
quoteId ::= ‘'’ alphaid
spliceId ::= ‘$’ alphaid ;

integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’]
decimalNumeral ::= ‘0’ | nonZeroDigit [{digit | ‘_’} digit]
Expand Down Expand Up @@ -183,8 +184,7 @@ SimpleType1 ::= id
| Singleton ‘.’ ‘type’ SingletonTypeTree(p)
| ‘(’ Types ‘)’ Tuple(ts)
| Refinement RefinedTypeTree(EmptyTree, refinement)
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
| ‘$’ ‘{’ Pattern ‘}’ -- only inside quoted pattern
| TypeSplice -- deprecated syntax
| SimpleType1 TypeArgs AppliedTypeTree(t, args)
| SimpleType1 ‘#’ id Select(t, name)
Singleton ::= SimpleRef
Expand Down Expand Up @@ -243,8 +243,7 @@ SimpleExpr ::= SimpleRef
| Literal
| ‘_’
| BlockExpr
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
| ‘$’ ‘{’ Pattern ‘}’ -- only inside quoted pattern
| ExprSplice
| Quoted
| quoteId -- only inside splices
| ‘new’ ConstrApp {‘with’ ConstrApp} [TemplateBody] New(constr | templ)
Expand All @@ -259,8 +258,14 @@ SimpleExpr ::= SimpleRef
| SimpleExpr ‘_’ PostfixOp(expr, _) (to be dropped)
| XmlExpr -- to be dropped
IndentedExpr ::= indent CaseClauses | Block outdent
Quoted ::= ‘'’ ‘{’ Block ‘}’
Quoted ::= ‘'’ ‘{’ Block ‘}’
| ‘'’ ‘[’ Type ‘]’
ExprSplice ::= spliceId -- if inside quoted block
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
| ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern
TypeSplice ::= spliceId -- if inside quoted type -- deprecated syntax
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted type pattern -- deprecated syntax
| ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted type pattern -- deprecated syntax
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here
| Expr
Expand Down
6 changes: 0 additions & 6 deletions tests/neg-macros/type-splice-in-val-pattern.check

This file was deleted.

7 changes: 0 additions & 7 deletions tests/neg-macros/type-splice-in-val-pattern.scala

This file was deleted.

36 changes: 36 additions & 0 deletions tests/neg/i15009a.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- Error: tests/neg/i15009a.scala:4:9 ----------------------------------------------------------------------------------
4 | '[List[${Type.of[Int]}]] // error
| ^^^^^^^^^^^^^^^
| Type splicing with `$` in quotes not supported anymore
|
| Hint: To use a given Type[T] in a quote just write T directly
-- Error: tests/neg/i15009a.scala:7:16 ---------------------------------------------------------------------------------
7 | case '[List[$a]] => // error
| ^^
| Type splicing with `$` in quotes not supported anymore
|
| Hint: Use lower cased variable name without the `$` instead
-- Error: tests/neg/i15009a.scala:10:16 --------------------------------------------------------------------------------
10 | '{ List.empty[$int] } // error
| ^^^^
| Type splicing with `$` in quotes not supported anymore
|
| Hint: To use a given Type[T] in a quote just write T directly
-- Error: tests/neg/i15009a.scala:11:9 ---------------------------------------------------------------------------------
11 | val t: ${int} = ??? // error
| ^^^^^^
| Type splicing with `$` in quotes not supported anymore
|
| Hint: To use a given Type[T] in a quote just write T directly
-- Error: tests/neg/i15009a.scala:3:2 ----------------------------------------------------------------------------------
3 | '[Int] // error
| ^^^^^^
| Quoted types `'[..]` can only be used in patterns.
|
| Hint: To get a scala.quoted.Type[T] use scala.quoted.Type.of[T] instead.
-- [E006] Not Found Error: tests/neg/i15009a.scala:12:2 ----------------------------------------------------------------
12 | $int // error: Not found: $int
| ^^^^
| Not found: $int
|
| longer explanation available when compiling with `-explain`
Loading