Skip to content

Commit b2d3cc8

Browse files
authored
Merge pull request #12048 from dotty-staging/fix-12019
Avoid wildcards when typing default parameters of context function type
2 parents a2ec0d6 + 8986cb7 commit b2d3cc8

File tree

3 files changed

+66
-1
lines changed

3 files changed

+66
-1
lines changed

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1483,6 +1483,30 @@ class Namer { typer: Typer =>
14831483
case _ =>
14841484
NoType
14851485

1486+
/** The expected type for a default argument. This is normally the `defaultParamType`
1487+
* with references to internal parameters replaced by wildcards. This replacement
1488+
* makes it possible that the default argument can have a more specific type than the
1489+
* parameter. For instance, we allow
1490+
*
1491+
* class C[A](a: A) { def copy[B](x: B = a): C[B] = C(x) }
1492+
*
1493+
* However, if the default parameter type is a context function type, we
1494+
* have to make sure that wildcard types do not leak into the implicitly
1495+
* generated closure's result type. Test case is pos/i12019.scala. If there
1496+
* would be a leakage with the wildcard approximation, we pick the original
1497+
* default parameter type as expected type.
1498+
*/
1499+
def expectedDefaultArgType =
1500+
val originalTp = defaultParamType
1501+
val approxTp = wildApprox(originalTp)
1502+
approxTp.stripPoly match
1503+
case atp @ defn.ContextFunctionType(_, resType, _)
1504+
if !defn.isNonRefinedFunction(atp) // in this case `resType` is lying, gives us only the non-dependent upper bound
1505+
|| resType.existsPart(_.isInstanceOf[WildcardType], stopAtStatic = true, forceLazy = false) =>
1506+
originalTp
1507+
case _ =>
1508+
approxTp
1509+
14861510
// println(s"final inherited for $sym: ${inherited.toString}") !!!
14871511
// println(s"owner = ${sym.owner}, decls = ${sym.owner.info.decls.show}")
14881512
// TODO Scala 3.1: only check for inline vals (no final ones)
@@ -1509,7 +1533,7 @@ class Namer { typer: Typer =>
15091533
// expected type but we run it through `wildApprox` to allow default
15101534
// parameters like in `def mkList[T](value: T = 1): List[T]`.
15111535
val defaultTp = defaultParamType
1512-
val pt = inherited.orElse(wildApprox(defaultTp)).orElse(WildcardType).widenExpr
1536+
val pt = inherited.orElse(expectedDefaultArgType).orElse(WildcardType).widenExpr
15131537
val tp = typedAheadRhs(pt).tpe
15141538
if (defaultTp eq pt) && (tp frozen_<:< defaultTp) then
15151539
// When possible, widen to the default getter parameter type to permit a

tests/neg/i12019.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
object test1:
2+
trait A
3+
type B <: AnyRef
4+
val test1: A ?=> B = null // error
5+
val test2: A ?=> B = A ?=> null // error
6+
7+
import scala.quoted.*
8+
9+
object Eg1 {
10+
11+
// no default arg: ok
12+
def ok (f: (q: Quotes) ?=> q.reflect.Term) = ()
13+
14+
// default the function *reference* to null: compilation error
15+
def ko_1(f: (q: Quotes) ?=> q.reflect.Term = null) = () // error
16+
17+
// default the function *result* to null: compilation error
18+
def ko_2(f: (q: Quotes) ?=> q.reflect.Term = (_: Quotes) ?=> null) = () // error
19+
}
20+

tests/pos/i12019.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
trait A:
2+
type X >: Null
3+
4+
def ko1(f: (q: A) ?=> Int => q.X = null) = ()
5+
def ko2(f: (q: A) ?=> Int => q.X = (_: A) ?=> null) = ()
6+
def ko3(f: (q: A) => q.X = (q => null)) = ()
7+
8+
9+
import scala.quoted.*
10+
11+
object Eg2 {
12+
13+
// no default arg: ok
14+
def ok (f: (q: Quotes) ?=> q.reflect.ValDef => q.reflect.Term) = ()
15+
16+
// default the function *reference* to null: crash!
17+
def ko_1(f: (q: Quotes) ?=> q.reflect.ValDef => q.reflect.Term = null) = ()
18+
19+
// default the function *result* to null: crash!
20+
def ko_2(f: (q: Quotes) ?=> q.reflect.ValDef => q.reflect.Term = (_: Quotes) ?=> null) = ()
21+
}

0 commit comments

Comments
 (0)