Skip to content

Fix #7264: Propagate prototypes in quoted type patterns #7600

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Dec 9, 2019
Merged
54 changes: 29 additions & 25 deletions compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)

Expand Down
8 changes: 5 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
}

Expand Down
7 changes: 7 additions & 0 deletions tests/neg/i7264d.scala
Original file line number Diff line number Diff line change
@@ -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

}
}
7 changes: 7 additions & 0 deletions tests/pos/i7264.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import scala.quoted._
class Foo {
def f[T2](t: Type[T2])(given QuoteContext) = t match {
case '[ *:[Int, $t] ] =>
'[ *:[Int, $t] ]
}
}
7 changes: 7 additions & 0 deletions tests/pos/i7264b.scala
Original file line number Diff line number Diff line change
@@ -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] ]
}
}
9 changes: 9 additions & 0 deletions tests/pos/i7264c.scala
Original file line number Diff line number Diff line change
@@ -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] ]
}
}