Skip to content

Commit

Permalink
Fix guarding & converting SAM result type to a function type
Browse files Browse the repository at this point in the history
In decomposeProtoFunction I wanted to reuse WithFunctionType by running
toFunctionType on the whole SAM and then dropping the first parameters.
But with result dependent functions that results in a refined type,
which is composed of:
  * the function with a result that is a non-depedent approximation
  * the original, non-function, method type

So I went back to calling toFunctionType on the method result type
specifically, like it used to be.

Which means I stopped using the SAMType.WithFunctionType extract, so I
merged it with its remaining usage with subtype checking and called the
SAMType.isSamCompatible.
  • Loading branch information
dwijnand committed Jun 6, 2023
1 parent 9880cea commit f641a87
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 24 deletions.
17 changes: 8 additions & 9 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5569,16 +5569,15 @@ object Types {
}
else None

object WithFunctionType:
def unapply(tp: Type)(using Context): Option[(MethodType, Type)] = tp match
case SAMType(mt) if !isParamDependentRec(mt) =>
Some((mt, mt.toFunctionType(isJava = tp.classSymbol.is(JavaDefined))))
case _ => None
def isSamCompatible(lhs: Type, rhs: Type)(using Context): Boolean = rhs match
case SAMType(mt) if !isParamDependentRec(mt) =>
lhs <:< mt.toFunctionType(isJava = rhs.classSymbol.is(JavaDefined))
case _ => false

private def isParamDependentRec(mt: MethodType)(using Context): Boolean =
mt.isParamDependent || mt.resultType.match
case mt: MethodType => isParamDependentRec(mt)
case _ => false
def isParamDependentRec(mt: MethodType)(using Context): Boolean =
mt.isParamDependent || mt.resultType.match
case mt: MethodType => isParamDependentRec(mt)
case _ => false
}

// ----- TypeMaps --------------------------------------------------------------------
Expand Down
4 changes: 1 addition & 3 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -695,9 +695,7 @@ trait Applications extends Compatibility {
val argtpe1 = argtpe.widen

def SAMargOK =
defn.isFunctionType(argtpe1) && formal.match
case SAMType.WithFunctionType(_, fntpe) => argtpe <:< fntpe
case _ => false
defn.isFunctionType(argtpe1) && SAMType.isSamCompatible(argtpe, formal)

isCompatible(argtpe, formal)
// Only allow SAM-conversion to PartialFunction if implicit conversions
Expand Down
22 changes: 10 additions & 12 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1335,7 +1335,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
case RefinedType(parent, nme.apply, mt @ MethodTpe(_, formals, restpe))
if (defn.isNonRefinedFunction(parent) || defn.isErasedFunctionType(parent)) && formals.length == defaultArity =>
(formals, untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef))))
case SAMType.WithFunctionType(mt @ MethodTpe(_, formals, _), defn.FunctionOf(_, restpe, _)) =>
case pt1 @ SAMType(mt @ MethodTpe(_, formals, _)) if !SAMType.isParamDependentRec(mt) =>
val restpe = mt.resultType match
case mt: MethodType => mt.toFunctionType(isJava = pt1.classSymbol.is(JavaDefined))
case tp => tp
(formals,
if (mt.isResultDependent)
untpd.DependentTypeTree(syms => restpe.substParams(mt, syms.map(_.termRef)))
Expand Down Expand Up @@ -4126,17 +4129,12 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
// convert function literal to SAM closure
tree match {
case closure(Nil, id @ Ident(nme.ANON_FUN), _)
if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) =>
pt match {
case SAMType.WithFunctionType(_, fntpe)
if wtp <:< fntpe =>
// was ... && isFullyDefined(pt, ForceDegree.flipBottom)
// but this prevents case blocks from implementing polymorphic partial functions,
// since we do not know the result parameter a priori. Have to wait until the
// body is typechecked.
return toSAM(tree)
case _ =>
}
if defn.isFunctionType(wtp) && !defn.isFunctionType(pt) && SAMType.isSamCompatible(wtp, pt) =>
// was ... && isFullyDefined(pt, ForceDegree.flipBottom)
// but this prevents case blocks from implementing polymorphic partial functions,
// since we do not know the result parameter a priori. Have to wait until the
// body is typechecked.
return toSAM(tree)
case _ =>
}

Expand Down

0 comments on commit f641a87

Please sign in to comment.