Skip to content

Commit dd81033

Browse files
committed
Fix approximation direction based on variance
1 parent 77d28da commit dd81033

File tree

8 files changed

+77
-6
lines changed

8 files changed

+77
-6
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,8 @@ class Definitions {
650650
@tu lazy val InternalQuoted_patternHole: Symbol = InternalQuotedModule.requiredMethod("patternHole")
651651
@tu lazy val InternalQuoted_patternBindHoleAnnot: ClassSymbol = InternalQuotedModule.requiredClass("patternBindHole")
652652
@tu lazy val InternalQuoted_QuoteTypeTagAnnot: ClassSymbol = InternalQuotedModule.requiredClass("quoteTypeTag")
653+
@tu lazy val InternalQuoted_formAboveAnnot: ClassSymbol = InternalQuotedModule.requiredClass("formAbove")
654+
653655

654656
@tu lazy val InternalQuotedExprModule: Symbol = ctx.requiredModule("scala.internal.quoted.Expr")
655657
@tu lazy val InternalQuotedExpr_unapply: Symbol = InternalQuotedExprModule.requiredMethod(nme.unapply)

compiler/src/dotty/tools/dotc/tastyreflect/ReflectionCompilerInterface.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,6 +1867,7 @@ class ReflectionCompilerInterface(val rootContext: core.Contexts.Context) extend
18671867

18681868
def Definitions_InternalQuoted_patternHole: Symbol = defn.InternalQuoted_patternHole
18691869
def Definitions_InternalQuoted_patternBindHoleAnnot: Symbol = defn.InternalQuoted_patternBindHoleAnnot
1870+
def Definitions_InternalQuoted_formAboveAnnot: Symbol = defn.InternalQuoted_formAboveAnnot
18701871

18711872
// Types
18721873

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ trait QuotesAndSplices { self: Typer =>
163163
})
164164

165165
object splitter extends tpd.TreeMap {
166+
private var variance: Int = 1
167+
168+
@forceInline private def atVariance[T](v: Int)(op: => T): T = {
169+
val saved = variance
170+
variance = v
171+
val res = op
172+
variance = saved
173+
res
174+
}
175+
166176
val patBuf = new mutable.ListBuffer[Tree]
167177
val freshTypePatBuf = new mutable.ListBuffer[Tree]
168178
val freshTypeBindingsBuff = new mutable.ListBuffer[Tree]
@@ -206,11 +216,21 @@ trait QuotesAndSplices { self: Typer =>
206216
super.transform(tree)
207217
case tdef: TypeDef if tdef.symbol.hasAnnotation(defn.InternalQuoted_patternBindHoleAnnot) =>
208218
transformTypeBindingTypeDef(tdef, typePatBuf)
219+
case tree @ AppliedTypeTree(tpt, args) =>
220+
val args1: List[Tree] = args.zipWithConserve(tpt.tpe.typeParams.map(_.paramVariance)) { (arg, v) =>
221+
arg.tpe match {
222+
case _: TypeBounds => transform(arg)
223+
case _ => atVariance(variance * v)(transform(arg))
224+
}
225+
}
226+
cpy.AppliedTypeTree(tree)(transform(tpt), args1)
209227
case _ =>
210228
super.transform(tree)
211229
}
212230

213-
def transformTypeBindingTypeDef(tdef: TypeDef, buff: mutable.Builder[Tree, List[Tree]]): Tree = {
231+
private def transformTypeBindingTypeDef(tdef: TypeDef, buff: mutable.Builder[Tree, List[Tree]])(implicit ctx: Context): Tree = {
232+
if (variance == -1)
233+
tdef.symbol.addAnnotation(Annotation(New(ref(defn.InternalQuoted_formAboveAnnot.typeRef)).withSpan(tdef.span)))
214234
val bindingType = getBinding(tdef.symbol).symbol.typeRef
215235
val bindingTypeTpe = AppliedType(defn.QuotedTypeClass.typeRef, bindingType :: Nil)
216236
assert(tdef.name.startsWith("$"))

library/src/scala/internal/Quoted.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ object Quoted {
2828
/** A splice of a name in a quoted pattern is that marks the definition of a type splice */
2929
class patternType extends Annotation
3030

31+
/** A type pattern that must be aproximated from above */
32+
class formAbove extends Annotation
33+
3134
/** Artifact of pickled type splices
3235
*
3336
* During quote reification a quote `'{ ... F[$t] ... }` will be transformed into

library/src/scala/internal/quoted/Matcher.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ private[quoted] object Matcher {
1919

2020
inline private def withEnv[T](env: Env)(body: => given Env => T): T = body given env
2121

22-
class SymBinding(val sym: Symbol)
22+
class SymBinding(val sym: Symbol, val fromAbove: Boolean)
2323

2424
def termMatch(scrutineeTerm: Term, patternTerm: Term, hasTypeSplices: Boolean): Option[Tuple] = {
2525
implicit val env: Env = Set.empty
@@ -30,7 +30,7 @@ private[quoted] object Matcher {
3030
// that we have found and seal them in a quoted.Type
3131
matchings.asOptionOfTuple.map { tup =>
3232
Tuple.fromArray(tup.toArray.map { // TODO improve performace
33-
case x: SymBinding => internal.Context_GADT_approximation(the[Context])(x.sym, true).seal
33+
case x: SymBinding => internal.Context_GADT_approximation(the[Context])(x.sym, !x.fromAbove).seal
3434
case x => x
3535
})
3636
}
@@ -50,7 +50,7 @@ private[quoted] object Matcher {
5050
// that we have found and seal them in a quoted.Type
5151
matchings.asOptionOfTuple.map { tup =>
5252
Tuple.fromArray(tup.toArray.map { // TODO improve performace
53-
case x: SymBinding => internal.Context_GADT_approximation(the[Context])(x.sym, true).seal
53+
case x: SymBinding => internal.Context_GADT_approximation(the[Context])(x.sym, !x.fromAbove).seal
5454
case x => x
5555
})
5656
}
@@ -67,11 +67,18 @@ private[quoted] object Matcher {
6767

6868
private def hasBindAnnotation(sym: Symbol) = sym.annots.exists(isBindAnnotation)
6969

70+
private def hasFromAboveAnnotation(sym: Symbol) = sym.annots.exists(isFromAboveAnnotation)
71+
7072
private def isBindAnnotation(tree: Tree): Boolean = tree match {
7173
case New(tpt) => tpt.symbol == internal.Definitions_InternalQuoted_patternBindHoleAnnot
7274
case annot => annot.symbol.owner == internal.Definitions_InternalQuoted_patternBindHoleAnnot
7375
}
7476

77+
private def isFromAboveAnnotation(tree: Tree): Boolean = tree match {
78+
case New(tpt) => tpt.symbol == internal.Definitions_InternalQuoted_formAboveAnnot
79+
case annot => annot.symbol.owner == internal.Definitions_InternalQuoted_formAboveAnnot
80+
}
81+
7582
/** Check that all trees match with `mtch` and concatenate the results with && */
7683
private def matchLists[T](l1: List[T], l2: List[T])(mtch: (T, T) => Matching): Matching = (l1, l2) match {
7784
case (x :: xs, y :: ys) => mtch(x, y) && matchLists(xs, ys)(mtch)
@@ -160,7 +167,7 @@ private[quoted] object Matcher {
160167

161168
case (Block(stats1, expr1), Block(binding :: stats2, expr2)) if isTypeBinding(binding) =>
162169
qctx.tasty.internal.Context_GADT_addToConstraint(the[Context])(binding.symbol :: Nil)
163-
matched(new SymBinding(binding.symbol)) && Block(stats1, expr1) =#= Block(stats2, expr2)
170+
matched(new SymBinding(binding.symbol, hasFromAboveAnnotation(binding.symbol))) && Block(stats1, expr1) =#= Block(stats2, expr2)
164171

165172
case (Block(stat1 :: stats1, expr1), Block(stat2 :: stats2, expr2)) =>
166173
withEnv(the[Env] + (stat1.symbol -> stat2.symbol)) {
@@ -170,7 +177,7 @@ private[quoted] object Matcher {
170177
case (scrutinee, Block(typeBindings, expr2)) if typeBindings.forall(isTypeBinding) =>
171178
val bindingSymbols = typeBindings.map(_.symbol)
172179
qctx.tasty.internal.Context_GADT_addToConstraint(the[Context])(bindingSymbols)
173-
bindingSymbols.foldRight(scrutinee =#= expr2)((x, acc) => matched(new SymBinding(x)) && acc)
180+
bindingSymbols.foldRight(scrutinee =#= expr2)((x, acc) => matched(new SymBinding(x, hasFromAboveAnnotation(x))) && acc)
174181

175182
case (If(cond1, thenp1, elsep1), If(cond2, thenp2, elsep2)) =>
176183
cond1 =#= cond2 && thenp1 =#= thenp2 && elsep1 =#= elsep2

library/src/scala/tasty/reflect/CompilerInterface.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,9 @@ trait CompilerInterface {
15171517
/** Symbol of scala.internal.Quoted.patternBindHole */
15181518
def Definitions_InternalQuoted_patternBindHoleAnnot: Symbol
15191519

1520+
/** Symbol of scala.internal.Quoted.formAbove */
1521+
def Definitions_InternalQuoted_formAboveAnnot: Symbol
1522+
15201523
def Definitions_UnitType: Type
15211524
def Definitions_ByteType: Type
15221525
def Definitions_ShortType: Type
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
scala.Int
2+
3+
scala.Int
4+
scala.Double
5+
6+
scala.Int
7+
scala.Short
8+
scala.Double
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import scala.quoted._
2+
import scala.reflect.ClassTag
3+
4+
object Test {
5+
implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(this.getClass.getClassLoader)
6+
def main(args: Array[String]): Unit = withQuoteContext {
7+
val '[List[Int]] = '[List[Int]]
8+
9+
val '[List[$int]] = '[List[Int]]
10+
println(int.show)
11+
println()
12+
13+
{
14+
val '[Function1[$t1, $r]] = '[Int => Double]
15+
println(t1.show)
16+
println(r.show)
17+
println()
18+
}
19+
20+
{
21+
val '[Function1[Function1[$t1, $r0], $r]] = '[(Int => Short) => Double]
22+
println(t1.show)
23+
println(r0.show)
24+
println(r.show)
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)