Skip to content

Commit b439deb

Browse files
committed
Don't capture wildcards if in closure or by-name
1 parent d99d9bf commit b439deb

File tree

4 files changed

+37
-1
lines changed

4 files changed

+37
-1
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4470,6 +4470,8 @@ object Types {
44704470

44714471
def hasWildcardArg(using Context): Boolean = args.exists(isBounds)
44724472

4473+
def hasCaptureConversionArg(using Context): Boolean = args.exists(_.typeSymbol == defn.TypeBox_CAP)
4474+
44734475
def derivedAppliedType(tycon: Type, args: List[Type])(using Context): Type =
44744476
if ((tycon eq this.tycon) && (args eq this.args)) this
44754477
else tycon.appliedTo(args)

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Decorators._
1313
import Uniques._
1414
import inlines.Inlines
1515
import config.Printers.typr
16+
import ErrorReporting.*
1617
import util.SourceFile
1718
import TypeComparer.necessarySubType
1819

@@ -492,7 +493,14 @@ object ProtoTypes {
492493
val targ = cacheTypedArg(arg,
493494
typer.typedUnadapted(_, wideFormal, locked)(using argCtx),
494495
force = true)
495-
typer.adapt(targ, wideFormal, locked)
496+
val targ1 = typer.adapt(targ, wideFormal, locked)
497+
if wideFormal eq formal then targ1
498+
else targ1.tpe match
499+
case tp: AppliedType if tp.hasCaptureConversionArg =>
500+
errorTree(targ1,
501+
em"""argument for by-name parameter contains capture conversion skolem types:
502+
|$tp""")
503+
case _ => targ1
496504
}
497505

498506
/** The type of the argument `arg`, or `NoType` if `arg` has not been typed before

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
15681568
else if ((tree.tpt `eq` untpd.ContextualEmptyTree) && mt.paramNames.isEmpty)
15691569
// Note implicitness of function in target type since there are no method parameters that indicate it.
15701570
TypeTree(defn.FunctionOf(Nil, mt.resType, isContextual = true, isErased = false))
1571+
else if mt.resType.match { case tp: AppliedType => tp.hasCaptureConversionArg case _ => false } then
1572+
errorTree(tree,
1573+
em"""cannot turn method type $mt into closure
1574+
|because it has capture conversion skolem types""")
15711575
else
15721576
EmptyTree
15731577
}

tests/neg/t9419.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
trait Magic[S]:
2+
def init: S
3+
def step(s: S): String
4+
5+
object IntMagic extends Magic[Int]:
6+
def init = 0
7+
def step(s: Int): String = (s - 1).toString
8+
9+
object StrMagic extends Magic[String]:
10+
def init = "hi"
11+
def step(s: String): String = s.reverse
12+
13+
object Main:
14+
def onestep[T](m: () => Magic[T]): String = m().step(m().init)
15+
def unostep[T](m: => Magic[T]): String = m.step(m.init)
16+
17+
val iter: Iterator[Magic[?]] = Iterator(IntMagic, StrMagic)
18+
19+
// was: class java.lang.String cannot be cast to class java.lang.Integer
20+
def main(args: Array[String]): Unit =
21+
onestep(() => iter.next()) // error
22+
unostep(iter.next()) // error

0 commit comments

Comments
 (0)