diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index faf420fcc44c..cf2c3c0e1a68 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 2decfd3c0479..a769df537f1a 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -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 @@ -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)) @@ -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) => @@ -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) => diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index c78e4cd8fb2f..3b7d11d16fd0 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -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) @@ -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) { @@ -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 = @@ -1635,7 +1644,7 @@ object Parsers { * | Singleton `.' type * | ‘(’ ArgTypes ‘)’ * | Refinement - * | ‘$’ ‘{’ Block ‘}’ + * | TypeSplice -- deprecated syntax (since 3.0.0) * | SimpleType1 TypeArgs * | SimpleType1 `#' id */ @@ -2237,7 +2246,7 @@ object Parsers { /** SimpleExpr ::= ‘new’ ConstrApp {`with` ConstrApp} [TemplateBody] * | ‘new’ TemplateBody * | BlockExpr - * | ‘$’ ‘{’ Block ‘}’ + * | ExprSplice * | Quoted * | quoteId * | SimpleExpr1 [`_`] diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 8bdf1d4822ce..5e89fa9c6e6b 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -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) => diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 0df893b7d6fd..c85006cc25be 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -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 @@ -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 @@ -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 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 1f3ed68c90b1..f2b14ee315df 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -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" diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 783c03cba812..d260d530994d 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -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) @@ -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)) @@ -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 @@ -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`. */ diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 34eb2b7df41f..eb0b92358db8 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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) diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index cae2ebae7af2..55b098e91849 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -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 ::= ‘!’ | ‘#’ | ‘%’ | ‘&’ | ‘*’ | ‘+’ | ‘-’ | ‘/’ | ‘:’ | ‘<’ | ‘=’ | ‘>’ | ‘?’ | ‘@’ | ‘\’ | ‘^’ | ‘|’ | ‘~’ @@ -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] @@ -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 @@ -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) @@ -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 diff --git a/tests/neg-macros/type-splice-in-val-pattern.check b/tests/neg-macros/type-splice-in-val-pattern.check deleted file mode 100644 index 627aa2d5755d..000000000000 --- a/tests/neg-macros/type-splice-in-val-pattern.check +++ /dev/null @@ -1,6 +0,0 @@ --- [E155] Syntax Error: tests/neg-macros/type-splice-in-val-pattern.scala:5:14 ----------------------------------------- -5 | val '[ *:[$t] ] = ??? // error - | ^^ - | Type splices cannot be used in val patterns. Consider using `match` instead. - | - | longer explanation available when compiling with `-explain` diff --git a/tests/neg-macros/type-splice-in-val-pattern.scala b/tests/neg-macros/type-splice-in-val-pattern.scala deleted file mode 100644 index 7e5e95cb7785..000000000000 --- a/tests/neg-macros/type-splice-in-val-pattern.scala +++ /dev/null @@ -1,7 +0,0 @@ -import scala.quoted.* -object Foo { - def f(using q: Quotes) = { - val t: Type[Int] = ??? - val '[ *:[$t] ] = ??? // error - } -} \ No newline at end of file diff --git a/tests/neg/i15009a.check b/tests/neg/i15009a.check new file mode 100644 index 000000000000..4bdca5318d38 --- /dev/null +++ b/tests/neg/i15009a.check @@ -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` diff --git a/tests/neg/i15009a.scala b/tests/neg/i15009a.scala new file mode 100644 index 000000000000..6360b251b83e --- /dev/null +++ b/tests/neg/i15009a.scala @@ -0,0 +1,13 @@ +import scala.quoted.* +def test(using Quotes): Unit = { + '[Int] // error + '[List[${Type.of[Int]}]] // error + + Type.of[Int] match + case '[List[$a]] => // error + + val int = Type.of[Int] + '{ List.empty[$int] } // error + val t: ${int} = ??? // error + $int // error: Not found: $int +}