diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index aa6cfc2df507..b928b2963857 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -97,26 +97,25 @@ trait QuotesAndSplices { ctx.warning("Canceled quote directly inside a splice. ${ '[ XYZ ] } is equivalent to XYZ.", tree.sourcePos) case _ => } + if (ctx.mode.is(Mode.QuotedPattern) && level == 1) - if (isFullyDefined(pt, ForceDegree.all)) { - ctx.error(i"Spliced type pattern must not be fully defined. Consider using $pt directly", tree.expr.sourcePos) - tree.withType(UnspecifiedErrorType) - } - else { - def spliceOwner(ctx: Context): Symbol = - if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner - val name = tree.expr match { - case Ident(name) => ("$" + name).toTypeName - case expr => - ctx.error("expected a name binding", expr.sourcePos) - "$error".toTypeName - } - val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, TypeBounds.empty, NoSymbol, tree.expr.span) - typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuoted_patternBindHoleAnnot.typeRef)).withSpan(tree.expr.span))) - val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))( - spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) - pat.select(tpnme.splice) + def spliceOwner(ctx: Context): Symbol = + if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner + val name = tree.expr match { + case Ident(name) => ("$" + name).toTypeName + case expr => + ctx.error("expected a name binding", expr.sourcePos) + "$error".toTypeName } + + val typeSymInfo = pt match + case pt: TypeBounds => pt + case _ => TypeBounds.empty + val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span) + typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuoted_patternBindHoleAnnot.typeRef)).withSpan(tree.expr.span))) + val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))( + spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx))) + pat.select(tpnme.splice) else typedSelect(untpd.Select(tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span) } @@ -191,11 +190,13 @@ trait QuotesAndSplices { patBuf += pat1 } case Select(pat, _) if tree.symbol == defn.QuotedType_splice => - val sym = tree.tpe.dealias.typeSymbol.asType - val tdef = TypeDef(sym).withSpan(sym.span) - freshTypeBindingsBuff += transformTypeBindingTypeDef(tdef, freshTypePatBuf) - TypeTree(tree.tpe.dealias).withSpan(tree.span) - + val sym = tree.tpe.dealias.typeSymbol + if sym.exists then + val tdef = TypeDef(sym.asType).withSpan(sym.span) + freshTypeBindingsBuff += transformTypeBindingTypeDef(tdef, freshTypePatBuf) + TypeTree(tree.tpe.dealias).withSpan(tree.span) + else + tree case ddef: ValOrDefDef => if (ddef.symbol.hasAnnotation(defn.InternalQuoted_patternBindHoleAnnot)) { val bindingType = ddef.symbol.info match { @@ -317,13 +318,16 @@ trait QuotesAndSplices { ctx.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span)) val quoted = tree.quoted - val exprPt = pt.baseType(defn.QuotedExprClass) + val exprPt = pt.baseType(if quoted.isType then defn.QuotedTypeClass else defn.QuotedExprClass) val quotedPt = exprPt.argInfos.headOption match { case Some(argPt: ValueType) => argPt // excludes TypeBounds case _ => defn.AnyType } val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt))) - val quoted1 = typedExpr(quoted0, WildcardType)(quoteContext.addMode(Mode.QuotedPattern)) + val quoteCtx = quoteContext.addMode(Mode.QuotedPattern) + val quoted1 = + if quoted.isType then typedType(quoted0, WildcardType)(quoteCtx) + else typedExpr(quoted0, WildcardType)(quoteCtx) val (typeBindings, shape, splices) = splitQuotePattern(quoted1) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index b8e262d2d239..c6bd512ba42b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1389,8 +1389,10 @@ class Typer extends Namer def typedArg(arg: untpd.Tree, tparam: ParamInfo) = { def tparamBounds = tparam.paramInfoAsSeenFrom(tpt1.tpe.appliedTo(tparams.map(_ => TypeBounds.empty))) val (desugaredArg, argPt) = - if (ctx.mode is Mode.Pattern) + if ctx.mode.is(Mode.Pattern) then (if (untpd.isVarPattern(arg)) desugar.patternVar(arg) else arg, tparamBounds) + else if ctx.mode.is(Mode.QuotedPattern) then + (arg, tparamBounds) else (arg, WildcardType) if (tpt1.symbol.isClass) @@ -2177,7 +2179,7 @@ class Typer extends Namer /** Typecheck and adapt tree, returning a typed tree. Parameters as for `typedUnadapted` */ def typed(tree: untpd.Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree = - trace(i"typing $tree", typr, show = true) { + trace(i"typing $tree, pt = $pt", typr, show = true) { record(s"typed $getClass") record("typed total") assertPositioned(tree) @@ -3030,7 +3032,7 @@ class Typer extends Namer tree.tpe.EtaExpand(tp.typeParamSymbols) tree.withType(tp1) } - if ((ctx.mode is Mode.Pattern) || tree1.tpe <:< pt) tree1 + if (ctx.mode.is(Mode.Pattern) || ctx.mode.is(Mode.QuotedPattern) || tree1.tpe <:< pt) tree1 else err.typeMismatch(tree1, pt) } diff --git a/tests/neg/i7264d.scala b/tests/neg/i7264d.scala new file mode 100644 index 000000000000..67954e92b422 --- /dev/null +++ b/tests/neg/i7264d.scala @@ -0,0 +1,7 @@ +import scala.quoted._ +class Foo { + def f[T2: Type](e: Expr[T2])(given QuoteContext) = e match { + case '{ $x: *:[Int, Any] } => // error: Type argument Any does not conform to upper bound Tuple + + } +} diff --git a/tests/pos/i7264.scala b/tests/pos/i7264.scala new file mode 100644 index 000000000000..1d8181ab50a8 --- /dev/null +++ b/tests/pos/i7264.scala @@ -0,0 +1,7 @@ +import scala.quoted._ +class Foo { + def f[T2](t: Type[T2])(given QuoteContext) = t match { + case '[ *:[Int, $t] ] => + '[ *:[Int, $t] ] + } +} \ No newline at end of file diff --git a/tests/pos/i7264b.scala b/tests/pos/i7264b.scala new file mode 100644 index 000000000000..f401f3076b84 --- /dev/null +++ b/tests/pos/i7264b.scala @@ -0,0 +1,7 @@ +import scala.quoted._ +class Foo { + def f[T2: Type](e: Expr[T2])(given QuoteContext) = e match { + case '{ $x: *:[Int, $t] } => + '[ *:[Int, $t] ] + } +} diff --git a/tests/pos/i7264c.scala b/tests/pos/i7264c.scala new file mode 100644 index 000000000000..d6abd0113f03 --- /dev/null +++ b/tests/pos/i7264c.scala @@ -0,0 +1,9 @@ +import scala.quoted._ +class Foo { + def f[T2: Type](e: Expr[T2])(given QuoteContext) = e match { + case '{ $x: $t0 } => + t0 match + case '[ *:[Int, $t] ] => + '[ *:[Int, $t] ] + } +}