Skip to content

Commit de903f4

Browse files
committed
Fix SAM check
1 parent f492867 commit de903f4

File tree

7 files changed

+102
-38
lines changed

7 files changed

+102
-38
lines changed

compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import core._
55
import Contexts._, Symbols._, Types._, Flags._, Decorators._, StdNames._, Constants._
66
import MegaPhase._
77
import SymUtils._
8+
import NullOpsDecorator._
89
import ast.Trees._
910
import reporting._
1011
import dotty.tools.dotc.util.Spans.Span
@@ -45,7 +46,8 @@ class ExpandSAMs extends MiniPhase {
4546
checkRefinements(tpe, fn)
4647
tree
4748
case tpe =>
48-
val tpe1 = checkRefinements(tpe, fn)
49+
val tpe0 = if ctx.explicitNulls then tpe.stripNull else tpe
50+
val tpe1 = checkRefinements(tpe0, fn)
4951
val Seq(samDenot) = tpe1.possibleSamMethods
5052
cpy.Block(tree)(stats,
5153
AnonClass(tpe1 :: Nil, fn.symbol.asTerm :: Nil, samDenot.symbol.asTerm.name :: Nil))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,7 @@ trait Checking {
784784
conv.name == nme.reflectiveSelectable && conv.maybeOwner.maybeOwner.maybeOwner == defn.ScalaPackageClass
785785
if (!conversionOK)
786786
checkFeature(nme.implicitConversions,
787-
i"Use of implicit conversion ${conv.showLocated}", NoSymbol, pos)
787+
i"Use of implicit conversion ${conv.showLocated}", NoSymbol, posd)
788788
}
789789

790790
private def infixOKSinceFollowedBy(tree: untpd.Tree): Boolean = tree match {

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,6 @@ object Nullables:
242242
&& s != refOwner
243243
&& (s.isOneOf(Lazy | Method) // not at the rhs of lazy ValDef or in a method (or lambda)
244244
|| s.isClass // not in a class
245-
// TODO: need to check by-name parameter
246245
|| recur(s.owner))
247246

248247
refSym.is(Mutable) // if it is immutable, we don't need to check the rest conditions

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ object ProtoTypes {
141141

142142
// equals comes from case class; no need to redefine
143143
end IgnoredProto
144-
144+
145145
final class CachedIgnoredProto(ignored: Type) extends IgnoredProto(ignored)
146146

147147
object IgnoredProto:
@@ -364,7 +364,7 @@ object ProtoTypes {
364364
/** Type single argument and remember the unadapted result in `myTypedArg`.
365365
* used to avoid repeated typings of trees when backtracking.
366366
*/
367-
def typedArg(arg: untpd.Tree, formal: Type)(using Context): Tree = {
367+
def typedArg(arg: untpd.Tree, formal: Type)(using Context): Tree = {
368368
val wideFormal = formal.widenExpr
369369
val argCtx =
370370
if wideFormal eq formal then ctx

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

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,26 +1253,35 @@ class Typer extends Namer
12531253
if (tree.tpt.isEmpty)
12541254
meth1.tpe.widen match {
12551255
case mt: MethodType =>
1256-
pt match {
1256+
val pt1 =
1257+
if ctx.explicitNulls then
1258+
pt.stripNull
1259+
else pt
1260+
val adaptWithUnsafeNullConver =
1261+
ctx.explicitNulls && (
1262+
config.Feature.enabled(nme.unsafeNulls) ||
1263+
ctx.mode.is(Mode.UnsafeNullConversion))
1264+
pt1 match {
12571265
case SAMType(sam)
1258-
if !defn.isFunctionType(pt) && mt <:< sam =>
1266+
if !defn.isFunctionType(pt1) && mt <:< sam ||
1267+
adaptWithUnsafeNullConver && mt.isUnsafeConvertable(sam) =>
12591268
// SAMs of the form C[?] where C is a class cannot be conversion targets.
12601269
// The resulting class `class $anon extends C[?] {...}` would be illegal,
12611270
// since type arguments to `C`'s super constructor cannot be constructed.
12621271
def isWildcardClassSAM =
1263-
!pt.classSymbol.is(Trait) && pt.argInfos.exists(_.isInstanceOf[TypeBounds])
1272+
!pt1.classSymbol.is(Trait) && pt1.argInfos.exists(_.isInstanceOf[TypeBounds])
12641273
val targetTpe =
1265-
if isFullyDefined(pt, ForceDegree.all) && !isWildcardClassSAM then
1266-
pt
1267-
else if pt.isRef(defn.PartialFunctionClass) then
1274+
if isFullyDefined(pt1, ForceDegree.all) && !isWildcardClassSAM then
1275+
pt1
1276+
else if pt1.isRef(defn.PartialFunctionClass) then
12681277
// Replace the underspecified expected type by one based on the closure method type
12691278
defn.PartialFunctionOf(mt.firstParamTypes.head, mt.resultType)
12701279
else
1271-
report.error(ex"result type of lambda is an underspecified SAM type $pt", tree.srcPos)
1272-
pt
1273-
if (pt.classSymbol.isOneOf(FinalOrSealed)) {
1274-
val offendingFlag = pt.classSymbol.flags & FinalOrSealed
1275-
report.error(ex"lambda cannot implement $offendingFlag ${pt.classSymbol}", tree.srcPos)
1280+
report.error(ex"result type of lambda is an underspecified SAM type $pt1", tree.srcPos)
1281+
pt1
1282+
if (pt1.classSymbol.isOneOf(FinalOrSealed)) {
1283+
val offendingFlag = pt1.classSymbol.flags & FinalOrSealed
1284+
report.error(ex"lambda cannot implement $offendingFlag ${pt1.classSymbol}", tree.srcPos)
12761285
}
12771286
TypeTree(targetTpe)
12781287
case _ =>
@@ -3434,13 +3443,23 @@ class Typer extends Namer
34343443
return tpd.Block(tree1 :: Nil, Literal(Constant(())))
34353444
}
34363445

3446+
val adaptWithUnsafeNullConver =
3447+
ctx.explicitNulls && (
3448+
config.Feature.enabled(nme.unsafeNulls) ||
3449+
ctx.mode.is(Mode.UnsafeNullConversion))
3450+
34373451
// convert function literal to SAM closure
34383452
tree match {
34393453
case closure(Nil, id @ Ident(nme.ANON_FUN), _)
34403454
if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) =>
3441-
pt match {
3455+
val pt1 =
3456+
if ctx.explicitNulls then
3457+
pt.stripNull
3458+
else pt
3459+
pt1 match {
34423460
case SAMType(sam)
3443-
if wtp <:< sam.toFunctionType() =>
3461+
if wtp <:< sam.toFunctionType() ||
3462+
(adaptWithUnsafeNullConver && wtp.isUnsafeConvertable(sam.toFunctionType())) =>
34443463
// was ... && isFullyDefined(pt, ForceDegree.flipBottom)
34453464
// but this prevents case blocks from implementing polymorphic partial functions,
34463465
// since we do not know the result parameter a priori. Have to wait until the
@@ -3509,12 +3528,11 @@ class Typer extends Namer
35093528
}
35103529
}
35113530

3512-
def tryUnsafeNullConver(fail: => Tree): Tree =
3531+
def tryUnsafeNullConver(fail: => Tree)(using Context): Tree =
35133532
// If explicitNulls and unsafeNulls are enabled, and
3514-
if ctx.explicitNulls && config.Feature.enabled(nme.unsafeNulls) &&
3515-
tree.tpe.isUnsafeConvertable(pt) then {
3516-
tree.cast(pt)
3517-
}
3533+
if ctx.mode.is(Mode.UnsafeNullConversion) && pt.isValueType &&
3534+
tree.tpe.isUnsafeConvertable(pt)
3535+
then tree.cast(pt)
35183536
else fail
35193537

35203538
def cannotFind(failure: SearchFailure) =
@@ -3527,21 +3545,25 @@ class Typer extends Namer
35273545
tree
35283546
else recover(failure.reason)
35293547

3530-
if ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType then
3531-
if pt.isRef(defn.AnyValClass) || pt.isRef(defn.ObjectClass) then
3532-
ctx.error(em"the result of an implicit conversion must be more specific than $pt", tree.sourcePos)
3533-
val searchCtx = if config.Feature.enabled(nme.unsafeNulls)
3534-
then ctx.addMode(Mode.UnsafeNullConversion)
3535-
else ctx
3536-
tree.tpe match {
3537-
case OrNull(tpe1) if ctx.explicitNulls && config.Feature.enabled(nme.unsafeNulls) =>
3538-
searchTree(tree.cast(tpe1)) { _ =>
3539-
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))(using searchCtx)
3540-
}(using searchCtx)
3541-
case _ =>
3542-
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))(using searchCtx)
3543-
}
3544-
else tryUnsafeNullConver(recover(NoMatchingImplicits))
3548+
val searchCtx =
3549+
if ctx.explicitNulls && config.Feature.enabled(nme.unsafeNulls) then
3550+
ctx.addMode(Mode.UnsafeNullConversion)
3551+
else ctx
3552+
3553+
inContext(searchCtx) {
3554+
if ctx.mode.is(Mode.ImplicitsEnabled) && tree.typeOpt.isValueType then
3555+
if pt.isRef(defn.AnyValClass) || pt.isRef(defn.ObjectClass) then
3556+
report.error(em"the result of an implicit conversion must be more specific than $pt", tree.srcPos)
3557+
tree.tpe match {
3558+
case OrNull(tpe1) if ctx.explicitNulls && config.Feature.enabled(nme.unsafeNulls) =>
3559+
searchTree(tree.cast(tpe1)) { _ =>
3560+
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))
3561+
}
3562+
case _ =>
3563+
searchTree(tree)(failure => tryUnsafeNullConver(cannotFind(failure)))
3564+
}
3565+
else tryUnsafeNullConver(recover(NoMatchingImplicits))
3566+
}
35453567
}
35463568

35473569
def adaptType(tp: Type): Tree = {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import java.util.function.*;
2+
3+
@FunctionalInterface
4+
interface SAMJava1 {
5+
public String[] f(String x);
6+
}
7+
8+
@FunctionalInterface
9+
interface SAMJava2 {
10+
public void f(int x);
11+
}
12+
13+
class J {
14+
public void g1(SAMJava1 s) {
15+
}
16+
17+
public void g2(SAMJava2 s) {
18+
}
19+
20+
public void h1(Function<String, String[]> s) {
21+
}
22+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
def m = {
2+
val j: J = ???
3+
4+
def f1(x: String | Null): Array[String | Null] | Null = null
5+
6+
def f2(i: Int): Unit = ()
7+
8+
j.g1(f1)
9+
j.g1((_: String | Null) => null)
10+
j.g1(null)
11+
12+
j.g2(f2)
13+
j.g2((_: Int) => ())
14+
j.g2(null)
15+
16+
j.h1(f1)
17+
j.h1((_: String | Null) => null)
18+
j.h1(null)
19+
}

0 commit comments

Comments
 (0)