diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 5ab4c7eb0b28..f8befc5a19df 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -229,6 +229,10 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case PointlessAppliedConstructorTypeID // errorNumber: 213 case IllegalContextBoundsID // errorNumber: 214 case NamedPatternNotApplicableID // errorNumber: 215 + case UnnecessaryNN // errorNumber: 216 + case ErasedNotPureID // errorNumber: 217 + case IllegalErasedDefID // errorNumber: 218 + case CannotInstantiateQuotedTypeVarID // errorNumber: 219 def errorNumber = ordinal - 1 diff --git a/tests/neg-macros/i22616b.check b/tests/neg-macros/i22616b.check new file mode 100644 index 000000000000..3c6007276cef --- /dev/null +++ b/tests/neg-macros/i22616b.check @@ -0,0 +1,13 @@ +-- [E007] Type Mismatch Error: tests/neg-macros/i22616b.scala:17:18 ---------------------------------------------------- +17 | case '{ Foo($y: t) } => // error + | ^^^^^ + | Found: t + | Required: String + | + | longer explanation available when compiling with `-explain` +-- [E006] Not Found Error: tests/neg-macros/i22616b.scala:18:19 -------------------------------------------------------- +18 | '{type S = t; ()} // error + | ^ + | Not found: type t + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-macros/i22616b.scala b/tests/neg-macros/i22616b.scala new file mode 100644 index 000000000000..45926f4e0d37 --- /dev/null +++ b/tests/neg-macros/i22616b.scala @@ -0,0 +1,22 @@ +// This test illustrates a current limitation of quoted pattern type variables, +// which has been discussed in https://github.com/scala/scala3/issues/22616#issuecomment-3012534064: +// These type variables do not have bound in general (see `typedQuotedTypeVar`), +// so they might not conform to the expected type. Here, `t` does not conform +// to `String`. + +import scala.quoted.{FromExpr, Expr, Quotes} + +case class Foo(x: String) + +object Macro: + inline def myMacro(): Unit = + ${ myMacroImpl('{Foo("hello")}) } + + def myMacroImpl(x: Expr[Foo])(using Quotes): Expr[Unit] = + x match + case '{ Foo($y: t) } => // error + '{type S = t; ()} // error + case _ => + println("not a foo") + + '{()} diff --git a/tests/run-macros/i22616c.check b/tests/run-macros/i22616c.check new file mode 100644 index 000000000000..d1918272a8f7 --- /dev/null +++ b/tests/run-macros/i22616c.check @@ -0,0 +1 @@ +_B_ diff --git a/tests/run-macros/i22616c/Macro_4.scala b/tests/run-macros/i22616c/Macro_4.scala new file mode 100644 index 000000000000..02e677db3428 --- /dev/null +++ b/tests/run-macros/i22616c/Macro_4.scala @@ -0,0 +1,11 @@ +import scala.quoted.* + +object Macro: + inline def myMacro[T](): String = + ${ myMacroImpl[T]() } + + def myMacroImpl[T: Type]()(using Quotes): Expr[String] = + import quotes.reflect.* + val myTypeRepr = MyTypeRepr(TypeRepr.of[T]) + val `caseName`(name) = myTypeRepr.requiredAnnotationValue[caseName] + Expr(name) diff --git a/tests/run-macros/i22616c/MyTypeRepr_3.scala b/tests/run-macros/i22616c/MyTypeRepr_3.scala new file mode 100644 index 000000000000..1a35889d403d --- /dev/null +++ b/tests/run-macros/i22616c/MyTypeRepr_3.scala @@ -0,0 +1,33 @@ +import scala.quoted.* + +final class MyTypeRepr(using val quotes: Quotes)(val unwrap: quotes.reflect.TypeRepr) { + import quotes.reflect.* + + def getAnnotation(annotTpe: quotes.reflect.Symbol): Option[quotes.reflect.Term] = + unwrap.typeSymbol.getAnnotation(annotTpe) + + def optionalAnnotation[Annot: Type]: Option[Expr[Annot]] = { + val annotTpe = TypeRepr.of[Annot] + val annotFlags = annotTpe.typeSymbol.flags + + if (annotFlags.is(Flags.Abstract) || annotFlags.is(Flags.Trait)) + report.errorAndAbort(s"Bad annotation type ${annotTpe.show} is abstract") + + this.getAnnotation(annotTpe.typeSymbol) match + case Some(tree) if tree.tpe <:< annotTpe => Some(tree.asExprOf[Annot]) + case _ => None + } + + def requiredAnnotation[Annot: Type]: Expr[Annot] = + optionalAnnotation[Annot].getOrElse(report.errorAndAbort(s"Missing required annotation `${TypeRepr.of[Annot].show}` for `$this`")) + + def optionalAnnotationValue[Annot: {Type, FromExpr}]: Option[Annot] = + optionalAnnotation[Annot].map { expr => + expr.value.getOrElse(report.errorAndAbort(s"Found annotation `${TypeRepr.of[Annot].show}` for `$this`, but are unable to extract Expr.value\n${expr.show}")) + } + + def requiredAnnotationValue[Annot: {Type, FromExpr}]: Annot = { + val expr = requiredAnnotation[Annot] + expr.value.getOrElse(report.errorAndAbort(s"Found annotation `${TypeRepr.of[Annot].show}` for `$this`, but are unable to extract Expr.value\n${expr.show}")) + } +} diff --git a/tests/run-macros/i22616c/SealedTrait3_2.scala b/tests/run-macros/i22616c/SealedTrait3_2.scala new file mode 100644 index 000000000000..9141a9ebd08b --- /dev/null +++ b/tests/run-macros/i22616c/SealedTrait3_2.scala @@ -0,0 +1,8 @@ +sealed trait SealedTrait3[+A, +B] +object SealedTrait3 { + final case class AB1[+B, +A](a: B, b: A) extends SealedTrait3[B, A] + final case class AB2[+C, +D](a: C, b: D) extends SealedTrait3[D, C] + final case class A[+T](a: T) extends SealedTrait3[T, Nothing] + @caseName("_B_") final case class B[+T](b: T) extends SealedTrait3[Nothing, T] + case object Neither extends SealedTrait3[Nothing, Nothing] +} diff --git a/tests/run-macros/i22616c/Test_5.scala b/tests/run-macros/i22616c/Test_5.scala new file mode 100644 index 000000000000..c8d177a5ae1b --- /dev/null +++ b/tests/run-macros/i22616c/Test_5.scala @@ -0,0 +1,2 @@ +@main def Test = + println(Macro.myMacro[SealedTrait3.B[Any]]()) diff --git a/tests/run-macros/i22616c/caseName_1.scala b/tests/run-macros/i22616c/caseName_1.scala new file mode 100644 index 000000000000..c8fee116e2e9 --- /dev/null +++ b/tests/run-macros/i22616c/caseName_1.scala @@ -0,0 +1,14 @@ +import scala.quoted.* + +final case class caseName(name: String) extends scala.annotation.Annotation +object caseName { + // This demonstrates a workaround for issue #22616. + given FromExpr[caseName] = + new FromExpr[caseName] { + override def unapply(x: Expr[caseName])(using Quotes): Option[caseName] = + x match { + case '{ new `caseName`(${ Expr(name) }) } => Some(caseName(name)) + case _ => println(x.show); None + } + } +}