diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 3759323515ab..b7bcc43f0050 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -381,11 +381,14 @@ object desugar { tree match case untpd.Block(stats, expr) => val (untpdTypeVariables, otherStats) = stats.span { - case tdef @ untpd.TypeDef(name, _) => name.isVarPattern + case tdef @ untpd.TypeDef(name, _) => !tdef.isBackquoted && name.isVarPattern case _ => false } + val untpdCaseTypeVariables = untpdTypeVariables.asInstanceOf[List[untpd.TypeDef]].map { + tdef => tdef.withMods(tdef.mods | Case) + } val pattern = if otherStats.isEmpty then expr else untpd.cpy.Block(tree)(otherStats, expr) - (untpdTypeVariables.asInstanceOf[List[untpd.TypeDef]], pattern) + (untpdCaseTypeVariables, pattern) case _ => (Nil, tree) diff --git a/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala b/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala index c0ee8a7a5261..48884f6b2d6e 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala @@ -15,6 +15,7 @@ import dotty.tools.dotc.core.NameKinds.PatMatGivenVarName import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.* +import dotty.tools.dotc.core.TypeOps.* import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.reporting.IllegalVariableInPatternAlternative import dotty.tools.dotc.transform.SymUtils._ @@ -24,6 +25,33 @@ import scala.collection.mutable object QuotePatterns: import tpd._ + /** Check for restricted patterns */ + def checkPattern(quotePattern: QuotePattern)(using Context): Unit = new tpd.TreeTraverser { + def traverse(tree: Tree)(using Context): Unit = tree match { + case _: SplicePattern => + case tdef: TypeDef if tdef.symbol.isClass => + val kind = if tdef.symbol.is(Module) then "objects" else "classes" + report.error(em"Implementation restriction: cannot match $kind", tree.srcPos) + case tree: NamedDefTree => + if tree.name.is(NameKinds.WildcardParamName) then + report.warning( + "Use of `_` for lambda in quoted pattern. Use explicit lambda instead or use `$_` to match any term.", + tree.srcPos) + if tree.name.isTermName && !tree.nameSpan.isSynthetic && tree.name != nme.ANON_FUN && tree.name.startsWith("$") then + report.error("Names cannot start with $ quote pattern", tree.namePos) + traverseChildren(tree) + case _: Match => + report.error("Implementation restriction: cannot match `match` expressions", tree.srcPos) + case _: Try => + report.error("Implementation restriction: cannot match `try` expressions", tree.srcPos) + case _: Return => + report.error("Implementation restriction: cannot match `return` statements", tree.srcPos) + case _ => + traverseChildren(tree) + } + + }.traverse(quotePattern.body) + /** Encode the quote pattern into an `unapply` that the pattern matcher can handle. * * A quote pattern diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 04509124bd7c..314b2363f67a 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -156,8 +156,9 @@ trait QuotesAndSplices { typedSplicePattern(untpd.cpy.SplicePattern(tree)(splice.body, args), pt) } - /** Type a pattern variable name `t` in quote pattern as `${given t$giveni: Type[t @ _]}`. - * The resulting pattern is the split in `splitQuotePattern`. + /** Type check a type binding reference in a quoted pattern. + * + * If no binding exists with that name, this becomes the definition of a new type binding. */ def typedQuotedTypeVar(tree: untpd.Ident, pt: Type)(using Context): Tree = val typeSymInfo = pt match @@ -178,15 +179,13 @@ trait QuotesAndSplices { warnOnInferredBounds(typeSym) ref(typeSym) case None => - val spliceContext = quotePatternSpliceContext - val name = tree.name.toTypeName - val nameOfSyntheticGiven = PatMatGivenVarName.fresh(tree.name.toTermName) - val expr = untpd.cpy.Ident(tree)(nameOfSyntheticGiven) - val typeSym = newSymbol(spliceContext.owner, name, EmptyFlags, typeSymInfo, NoSymbol, tree.span) - typeSym.addAnnotation(Annotation(New(ref(defn.QuotedRuntimePatterns_patternTypeAnnot.typeRef)).withSpan(tree.span))) + if ctx.mode.is(Mode.InPatternAlternative) then + report.error(IllegalVariableInPatternAlternative(tree.name), tree.srcPos) + val typeSym = inContext(quotePatternOuterContext(ctx)) { + newSymbol(ctx.owner, tree.name.toTypeName, Case, typeSymInfo, NoSymbol, tree.span) + } addQuotedPatternTypeVariable(typeSym) - val pat = typedPattern(expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(using spliceContext) - pat.select(tpnme.Underlying) + Bind(typeSym, untpd.Ident(nme.WILDCARD).withType(typeSymInfo)).withSpan(tree.span) private def checkSpliceOutsideQuote(tree: untpd.Tree)(using Context): Unit = if (level == 0 && !ctx.owner.ownersIterator.exists(_.isInlineMethod)) @@ -199,234 +198,11 @@ trait QuotesAndSplices { |""", tree.srcPos ) - /** Split a typed quoted pattern is split into its type bindings, pattern expression and inner patterns. - * Type definitions with `@patternType` will be inserted in the pattern expression for each type binding. - * - * A quote pattern - * ``` - * case '{ type ${given t$giveni: Type[t @ _]}; ${ls: Expr[List[t]]} } => ... - * ``` - * will return - * ``` - * ( - * Map(: Symbol -> : Symbol), - * <'{ - * @scala.internal.Quoted.patternType type t - * scala.internal.Quoted.patternHole[List[t]] - * }>: Tree, - * List(: Tree) - * ) - * ``` - */ - private def splitQuotePattern(quoted: Tree)(using Context): (SeqMap[Symbol, Symbol], Tree, List[Tree]) = { - val ctx0 = ctx - - val bindSymMapping: SeqMap[Symbol, Symbol] = unapplyBindingsMapping(quoted) - - object splitter extends tpd.TreeMap { - private var variance: Int = 1 - - inline private def atVariance[T](v: Int)(op: => T): T = { - val saved = variance - variance = v - val res = op - variance = saved - res - } - - val patBuf = new mutable.ListBuffer[Tree] - val freshTypePatBuf = new mutable.ListBuffer[Tree] - val freshTypeBindingsBuff = new mutable.ListBuffer[Tree] - val typePatBuf = new mutable.ListBuffer[Tree] - override def transform(tree: Tree)(using Context) = tree match { - case Typed(splice @ SplicePattern(pat, Nil), tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => - transform(tpt) // Collect type bindings - transform(splice) - case SplicePattern(pat, args) => - val patType = pat.tpe.widen - val patType1 = patType.translateFromRepeated(toArray = false) - val pat1 = if (patType eq patType1) pat else pat.withType(patType1) - patBuf += pat1 - if args.isEmpty then ref(defn.QuotedRuntimePatterns_patternHole.termRef).appliedToType(tree.tpe).withSpan(tree.span) - else ref(defn.QuotedRuntimePatterns_higherOrderHole.termRef).appliedToType(tree.tpe).appliedTo(SeqLiteral(args, TypeTree(defn.AnyType))).withSpan(tree.span) - case Select(pat: Bind, _) if tree.symbol.isTypeSplice => - val sym = tree.tpe.dealias.typeSymbol - if sym.exists then - val tdef = TypeDef(sym.asType).withSpan(sym.span) - val nameOfSyntheticGiven = pat.symbol.name.toTermName - freshTypeBindingsBuff += transformTypeBindingTypeDef(nameOfSyntheticGiven, tdef, freshTypePatBuf) - TypeTree(tree.tpe.dealias).withSpan(tree.span) - else - tree - case tdef: TypeDef => - if tdef.symbol.hasAnnotation(defn.QuotedRuntimePatterns_patternTypeAnnot) then - transformTypeBindingTypeDef(PatMatGivenVarName.fresh(tdef.name.toTermName), tdef, typePatBuf) - else if tdef.symbol.isClass then - val kind = if tdef.symbol.is(Module) then "objects" else "classes" - report.error(em"Implementation restriction: cannot match $kind", tree.srcPos) - EmptyTree - else - super.transform(tree) - case tree @ AppliedTypeTree(tpt, args) => - val args1: List[Tree] = args.zipWithConserve(tpt.tpe.typeParams.map(_.paramVarianceSign)) { (arg, v) => - arg.tpe match { - case _: TypeBounds => transform(arg) - case _ => atVariance(variance * v)(transform(arg)) - } - } - cpy.AppliedTypeTree(tree)(transform(tpt), args1) - case tree: NamedDefTree => - if tree.name.is(NameKinds.WildcardParamName) then - report.warning( - "Use of `_` for lambda in quoted pattern. Use explicit lambda instead or use `$_` to match any term.", - tree.srcPos) - if tree.name.isTermName && !tree.nameSpan.isSynthetic && tree.name.startsWith("$") then - report.error("Names cannot start with $ quote pattern ", tree.namePos) - super.transform(tree) - case _: Match => - report.error("Implementation restriction: cannot match `match` expressions", tree.srcPos) - EmptyTree - case _: Try => - report.error("Implementation restriction: cannot match `try` expressions", tree.srcPos) - EmptyTree - case _: Return => - report.error("Implementation restriction: cannot match `return` statements", tree.srcPos) - EmptyTree - case _ => - super.transform(tree) - } - - private def transformTypeBindingTypeDef(nameOfSyntheticGiven: TermName, tdef: TypeDef, buff: mutable.Builder[Tree, List[Tree]])(using Context): Tree = { - if ctx.mode.is(Mode.InPatternAlternative) then - report.error(IllegalVariableInPatternAlternative(tdef.symbol.name), tdef.srcPos) - if variance == -1 then - tdef.symbol.addAnnotation(Annotation(New(ref(defn.QuotedRuntimePatterns_fromAboveAnnot.typeRef)).withSpan(tdef.span))) - val bindingType = bindSymMapping(tdef.symbol).typeRef - val bindingTypeTpe = AppliedType(defn.QuotedTypeClass.typeRef, bindingType :: Nil) - val sym = newPatternBoundSymbol(nameOfSyntheticGiven, bindingTypeTpe, tdef.span, flags = ImplicitVal)(using ctx0) - buff += Bind(sym, untpd.Ident(nme.WILDCARD).withType(bindingTypeTpe)).withSpan(tdef.span) - super.transform(tdef) - } - } - - val shape0 = splitter.transform(quoted) - val patterns = (splitter.typePatBuf.iterator ++ splitter.freshTypePatBuf.iterator ++ splitter.patBuf.iterator).toList - val freshTypeBindings = splitter.freshTypeBindingsBuff.result() - - val shape1 = shape0 match - case Block(stats @ ((tdef: TypeDef) :: rest), expr) if tdef.symbol.hasAnnotation(defn.QuotedRuntimePatterns_patternTypeAnnot) => - val (bindings, otherStats) = stats.span { - case tdef: TypeDef => tdef.symbol.hasAnnotation(defn.QuotedRuntimePatterns_patternTypeAnnot) - case _ => true - } - cpy.Block(shape0)(bindings ::: freshTypeBindings ::: otherStats, expr) - case _ => - seq(freshTypeBindings, shape0) - - val shape2 = - if (freshTypeBindings.isEmpty) shape1 - else { - val isFreshTypeBindings = freshTypeBindings.map(_.symbol).toSet - val typeMap = new TypeMap() { - def apply(tp: Type): Type = tp match { - case tp: TypeRef if tp.symbol.isTypeSplice => - val tp1 = tp.dealias - if (isFreshTypeBindings(tp1.typeSymbol)) tp1 - else tp - case tp => mapOver(tp) - } - } - new TreeTypeMap(typeMap = typeMap).transform(shape1) - } - - (bindSymMapping, shape2, patterns) - } - - /** For each type variable defined in the quote pattern we generate an equivalent - * binding that will be as type variable in the encoded `unapply` of the quote pattern. - * - * @return Mapping from type variable symbols defined in the quote pattern into - * type variable definitions for the `unapply` of the quote pattern. - * This mapping retains the original type variable definition order. - */ - private def unapplyBindingsMapping(quoted: Tree)(using Context): SeqMap[Symbol, Symbol] = { - val mapping = mutable.LinkedHashMap.empty[Symbol, Symbol] - - // Collect all existing type variable bindings and create new symbols for them. - // The old info is used, it may contain references to the old symbols. - new tpd.TreeTraverser { - def traverse(tree: Tree)(using Context): Unit = tree match { - case _: SplicePattern => - case Select(pat: Bind, _) if tree.symbol.isTypeSplice => - val sym = tree.tpe.dealias.typeSymbol - if sym.exists then registerNewBindSym(sym) - case tdef: TypeDef => - if tdef.symbol.hasAnnotation(defn.QuotedRuntimePatterns_patternTypeAnnot) then - registerNewBindSym(tdef.symbol) - traverseChildren(tdef) - case _ => - traverseChildren(tree) - } - private def registerNewBindSym(sym: Symbol): Unit = - if !mapping.contains(sym) then - mapping(sym) = newSymbol(ctx.owner, sym.name.toString.stripPrefix("$").toTypeName, Case | sym.flags, sym.info, coord = quoted.span) - }.traverse(quoted) - - // Replace symbols in `mapping` in the infos of the new symbol and register GADT bounds. - // GADT bounds need to be added after the info is updated to avoid references to the old symbols. - var oldBindings: List[Symbol] = mapping.keys.toList - var newBindingsRefs: List[Type] = mapping.values.toList.map(_.typeRef) - for newBindings <- mapping.values do - newBindings.info = newBindings.info.subst(oldBindings, newBindingsRefs) - ctx.gadtState.addToConstraint(newBindings) // This must be preformed after the info has been updated - - mapping - } - - /** Type a quote pattern `case '{ } =>` qiven the a current prototype. Typing the pattern - * will also transform it into a call to `scala.internal.quoted.Expr.unapply`. + /** Type a quote pattern `case '{ } =>` given the a current prototype. Typing the pattern + * will create a QuotePattern tree. * * Code directly inside the quote is typed as an expression using Mode.QuotedPattern. Splices * within the quotes become patterns again and typed accordingly. - * - * ``` - * case '{ ($ls: List[t]) } => - * // `t$giveni` is of type `Type[t]` for some unknown `t` - * // `t$giveni` is implicitly available - * // `ls` is of type `Expr[List[t]]` - * '{ val h: $t = $ls.head } - * ``` - * - * For each type splice we will create a new type binding in the pattern match (`t @ _` in this case) - * and a corresponding type in the quoted pattern as a hole (`@patternType type t` in this case). - * All these generated types are inserted at the start of the quoted code. - * - * After typing the tree will resemble - * - * ``` - * case '{ type ${given t$giveni: Type[t @ _]}; ${ls: Expr[List[t]]} } => ... - * ``` - * - * Then the pattern is _split_ into the expression contained in the pattern replacing the splices by holes, - * and the patterns in the splices. All these are recombined into a call to `Matcher.unapply`. - * - * ``` - * case scala.internal.quoted.Expr.unapply[ - * KList[t @ _, KNil], // Type binging definition - * Tuple2[Type[t], Expr[List[t]]] // Typing the result of the pattern match - * ]( - * Tuple2.unapply - * [Type[t], Expr[List[t]]] //Propagated from the tuple above - * (given t$giveni @ _, ls @ _: Expr[List[t]]) // from the spliced patterns - * )( - * '{ // Runtime quote Matcher.unapply uses to mach against. Expression directly inside the quoted pattern without the splices - * @scala.internal.Quoted.patternType type t - * scala.internal.Quoted.patternHole[List[t]] - * }, - * true, // If there is at least one type splice. Used to instantiate the context with or without GADT constraints - * x$2 // tasty.Reflection instance - * ) => ... - * ``` */ private def typedQuotePattern(tree: untpd.Quote, pt: Type, quotes: Tree)(using Context): Tree = { val quoted = tree.body @@ -453,83 +229,55 @@ trait QuotesAndSplices { untpdTypeVariables.head.srcPos, "\n\nSIP-53: https://docs.scala-lang.org/sips/quote-pattern-type-variable-syntax.html") - val (typeTypeVariables, patternCtx) = + if ctx.mode.is(Mode.InPatternAlternative) then + for tpVar <- untpdTypeVariables do + report.error(IllegalVariableInPatternAlternative(tpVar.name), tpVar.srcPos) + + val (typeTypeVariables, patternBlockCtx) = val quoteCtx = quotePatternContext(quoted.isType) if untpdTypeVariables.isEmpty then (Nil, quoteCtx) else typedBlockStats(untpdTypeVariables)(using quoteCtx) + val patternCtx = patternBlockCtx.addMode(if quoted.isType then Mode.QuotedTypePattern else Mode.QuotedExprPattern) - val quoted1 = inContext(patternCtx) { + val allTypeBindings = List.newBuilder[Bind] + for tpVar <- typeTypeVariables do + val sym = tpVar.symbol + allTypeBindings += Bind(sym, untpd.Ident(nme.WILDCARD).withType(sym.info)).withSpan(tpVar.span) + + val body1 = inContext(patternCtx) { for typeVariable <- typeTypeVariables do addQuotedPatternTypeVariable(typeVariable.symbol) - val pattern = - if quoted.isType then typedType(quoted0, WildcardType)(using patternCtx) - else typedExpr(quoted0, WildcardType) - - if untpdTypeVariables.isEmpty then pattern - else tpd.Block(typeTypeVariables, pattern) - } - - val (bindSymMapping, shape, splices) = splitQuotePattern(quoted1) - - class ReplaceBindings extends TypeMap() { - override def apply(tp: Type): Type = tp match { - case tp: TypeRef => - val tp1 = if (tp.symbol.isTypeSplice) tp.dealias else tp - mapOver(bindSymMapping.get(tp1.typeSymbol).fold(tp)(_.typeRef)) - case tp => mapOver(tp) - } - } - val replaceBindings = new ReplaceBindings - val patType = defn.tupleType(splices.tpes.map(tpe => replaceBindings(tpe.widen))) - - val typeBinds = bindSymMapping.values.map(sym => - Bind(sym, untpd.Ident(nme.WILDCARD).withType(sym.info)).withSpan(quoted.span) - ).toList - val typeBindingsTuple = tpd.hkNestedPairsTypeTree(typeBinds) - - val replaceBindingsInTree = new TreeMap { - private var bindMap = Map.empty[Symbol, Symbol] - override def transform(tree: tpd.Tree)(using Context): tpd.Tree = - tree match { - case tree: Bind => - val sym = tree.symbol - val newInfo = replaceBindings(sym.info) - val newSym = newSymbol(sym.owner, sym.name, sym.flags, newInfo, sym.privateWithin, sym.coord) - bindMap += sym -> newSym - Bind(newSym, transform(tree.body)).withSpan(sym.span) - case _ => - super.transform(tree).withType(replaceBindingsInType(tree.tpe)) - } - private val replaceBindingsInType = new ReplaceBindings { - override def apply(tp: Type): Type = tp match { - case tp: TermRef => bindMap.get(tp.termSymbol).fold[Type](tp)(_.typeRef) - case tp => super.apply(tp) - } - } + if quoted.isType then typedType(quoted0, WildcardType) + else typedExpr(quoted0, WildcardType) } - val splicePat = - if splices.isEmpty then ref(defn.EmptyTupleModule.termRef) - else typed(untpd.Tuple(splices.map(x => untpd.TypedSplice(replaceBindingsInTree.transform(x)))).withSpan(quoted.span), patType) + val extractTypeBindings = new TreeMapWithVariance { + override def transform(tree: Tree)(using Context) = tree match + case pat: Bind if pat.isType => + if inContravariantPosition then + pat.symbol.addAnnotation(Annotation(New(ref(defn.QuotedRuntimePatterns_fromAboveAnnot.typeRef)).withSpan(pat.span))) + allTypeBindings += pat + TypeTree(pat.symbol.typeRef).withSpan(pat.span) - val quoteClass = if (quoted.isTerm) defn.QuotedExprClass else defn.QuotedTypeClass - val quotedPattern = - if (quoted.isTerm) tpd.Quote(shape, Nil).select(nme.apply).appliedTo(quotes) - else ref(defn.QuotedTypeModule_of.termRef).appliedToTypeTree(shape).appliedTo(quotes) + case Typed(splice: SplicePattern, tpt) if !tpt.tpe.derivesFrom(defn.RepeatedParamClass) => + // We drop the type ascription because it is redundant, the SplicePattern contains the same type + transform(tpt) // Collect type bindings + splice + case _: SplicePattern => + tree - val matchModule = if quoted.isTerm then defn.QuoteMatching_ExprMatch else defn.QuoteMatching_TypeMatch - val unapplySym = if quoted.isTerm then defn.QuoteMatching_ExprMatch_unapply else defn.QuoteMatching_TypeMatch_unapply - val unapplyFun = quotes.asInstance(defn.QuoteMatchingClass.typeRef).select(matchModule).select(unapplySym) + case _ => + super.transform(tree) + } + val body2 = extractTypeBindings.transform(body1) - val encodedPattern = UnApply( - fun = unapplyFun.appliedToTypeTrees(typeBindingsTuple :: TypeTree(patType) :: Nil), - implicits = quotedPattern :: Nil, - patterns = splicePat :: Nil, - proto = quoteClass.typeRef.appliedTo(replaceBindings(quoted1.tpe))) + val quoteClass = if quoted.isTerm then defn.QuotedExprClass else defn.QuotedTypeClass + val pt1 = quoteClass.typeRef.appliedTo(body2.tpe) - if ctx.reporter.hasErrors then encodedPattern - else QuotePatterns.decode(encodedPattern) // TODO type directly into the encoded version + val quotePattern = QuotePattern(allTypeBindings.result(), body2, quotes, pt1) + QuotePatterns.checkPattern(quotePattern) + quotePattern } } @@ -550,8 +298,7 @@ object QuotesAndSplices { /** Context used to type the contents of a quote pattern */ def quotePatternContext(isTypePattern: Boolean)(using Context): Context = quoteContext.fresh.setNewScope - .addMode(if isTypePattern then Mode.QuotedTypePattern else Mode.QuotedExprPattern) - .retractMode(Mode.Pattern) // TODO do we need Mode.QuotedPattern? + .retractMode(Mode.Pattern) .setProperty(TypeVariableKey, collection.mutable.Map.empty) /** Context used to type the contents of a quote pattern splice */ @@ -559,10 +306,36 @@ object QuotesAndSplices { spliceContext .retractMode(Mode.QuotedPatternBits) .addMode(Mode.Pattern) - .withOwner(quotePatternOwner(ctx)) + .withOwner(quotePatternOuterContext(ctx).owner) - /** First outer context owner that is outside of a quoted pattern context. */ - private def quotePatternOwner(ctx: Context): Symbol = - if ctx.mode.isQuotedPattern then quotePatternOwner(ctx.outer) else ctx.owner + /** First outer context that is outside of a quoted pattern. */ + def quotePatternOuterContext(ctx: Context): Context = + if ctx.mode.isQuotedPattern then quotePatternOuterContext(ctx.outer) else ctx + private[QuotesAndSplices] class TreeMapWithVariance extends TreeMap: + private var variance: Int = 1 + + def inContravariantPosition: Boolean = variance == -1 + + inline private def atVariance[T](v: Int)(op: => T): T = { + val saved = variance + variance = v + val res = op + variance = saved + res + } + + override def transform(tree: Tree)(using Context) = tree match + // TODO: handle TypeBoundsTree, LambdaTypeTree as well as method parameters in DefTrees? + case tree @ AppliedTypeTree(tpt, args) => + val args1: List[Tree] = args.zipWithConserve(tpt.tpe.typeParams.map(_.paramVarianceSign)) { (arg, v) => + arg.tpe match { + case _: TypeBounds => transform(arg) + case _ => atVariance(v * variance)(transform(arg)) + } + } + cpy.AppliedTypeTree(tree)(transform(tpt), args1) + case _ => + super.transform(tree) + end TreeMapWithVariance } diff --git a/tests/neg-macros/i16522.check b/tests/neg-macros/i16522.check new file mode 100644 index 000000000000..17eb38f7052d --- /dev/null +++ b/tests/neg-macros/i16522.check @@ -0,0 +1,7 @@ +-- [E007] Type Mismatch Error: tests/neg-macros/i16522.scala:10:45 ----------------------------------------------------- +10 | case '{HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString} // error + | ^^^^^^^ + | Found: tl + | Required: HList + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-macros/i16522.scala b/tests/neg-macros/i16522.scala new file mode 100644 index 000000000000..cbb0bb733ab7 --- /dev/null +++ b/tests/neg-macros/i16522.scala @@ -0,0 +1,16 @@ + import scala.quoted.* + + sealed trait HList + case class HCons[+HD, TL <: HList](hd: HD, tl: TL) extends HList + case object HNil extends HList + + def showFirstTwoImpl(e: Expr[HList])(using Quotes): Expr[String] = { + e match { + case '{HCons($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString} + case '{HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString} // error + case '{HCons[hd, HCons[sd, tl]]($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString} + case _ => '{""} + } + } + + transparent inline def showFirstTwo(inline xs: HList) = ${ showFirstTwoImpl('xs) } diff --git a/tests/neg-macros/i6997b.scala b/tests/neg-macros/i6997b.scala index 0ba517d97582..fe4f0dd638f5 100644 --- a/tests/neg-macros/i6997b.scala +++ b/tests/neg-macros/i6997b.scala @@ -5,7 +5,7 @@ import scala.quoted.* inline def mcr(x: => Any): Any = ${mcrImpl('x)} def mcrImpl(body: Expr[Any])(using ctx: Quotes): Expr[Any] = { - val '{$x: $t} = body // error + val '{$x: $t} = body // error // error '{ val tmp: $t = $x.asInstanceOf[$t] // error // error println(tmp) diff --git a/tests/pos-macros/i14708.scala b/tests/pos-macros/i14708.scala new file mode 100644 index 000000000000..bf8330d34746 --- /dev/null +++ b/tests/pos-macros/i14708.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +object Main { + def foo(a: Expr[Any])(using Quotes) = { + a match { + case '{ ($x: Set[t]).toSet } => + case _ => + } + } +} diff --git a/tests/pos-macros/i16522.scala b/tests/pos-macros/i16522.scala new file mode 100644 index 000000000000..0a94931ec1a7 --- /dev/null +++ b/tests/pos-macros/i16522.scala @@ -0,0 +1,16 @@ + import scala.quoted.* + + sealed trait HList + case class HCons[+HD, TL <: HList](hd: HD, tl: TL) extends HList + case object HNil extends HList + + def showFirstTwoImpl(e: Expr[HList])(using Quotes): Expr[String] = { + e match { + case '{HCons($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString} + case '{type tl <: HList; HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString} // error + case '{HCons[hd, HCons[sd, tl]]($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString} + case _ => '{""} + } + } + + transparent inline def showFirstTwo(inline xs: HList) = ${ showFirstTwoImpl('xs) } diff --git a/tests/pos-macros/i18125.scala b/tests/pos-macros/i18125.scala new file mode 100644 index 000000000000..8c5504cafacc --- /dev/null +++ b/tests/pos-macros/i18125.scala @@ -0,0 +1,10 @@ +import scala.quoted.* + +final class Foo[T](ns: T) + +def foo(using Quotes)(x: Expr[Any]): Unit = + x match + case '{ new Foo($y: b) } => + case '{ new Foo($y: List[b]) } => + case '{ type b; new Foo($y: b) } => + diff --git a/tests/pos-macros/i18125b.scala b/tests/pos-macros/i18125b.scala new file mode 100644 index 000000000000..47e687d0a4a6 --- /dev/null +++ b/tests/pos-macros/i18125b.scala @@ -0,0 +1,23 @@ +package oolong.phobos + +import scala.quoted.* +import scala.compiletime.* +import scala.annotation.StaticAnnotation + +final class xmlns[T](ns: T) extends StaticAnnotation +trait Namespace[T]{ + val getNamespace: String +} + +object common{ + private def extractFeildNamespace(using Quotes)( + fieldAnnotations: List[Expr[Any]], + ): Expr[Option[String]] = { + import quotes.reflect.* + + fieldAnnotations.collect { case '{ xmlns($namespace: b) } => + '{ Some(summonInline[Namespace[b]].getNamespace) } + } + ??? + } +} diff --git a/tests/pos-macros/i18250.scala b/tests/pos-macros/i18250.scala new file mode 100644 index 000000000000..64d34cda85d8 --- /dev/null +++ b/tests/pos-macros/i18250.scala @@ -0,0 +1,6 @@ +import scala.quoted.* + +def test(x: Expr[Any])(using Quotes): Unit = + x match + case '{ type t; type u <: t; () } => + case '{ type t <: Comparable[t]; () } => diff --git a/tests/pos-macros/mirrorQuotePattern.scala b/tests/pos-macros/mirrorQuotePattern.scala new file mode 100644 index 000000000000..1b3b77591339 --- /dev/null +++ b/tests/pos-macros/mirrorQuotePattern.scala @@ -0,0 +1,13 @@ +import scala.deriving._ +import scala.quoted._ + +private def derivedExpr[T](mirrorExpr: Expr[Mirror.Of[T]])(using Quotes, Type[T]): Expr[Any] = { + mirrorExpr match { + case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } => + '{ liftableSum[mirroredElemTypes]($mirrorExpr) } + case '{ type mirroredElemTypes; $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } => + '{ liftableSum[mirroredElemTypes]($mirrorExpr) } + } +} + +def liftableSum[MElemTypes](mirror: Mirror.Sum { type MirroredElemTypes = MElemTypes }): Any = ??? diff --git a/tests/pos-macros/mirrorQuotePattern2.scala b/tests/pos-macros/mirrorQuotePattern2.scala new file mode 100644 index 000000000000..a123cc28907f --- /dev/null +++ b/tests/pos-macros/mirrorQuotePattern2.scala @@ -0,0 +1,10 @@ +import scala.deriving._ +import scala.quoted._ + +private def derivedExpr(x: Expr[Any])(using Quotes): Unit = + x match + case '{ type mtp1; ($m1 : Mirror.Sum { type MirroredElemTypes = mtp1 } & Mirror.Of[Any], $m2 : Mirror.Sum { type MirroredElemTypes = mtp2 } & Mirror.Of[Any]); ??? } => + val _: Expr[Mirror.Sum { type MirroredElemTypes = mtp1 } & Mirror.Of[Any]] = m1 + val _: Expr[Mirror.Sum { type MirroredElemTypes = mtp2 } & Mirror.Of[Any]] = m2 + '{ $m1: Mirror.Sum { type MirroredElemTypes = mtp1 } } + '{ $m2: Mirror.Sum { type MirroredElemTypes = mtp2 } }