in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/psi/api/InferUtil.scala [332:513]
def updateAccordingToExpectedType(
_nonValueType: ScType,
filterTypeParams: Boolean,
expectedType: Option[ScType],
expr: PsiElement,
canThrowSCE: Boolean,
shouldTruncateMethodType: Boolean = true
): ScType = {
implicit val projectContext: ProjectContext = expr
implicit val context: Context = Context(expr)
val Unit = projectContext.stdTypes.Unit
val shouldTruncateImplicitParameters = expectedType match {
case Some(ContextFunctionType(_, _)) => false
case _ => true
}
val ptUnwrapped = expectedType match {
case Some(ContextFunctionType(retTpe, _)) => retTpe.toOption
case other => other
}
@tailrec
def shouldSearchImplicit(t: ScType, ptConstraints: ConstraintSystem, first: Boolean = true): Boolean = t match {
case ScMethodType(_, params, isImplicit) if isImplicit =>
!first && // implicit method type on top level means explicit implicit argument
params.forall(p => p.paramType.subtypeExists {
case tpt: TypeParameterType => ptConstraints.isApplicable(tpt.typeParamId)
case _ => false
})
case ScTypePolymorphicType(internalType, _) => shouldSearchImplicit(internalType, ptConstraints, first = first)
case ScMethodType(returnType, _, _) => shouldSearchImplicit(returnType, ptConstraints, first = false)
case _ => false
}
def implicitSearchFails(tp: ScType): Boolean = expr match {
case e: ScExpression =>
val appliedClauses = e.updatedWithImplicitArguments(tp, checkExpectedType = false, updateDeep = true)._2
appliedClauses.exists {
_.args.exists {
case srr if srr.isNotFoundImplicitParameter => true
case srr if srr.isAmbiguousImplicitParameter =>
// we found several implicits, but not all type parameters are fully inferred yet, it may be fine
tp.asOptionOf[ScTypePolymorphicType].exists(_.typeParameters.isEmpty)
case _ => false
}
}
case _ => false
}
def cantFindImplicitsFor(tp: ScType, ptConstraints: ConstraintSystem): Boolean =
shouldSearchImplicit(tp, ptConstraints) && implicitSearchFails(tp)
def doLocalTypeInference(tpt: ScTypePolymorphicType, expected: ScType): ScType = {
val ScTypePolymorphicType(internal, typeParams) = tpt
val sameDepth = internal match {
case m: ScMethodType =>
truncateMethodType(
m,
expr,
shouldTruncateImplicitParameters,
shouldTruncateMethodType
)
case _ => internal
}
val valueType = sameDepth.inferValueType
val expectedParam = Parameter("", None, expected, expected)
val expressionToUpdate = Expression(ScSubstitutor.bind(typeParams)(UndefinedType(_)).apply(valueType))
val (inferredWithExpected, conformanceResult) =
localTypeInferenceWithApplicabilityExt(
internal,
Seq(expectedParam),
Seq(expressionToUpdate),
typeParams,
shouldUndefineParameters = false,
canThrowSCE = canThrowSCE,
filterTypeParams = filterTypeParams
)
val subst =
if (!filterTypeParams) {
val fullyInferedTypeParameters =
inferredWithExpected
.typeParameters
.filter(p => p.lowerType.equiv(p.upperType))
ScSubstitutor.bind(fullyInferedTypeParameters)(_.lowerType)
} else ScSubstitutor.empty
val result = subst(inferredWithExpected)
/** See
* [[scala.tools.nsc.typechecker.Typers.Typer.adapt#adaptToImplicitMethod]]
*
* If there is not found implicit for type parameters inferred using expected type,
* rollback type inference, it may be fixed later with implicit conversion
*/
if (cantFindImplicitsFor(result, conformanceResult.constraints)) _nonValueType
else result
}
val nonValueType = (_nonValueType, ptUnwrapped) match {
case (tpt: ScTypePolymorphicType, Some(expected)) if !expected.equiv(Unit) =>
doLocalTypeInference(tpt, expected)
case _ =>
_nonValueType
}
if (!expr.is[ScExpression])
return nonValueType
// interim fix for SCL-3905.
def applyImplicitViewToResult(
mt: ScMethodType,
expectedType: Option[ScType],
fromSAM: Boolean = false,
fromMethodInvocation: Boolean = false
): ScMethodType = {
implicit val elementScope: ElementScope = mt.elementScope
val ScMethodType(result, params, _) = mt
expr match {
case _: MethodInvocation if !fromMethodInvocation =>
result match {
case methodType: ScMethodType =>
val resultNew = applyImplicitViewToResult(methodType, expectedType, fromSAM, fromMethodInvocation = true)
mt.copy(result = resultNew)
case _ => mt
}
case _ =>
expectedType match {
case Some(expected) if result.conforms(expected) => mt
case Some(FunctionType(expectedRet, expectedParams)) if expectedParams.length == params.length =>
if (expectedRet.equiv(Unit)) { //value discarding
mt.copy(result = Unit)
}
else {
result match {
case methodType: ScMethodType =>
val resultNew = applyImplicitViewToResult(methodType, Some(expectedRet), fromSAM)
return mt.copy(result = resultNew)
case _ =>
}
import literals.ScNullLiteral
val nullLiteral = ScalaPsiElementFactory.createExpressionWithContextFromText(
"null",
expr.getContext,
expr
).asInstanceOf[ScNullLiteral]
ScNullLiteral(nullLiteral) = result
val updatedResultType = nullLiteral.getTypeAfterImplicitConversion(expectedOption = Some(expectedRet))
expr.asInstanceOf[ScExpression].setAdditionalExpression(Some(nullLiteral, expectedRet))
mt.copy(result = updatedResultType.tr.getOrElse(result))
}
case _ => mt
}
}
}
nonValueType match {
case tpt@ScTypePolymorphicType(mt: ScMethodType, _) =>
val canConform = if (!filterTypeParams) {
val subst = tpt.abstractTypeSubstitutor
val withAbstracts = subst(mt).asInstanceOf[ScMethodType]
truncateMethodType(withAbstracts, expr, shouldTruncateImplicitParameters, shouldTruncateMethodType)
} else truncateMethodType(mt, expr, shouldTruncateImplicitParameters, shouldTruncateMethodType)
if (ptUnwrapped.forall(canConform.conforms)) tpt
else tpt.copy(internalType = applyImplicitViewToResult(mt, ptUnwrapped))
case mt: ScMethodType =>
applyImplicitViewToResult(mt, ptUnwrapped)
case t => t
}
}