Skip to content

Commit d8f2102

Browse files
Backport "Emit an error for quoted pattern type variable after new" to 3.7.3 (#23644)
Backports #23618 to the 3.7.3-RC1. PR submitted by the release tooling. [skip ci]
2 parents 208b007 + 02886ac commit d8f2102

File tree

14 files changed

+155
-1
lines changed

14 files changed

+155
-1
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe
232232
case UnnecessaryNN // errorNumber: 216
233233
case ErasedNotPureID // errorNumber: 217
234234
case IllegalErasedDefID // errorNumber: 218
235+
case CannotInstantiateQuotedTypeVarID // errorNumber: 219
235236

236237
def errorNumber = ordinal - 1
237238

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3435,6 +3435,18 @@ final class QuotedTypeMissing(tpe: Type)(using Context) extends StagingMessage(Q
34353435

34363436
end QuotedTypeMissing
34373437

3438+
final class CannotInstantiateQuotedTypeVar(symbol: Symbol)(using patternCtx: Context) extends StagingMessage(CannotInstantiateQuotedTypeVarID):
3439+
override protected def msg(using Context): String =
3440+
i"""Quoted pattern type variable `${symbol.name}` cannot be instantiated.
3441+
|If you meant to refer to a class named `${symbol.name}`, wrap it in backticks.
3442+
|If you meant to introduce a binding, this is not allowed after `new`. You might
3443+
|want to use the lower-level `quotes.reflect` API instead.
3444+
|Read more about type variables in quoted pattern in the Scala documentation:
3445+
|https://docs.scala-lang.org/scala3/guides/macros/quotes.html#type-variables-in-quoted-patterns
3446+
"""
3447+
3448+
override protected def explain(using Context): String = ""
3449+
34383450
final class DeprecatedAssignmentSyntax(key: Name, value: untpd.Tree)(using Context) extends SyntaxMsg(DeprecatedAssignmentSyntaxID):
34393451
override protected def msg(using Context): String =
34403452
i"""Deprecated syntax: since 3.7 this is interpreted as a named tuple with one element,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ trait QuotesAndSplices {
222222
if ctx.mode.is(Mode.InPatternAlternative) then
223223
report.error(IllegalVariableInPatternAlternative(tree.name), tree.srcPos)
224224
val typeSym = inContext(quotePatternOuterContext(ctx)) {
225-
newSymbol(ctx.owner, tree.name.toTypeName, Case, typeSymInfo, NoSymbol, tree.span)
225+
newSymbol(ctx.owner, tree.name.toTypeName, Synthetic | Case, typeSymInfo, NoSymbol, tree.span)
226226
}
227227
addQuotedPatternTypeVariable(typeSym)
228228
Bind(typeSym, untpd.Ident(nme.WILDCARD).withType(typeSymInfo)).withSpan(tree.span)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
12371237
case _ =>
12381238
var tpt1 = typedType(tree.tpt)
12391239
val tsym = tpt1.tpe.underlyingClassRef(refinementOK = false).typeSymbol
1240+
if ctx.mode.isQuotedPattern && tpt1.tpe.typeSymbol.isAllOf(Synthetic | Case) then
1241+
val errorTp = errorType(CannotInstantiateQuotedTypeVar(tpt1.tpe.typeSymbol), tpt1.srcPos)
1242+
return cpy.New(tree)(tpt1).withType(errorTp)
12401243
if tsym.is(Package) then
12411244
report error(em"$tsym cannot be instantiated", tpt1.srcPos)
12421245
tpt1 = tpt1.withType(ensureAccessible(tpt1.tpe, superAccess = false, tpt1.srcPos))

tests/neg-macros/i22616.check

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- [E219] Staging Issue Error: tests/neg-macros/i22616.scala:13:22 -----------------------------------------------------
2+
13 | case '{ new caseName(${ Expr(name) }) } => Some(caseName(name)) // error // error
3+
| ^^^^^^^^
4+
| Quoted pattern type variable `caseName` cannot be instantiated.
5+
| If you meant to refer to a class named `caseName`, wrap it in backticks.
6+
| If you meant to introduce a binding, this is not allowed after `new`. You might
7+
| want to use the lower-level `quotes.reflect` API instead.
8+
| Read more about type variables in quoted pattern in the Scala documentation:
9+
| https://docs.scala-lang.org/scala3/guides/macros/quotes.html#type-variables-in-quoted-patterns
10+
|
11+
-- [E006] Not Found Error: tests/neg-macros/i22616.scala:13:67 ---------------------------------------------------------
12+
13 | case '{ new caseName(${ Expr(name) }) } => Some(caseName(name)) // error // error
13+
| ^^^^
14+
| Not found: name
15+
|
16+
| longer explanation available when compiling with `-explain`

tests/neg-macros/i22616.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import scala.quoted.*
2+
3+
final case class caseName(name: String) extends scala.annotation.Annotation
4+
object caseName {
5+
6+
given FromExpr[caseName] =
7+
new FromExpr[caseName] {
8+
override def unapply(x: Expr[caseName])(using Quotes): Option[caseName] =
9+
val y: Int = 42
10+
x match {
11+
case '{ caseName(${ Expr(name) }) } => Some(caseName(name))
12+
// with/without the following line...
13+
case '{ new caseName(${ Expr(name) }) } => Some(caseName(name)) // error // error
14+
case _ => println(x.show); None
15+
}
16+
}
17+
18+
}

tests/neg-macros/i22616b.check

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-- [E007] Type Mismatch Error: tests/neg-macros/i22616b.scala:17:18 ----------------------------------------------------
2+
17 | case '{ Foo($y: t) } => // error
3+
| ^^^^^
4+
| Found: t
5+
| Required: String
6+
|
7+
| longer explanation available when compiling with `-explain`
8+
-- [E006] Not Found Error: tests/neg-macros/i22616b.scala:18:19 --------------------------------------------------------
9+
18 | '{type S = t; ()} // error
10+
| ^
11+
| Not found: type t
12+
|
13+
| longer explanation available when compiling with `-explain`

tests/neg-macros/i22616b.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// This test illustrates a current limitation of quoted pattern type variables,
2+
// which has been discussed in https://github.com/scala/scala3/issues/22616#issuecomment-3012534064:
3+
// These type variables do not have bound in general (see `typedQuotedTypeVar`),
4+
// so they might not conform to the expected type. Here, `t` does not conform
5+
// to `String`.
6+
7+
import scala.quoted.{FromExpr, Expr, Quotes}
8+
9+
case class Foo(x: String)
10+
11+
object Macro:
12+
inline def myMacro(): Unit =
13+
${ myMacroImpl('{Foo("hello")}) }
14+
15+
def myMacroImpl(x: Expr[Foo])(using Quotes): Expr[Unit] =
16+
x match
17+
case '{ Foo($y: t) } => // error
18+
'{type S = t; ()} // error
19+
case _ =>
20+
println("not a foo")
21+
22+
'{()}

tests/run-macros/i22616c.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
_B_
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import scala.quoted.*
2+
3+
object Macro:
4+
inline def myMacro[T](): String =
5+
${ myMacroImpl[T]() }
6+
7+
def myMacroImpl[T: Type]()(using Quotes): Expr[String] =
8+
import quotes.reflect.*
9+
val myTypeRepr = MyTypeRepr(TypeRepr.of[T])
10+
val `caseName`(name) = myTypeRepr.requiredAnnotationValue[caseName]
11+
Expr(name)

0 commit comments

Comments
 (0)