Skip to content

Commit bc75262

Browse files
authored
Merge pull request #7600 from dotty-staging/fix-#7264a
Fix #7264: Propagate prototypes in quoted type patterns
2 parents 1fb73f9 + de3fa6a commit bc75262

File tree

6 files changed

+64
-28
lines changed

6 files changed

+64
-28
lines changed

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

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -97,26 +97,25 @@ trait QuotesAndSplices {
9797
ctx.warning("Canceled quote directly inside a splice. ${ '[ XYZ ] } is equivalent to XYZ.", tree.sourcePos)
9898
case _ =>
9999
}
100+
100101
if (ctx.mode.is(Mode.QuotedPattern) && level == 1)
101-
if (isFullyDefined(pt, ForceDegree.all)) {
102-
ctx.error(i"Spliced type pattern must not be fully defined. Consider using $pt directly", tree.expr.sourcePos)
103-
tree.withType(UnspecifiedErrorType)
104-
}
105-
else {
106-
def spliceOwner(ctx: Context): Symbol =
107-
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
108-
val name = tree.expr match {
109-
case Ident(name) => ("$" + name).toTypeName
110-
case expr =>
111-
ctx.error("expected a name binding", expr.sourcePos)
112-
"$error".toTypeName
113-
}
114-
val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, TypeBounds.empty, NoSymbol, tree.expr.span)
115-
typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuoted_patternBindHoleAnnot.typeRef)).withSpan(tree.expr.span)))
116-
val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
117-
spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
118-
pat.select(tpnme.splice)
102+
def spliceOwner(ctx: Context): Symbol =
103+
if (ctx.mode.is(Mode.QuotedPattern)) spliceOwner(ctx.outer) else ctx.owner
104+
val name = tree.expr match {
105+
case Ident(name) => ("$" + name).toTypeName
106+
case expr =>
107+
ctx.error("expected a name binding", expr.sourcePos)
108+
"$error".toTypeName
119109
}
110+
111+
val typeSymInfo = pt match
112+
case pt: TypeBounds => pt
113+
case _ => TypeBounds.empty
114+
val typeSym = ctx.newSymbol(spliceOwner(ctx), name, EmptyFlags, typeSymInfo, NoSymbol, tree.expr.span)
115+
typeSym.addAnnotation(Annotation(New(ref(defn.InternalQuoted_patternBindHoleAnnot.typeRef)).withSpan(tree.expr.span)))
116+
val pat = typedPattern(tree.expr, defn.QuotedTypeClass.typeRef.appliedTo(typeSym.typeRef))(
117+
spliceContext.retractMode(Mode.QuotedPattern).withOwner(spliceOwner(ctx)))
118+
pat.select(tpnme.splice)
120119
else
121120
typedSelect(untpd.Select(tree.expr, tpnme.splice), pt)(spliceContext).withSpan(tree.span)
122121
}
@@ -191,11 +190,13 @@ trait QuotesAndSplices {
191190
patBuf += pat1
192191
}
193192
case Select(pat, _) if tree.symbol == defn.QuotedType_splice =>
194-
val sym = tree.tpe.dealias.typeSymbol.asType
195-
val tdef = TypeDef(sym).withSpan(sym.span)
196-
freshTypeBindingsBuff += transformTypeBindingTypeDef(tdef, freshTypePatBuf)
197-
TypeTree(tree.tpe.dealias).withSpan(tree.span)
198-
193+
val sym = tree.tpe.dealias.typeSymbol
194+
if sym.exists then
195+
val tdef = TypeDef(sym.asType).withSpan(sym.span)
196+
freshTypeBindingsBuff += transformTypeBindingTypeDef(tdef, freshTypePatBuf)
197+
TypeTree(tree.tpe.dealias).withSpan(tree.span)
198+
else
199+
tree
199200
case ddef: ValOrDefDef =>
200201
if (ddef.symbol.hasAnnotation(defn.InternalQuoted_patternBindHoleAnnot)) {
201202
val bindingType = ddef.symbol.info match {
@@ -317,13 +318,16 @@ trait QuotesAndSplices {
317318
ctx.error(missingArgMsg(qctx, defn.QuoteContextClass.typeRef, ""), ctx.source.atSpan(tree.span))
318319

319320
val quoted = tree.quoted
320-
val exprPt = pt.baseType(defn.QuotedExprClass)
321+
val exprPt = pt.baseType(if quoted.isType then defn.QuotedTypeClass else defn.QuotedExprClass)
321322
val quotedPt = exprPt.argInfos.headOption match {
322323
case Some(argPt: ValueType) => argPt // excludes TypeBounds
323324
case _ => defn.AnyType
324325
}
325326
val quoted0 = desugar.quotedPattern(quoted, untpd.TypedSplice(TypeTree(quotedPt)))
326-
val quoted1 = typedExpr(quoted0, WildcardType)(quoteContext.addMode(Mode.QuotedPattern))
327+
val quoteCtx = quoteContext.addMode(Mode.QuotedPattern)
328+
val quoted1 =
329+
if quoted.isType then typedType(quoted0, WildcardType)(quoteCtx)
330+
else typedExpr(quoted0, WildcardType)(quoteCtx)
327331

328332
val (typeBindings, shape, splices) = splitQuotePattern(quoted1)
329333

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1389,8 +1389,10 @@ class Typer extends Namer
13891389
def typedArg(arg: untpd.Tree, tparam: ParamInfo) = {
13901390
def tparamBounds = tparam.paramInfoAsSeenFrom(tpt1.tpe.appliedTo(tparams.map(_ => TypeBounds.empty)))
13911391
val (desugaredArg, argPt) =
1392-
if (ctx.mode is Mode.Pattern)
1392+
if ctx.mode.is(Mode.Pattern) then
13931393
(if (untpd.isVarPattern(arg)) desugar.patternVar(arg) else arg, tparamBounds)
1394+
else if ctx.mode.is(Mode.QuotedPattern) then
1395+
(arg, tparamBounds)
13941396
else
13951397
(arg, WildcardType)
13961398
if (tpt1.symbol.isClass)
@@ -2177,7 +2179,7 @@ class Typer extends Namer
21772179

21782180
/** Typecheck and adapt tree, returning a typed tree. Parameters as for `typedUnadapted` */
21792181
def typed(tree: untpd.Tree, pt: Type, locked: TypeVars)(implicit ctx: Context): Tree =
2180-
trace(i"typing $tree", typr, show = true) {
2182+
trace(i"typing $tree, pt = $pt", typr, show = true) {
21812183
record(s"typed $getClass")
21822184
record("typed total")
21832185
assertPositioned(tree)
@@ -3030,7 +3032,7 @@ class Typer extends Namer
30303032
tree.tpe.EtaExpand(tp.typeParamSymbols)
30313033
tree.withType(tp1)
30323034
}
3033-
if ((ctx.mode is Mode.Pattern) || tree1.tpe <:< pt) tree1
3035+
if (ctx.mode.is(Mode.Pattern) || ctx.mode.is(Mode.QuotedPattern) || tree1.tpe <:< pt) tree1
30343036
else err.typeMismatch(tree1, pt)
30353037
}
30363038

tests/neg/i7264d.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import scala.quoted._
2+
class Foo {
3+
def f[T2: Type](e: Expr[T2])(given QuoteContext) = e match {
4+
case '{ $x: *:[Int, Any] } => // error: Type argument Any does not conform to upper bound Tuple
5+
6+
}
7+
}

tests/pos/i7264.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import scala.quoted._
2+
class Foo {
3+
def f[T2](t: Type[T2])(given QuoteContext) = t match {
4+
case '[ *:[Int, $t] ] =>
5+
'[ *:[Int, $t] ]
6+
}
7+
}

tests/pos/i7264b.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import scala.quoted._
2+
class Foo {
3+
def f[T2: Type](e: Expr[T2])(given QuoteContext) = e match {
4+
case '{ $x: *:[Int, $t] } =>
5+
'[ *:[Int, $t] ]
6+
}
7+
}

tests/pos/i7264c.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import scala.quoted._
2+
class Foo {
3+
def f[T2: Type](e: Expr[T2])(given QuoteContext) = e match {
4+
case '{ $x: $t0 } =>
5+
t0 match
6+
case '[ *:[Int, $t] ] =>
7+
'[ *:[Int, $t] ]
8+
}
9+
}

0 commit comments

Comments
 (0)