Skip to content

Commit f65d1b1

Browse files
committed
Detect uncaptured used of type variables
1 parent efc0f32 commit f65d1b1

File tree

3 files changed

+28
-35
lines changed

3 files changed

+28
-35
lines changed

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

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -268,18 +268,6 @@ sealed trait GadtState {
268268
finally if !result then restore(saved)
269269
result
270270

271-
def unifySyms(params1: List[Symbol], params2: List[Symbol])(using Context) =
272-
addToConstraint(params1)
273-
addToConstraint(params2)
274-
val paramrefs1 = params1 map (gadt.tvarOrError(_))
275-
val paramrefs2 = params2 map (gadt.tvarOrError(_))
276-
for ((p1, p2) <- paramrefs1.zip(paramrefs2))
277-
do
278-
println(s"unifySyms: adding constr ${p1.show} <:< ${p2.show}")
279-
addLess(p1.origin, p2.origin)
280-
println(s"unifySyms: adding constr ${p2.show} <:< ${p1.show}")
281-
addLess(p2.origin, p1.origin)
282-
283271
// ---- Protected/internal -----------------------------------------------
284272

285273
override protected def constraint = gadt.constraint

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ trait QuotesAndSplices {
171171
val splice1 = typedSplicePattern(splice, defn.FunctionOf(argTypes, pt))
172172
untpd.cpy.Apply(tree)(splice1.select(nme.apply), typedArgs).withType(pt)
173173
else // $x(...) higher-order quasipattern
174-
// TODO-18271: Case for highr-order quasi-quote pattern with type params
175174
if args.isEmpty then
176175
report.error("Missing arguments for open pattern", tree.srcPos)
177176
typedSplicePattern(untpd.cpy.SplicePattern(tree)(splice.body, Nil, args), pt)
@@ -406,7 +405,6 @@ object QuotesAndSplices {
406405
val resultType1 = resultType.subst(fromSymbols, pt.paramRefs)
407406
MethodType(args1, resultType1)
408407
}
409-
val tpe = PolyType(typeargs1)(_ => bounds, resultTypeExp)
410-
RefinedType(defn.PolyFunctionType, nme.apply, tpe)
408+
defn.PolyFunctionOf(PolyType(typeargs1)(_ => bounds, resultTypeExp))
411409
}
412410
}

compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -244,14 +244,14 @@ class QuoteMatcher(debug: Boolean) {
244244
if patternHole.symbol.eq(defn.QuotedRuntimePatterns_patternHole) &&
245245
tpt2.tpe.derivesFrom(defn.RepeatedParamClass) =>
246246
scrutinee match
247-
case Typed(s, tpt1) if s.tpe <:< tpt.tpe => matched(scrutinee)
247+
case Typed(s, tpt1) if isSubTypeUnderEnv(s, tpt) => matched(scrutinee)
248248
case _ => notMatched
249249

250250
/* Term hole */
251251
// Match a scala.internal.Quoted.patternHole and return the scrutinee tree
252252
case TypeApply(patternHole, tpt :: Nil)
253253
if patternHole.symbol.eq(defn.QuotedRuntimePatterns_patternHole) &&
254-
scrutinee.tpe <:< tpt.tpe =>
254+
isSubTypeUnderEnv(scrutinee, tpt) =>
255255
scrutinee match
256256
case ClosedPatternTerm(scrutinee) => matched(scrutinee)
257257
case _ => notMatched
@@ -360,7 +360,7 @@ class QuoteMatcher(debug: Boolean) {
360360
/* Match reference */
361361
case _: Ident if symbolMatch(scrutinee, pattern) => matched
362362
/* Match type */
363-
case TypeTreeTypeTest(pattern) if scrutinee.tpe <:< pattern.tpe => matched
363+
case TypeTreeTypeTest(pattern) if isSubTypeUnderEnv(scrutinee, pattern) => matched
364364
case _ => notMatched
365365

366366
/* Match application */
@@ -439,7 +439,7 @@ class QuoteMatcher(debug: Boolean) {
439439
// TODO remove this?
440440
case TypeTreeTypeTest(scrutinee) =>
441441
pattern match
442-
case TypeTreeTypeTest(pattern) if scrutinee.tpe <:< pattern.tpe => matched
442+
case TypeTreeTypeTest(pattern) if isSubTypeUnderEnv(scrutinee, pattern) => matched
443443
case _ => notMatched
444444

445445
/* Match val */
@@ -476,8 +476,13 @@ class QuoteMatcher(debug: Boolean) {
476476
case (scparams :: screst, ptparams :: ptrest) =>
477477
(scparams, ptparams) match
478478
case (TypeDefs(scparams), TypeDefs(ptparams)) =>
479-
matchTypeParams(scparams, ptparams)
480-
matchParamss(screst, ptrest)
479+
scparams.foreach(tdef => println(s"tdef.rhs = ${tdef.rhs.show}"))
480+
if scparams.exists(tdef => tdef.rhs.isEmpty) then
481+
notMatched
482+
483+
val newEnv = summon[Env] ++ scparams.map(_.symbol).zip(ptparams.map(_.symbol))
484+
val (resEnv, mrrest) = withEnv(newEnv)(matchParamss(screst, ptrest))
485+
(resEnv, mrrest)
481486
case (ValDefs(scparams), ValDefs(ptparams)) =>
482487
val mr1 = matchLists(scparams, ptparams)(_ =?= _)
483488
val newEnv = summon[Env] ++ scparams.map(_.symbol).zip(ptparams.map(_.symbol))
@@ -569,20 +574,32 @@ class QuoteMatcher(debug: Boolean) {
569574
|| summon[Env].get(devirtualizedScrutinee).contains(pattern)
570575
|| devirtualizedScrutinee.allOverriddenSymbols.contains(pattern)
571576

577+
private def isSubTypeUnderEnv(scrutinee: Tree, pattern: Tree)(using Env, Context): Boolean =
578+
val env = summon[Env]
579+
scrutinee.subst(env.keys.toList, env.values.toList).tpe <:< pattern.tpe
580+
572581
private object ClosedPatternTerm {
573582
/** Matches a term that does not contain free variables defined in the pattern (i.e. not defined in `Env`) */
574583
def unapply(term: Tree)(using Env, Context): Option[term.type] =
575584
if freePatternVars(term).isEmpty then Some(term) else None
576585

577586
/** Return all free variables of the term defined in the pattern (i.e. defined in `Env`) */
578587
def freePatternVars(term: Tree)(using Env, Context): Set[Symbol] =
579-
val accumulator = new TreeAccumulator[Set[Symbol]] {
588+
val typeAccumulator = new TypeAccumulator[Set[Symbol]] {
589+
def apply(x: Set[Symbol], tp: Type): Set[Symbol] =
590+
if summon[Env].contains(tp.typeSymbol) then
591+
foldOver(x + tp.typeSymbol, tp)
592+
else
593+
foldOver(x, tp)
594+
}
595+
val treeAccumulator = new TreeAccumulator[Set[Symbol]] {
580596
def apply(x: Set[Symbol], tree: Tree)(using Context): Set[Symbol] =
597+
val tvars = typeAccumulator(Set.empty, tree.tpe)
581598
tree match
582-
case tree: Ident if summon[Env].contains(tree.symbol) => foldOver(x + tree.symbol, tree)
583-
case _ => foldOver(x, tree)
599+
case tree: Ident if summon[Env].contains(tree.symbol) => foldOver(x ++ tvars + tree.symbol, tree)
600+
case _ => foldOver(x ++ tvars, tree)
584601
}
585-
accumulator.apply(Set.empty, term)
602+
treeAccumulator(Set.empty, term)
586603
}
587604

588605
enum MatchResult:
@@ -685,16 +702,6 @@ class QuoteMatcher(debug: Boolean) {
685702
private def matchedOpen(tree: Tree, patternTpe: Type, argIds: List[Tree], argTypes: List[Type], typeArgs: List[Type], env: Env)(using Context): MatchingExprs =
686703
Seq(MatchResult.OpenTree(tree, patternTpe, argIds, argTypes, typeArgs, env))
687704

688-
// private def unifySyms(params1: List[Symbol], params2: List[Symbol])(using Context) =
689-
// ctx.gadtState.addToConstraint(params1)
690-
// ctx.gadtState.addToConstraint(params2)
691-
// val paramrefs1 = params1 map (ctx.gadt.tvarOrError(_))
692-
// val paramrefs2 = params2 map (ctx.gadt.tvarOrError(_))
693-
// for ((p1, p2) <- paramrefs1.zip(paramrefs2))
694-
// do
695-
// p1 <:< p2
696-
// p2 <:< p1
697-
698705
extension (self: MatchingExprs)
699706
/** Concatenates the contents of two successful matchings */
700707
def &&& (that: MatchingExprs): MatchingExprs = self ++ that

0 commit comments

Comments
 (0)