PatternMatchingTypingVisitor:
separate condition data flow info analysis and exhaustiveness check from type inference for "incomplete" (statement-like) 'when'.
This commit is contained in:
+123
-83
@@ -16,8 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.types.expressions
|
package org.jetbrains.kotlin.types.expressions
|
||||||
|
|
||||||
import com.google.common.collect.Sets
|
import com.google.common.collect.Maps
|
||||||
import com.intellij.openapi.util.Ref
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.isBoolean
|
||||||
import org.jetbrains.kotlin.cfg.WhenChecker
|
import org.jetbrains.kotlin.cfg.WhenChecker
|
||||||
@@ -39,6 +38,7 @@ import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
|
|||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.newWritableScopeImpl
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.newWritableScopeImpl
|
||||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
|
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
|
||||||
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
|
|
||||||
class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) {
|
class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTypingInternals) : ExpressionTypingVisitor(facade) {
|
||||||
|
|
||||||
@@ -65,118 +65,157 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
|
|
||||||
components.dataFlowAnalyzer.recordExpectedType(contextWithExpectedType.trace, expression, contextWithExpectedType.expectedType)
|
components.dataFlowAnalyzer.recordExpectedType(contextWithExpectedType.trace, expression, contextWithExpectedType.expectedType)
|
||||||
|
|
||||||
var context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT)
|
val contextBeforeSubject = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT)
|
||||||
// TODO :change scope according to the bound value in the when header
|
// TODO :change scope according to the bound value in the when header
|
||||||
val subjectExpression = expression.subjectExpression
|
val subjectExpression = expression.subjectExpression
|
||||||
|
|
||||||
|
val contextAfterSubject: ExpressionTypingContext
|
||||||
val subjectType: KotlinType
|
val subjectType: KotlinType
|
||||||
var loopBreakContinuePossible = false
|
val subjectDataFlowValue: DataFlowValue
|
||||||
|
val jumpOutPossible: Boolean
|
||||||
|
|
||||||
if (subjectExpression == null) {
|
if (subjectExpression == null) {
|
||||||
subjectType = ErrorUtils.createErrorType("Unknown type")
|
subjectType = ErrorUtils.createErrorType("Unknown type")
|
||||||
|
subjectDataFlowValue = DataFlowValue.nullValue(components.builtIns)
|
||||||
|
contextAfterSubject = contextBeforeSubject
|
||||||
|
jumpOutPossible = false
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val typeInfo = facade.safeGetTypeInfo(subjectExpression, context)
|
val subjectTypeInfo = facade.safeGetTypeInfo(subjectExpression, contextBeforeSubject)
|
||||||
loopBreakContinuePossible = typeInfo.jumpOutPossible
|
|
||||||
subjectType = typeInfo.type!!
|
contextAfterSubject = contextBeforeSubject.replaceDataFlowInfo(subjectTypeInfo.dataFlowInfo)
|
||||||
if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, context.trace.bindingContext)) {
|
subjectType = subjectTypeInfo.type!!
|
||||||
val trace = TemporaryBindingTrace.create(context.trace, "Temporary trace for when subject nullability")
|
subjectDataFlowValue = DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, contextAfterSubject)
|
||||||
val subjectContext = context.replaceExpectedType(TypeUtils.makeNotNullable(subjectType)).replaceBindingTrace(trace)
|
jumpOutPossible = subjectTypeInfo.jumpOutPossible
|
||||||
|
|
||||||
|
if (TypeUtils.isNullableType(subjectType) && !WhenChecker.containsNullCase(expression, contextBeforeSubject.trace.bindingContext)) {
|
||||||
|
val trace = TemporaryBindingTrace.create(contextBeforeSubject.trace, "Temporary trace for when subject nullability")
|
||||||
|
val subjectContext = contextBeforeSubject.replaceExpectedType(TypeUtils.makeNotNullable(subjectType)).replaceBindingTrace(trace)
|
||||||
val castResult = DataFlowAnalyzer.checkPossibleCast(
|
val castResult = DataFlowAnalyzer.checkPossibleCast(
|
||||||
subjectType, KtPsiUtil.safeDeparenthesize(subjectExpression), subjectContext)
|
subjectType, KtPsiUtil.safeDeparenthesize(subjectExpression), subjectContext)
|
||||||
if (castResult != null && castResult.isCorrect) {
|
if (castResult != null && castResult.isCorrect) {
|
||||||
trace.commit()
|
trace.commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context = context.replaceDataFlowInfo(typeInfo.dataFlowInfo)
|
|
||||||
}
|
}
|
||||||
val subjectDataFlowValue = if (subjectExpression != null)
|
|
||||||
DataFlowValueFactory.createDataFlowValue(subjectExpression, subjectType, context)
|
|
||||||
else
|
|
||||||
DataFlowValue.nullValue(components.builtIns)
|
|
||||||
|
|
||||||
// TODO : exhaustive patterns
|
val dataFlowInfoBeforeEntryBodies = collectDataFlowInfoBeforeEntryBodies(expression, contextAfterSubject, subjectType, subjectDataFlowValue)
|
||||||
|
val isExhaustive = WhenChecker.isWhenExhaustive(expression, contextAfterSubject.trace)
|
||||||
|
|
||||||
val expressionTypes = Sets.newHashSet<KotlinType>()
|
return getTypeInfoForIncompleteWhen(expression, isStatement, isExhaustive, contextWithExpectedType,
|
||||||
var commonDataFlowInfo: DataFlowInfo? = null
|
contextAfterSubject, jumpOutPossible, dataFlowInfoBeforeEntryBodies)
|
||||||
var elseDataFlowInfo = context.dataFlowInfo
|
}
|
||||||
val whenValue = DataFlowValueFactory.createDataFlowValue(expression, components.builtIns.nullableAnyType, context)
|
|
||||||
|
private fun collectDataFlowInfoBeforeEntryBodies(
|
||||||
|
expression: KtWhenExpression,
|
||||||
|
contextAfterSubject: ExpressionTypingContext,
|
||||||
|
subjectType: KotlinType,
|
||||||
|
subjectDataFlowValue: DataFlowValue
|
||||||
|
): Map<KtWhenEntry, DataFlowInfo> {
|
||||||
|
val subjectExpression = expression.subjectExpression
|
||||||
|
val thenInfo = Maps.newHashMapWithExpectedSize<KtWhenEntry, DataFlowInfo>(expression.entries.size)
|
||||||
|
|
||||||
|
var inputDataFlowInfo = contextAfterSubject.dataFlowInfo
|
||||||
for (whenEntry in expression.entries) {
|
for (whenEntry in expression.entries) {
|
||||||
val infosForCondition = getDataFlowInfosForEntryCondition(
|
val conditionsInfo = analyzeWhenEntryConditions(whenEntry,
|
||||||
whenEntry, context.replaceDataFlowInfo(elseDataFlowInfo), subjectExpression, subjectType, subjectDataFlowValue)
|
contextAfterSubject.replaceDataFlowInfo(inputDataFlowInfo),
|
||||||
elseDataFlowInfo = elseDataFlowInfo.and(infosForCondition.elseInfo)
|
subjectExpression, subjectType, subjectDataFlowValue)
|
||||||
|
thenInfo[whenEntry] = conditionsInfo.thenInfo
|
||||||
|
inputDataFlowInfo = inputDataFlowInfo.and(conditionsInfo.elseInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return thenInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTypeInfoForIncompleteWhen(
|
||||||
|
expression: KtWhenExpression,
|
||||||
|
isStatement: Boolean,
|
||||||
|
isExhaustive: Boolean,
|
||||||
|
contextWithExpectedType: ExpressionTypingContext,
|
||||||
|
contextAfterSubject: ExpressionTypingContext,
|
||||||
|
jumpOutPossibleInSubject: Boolean,
|
||||||
|
dataFlowBeforeEntryBody: Map<KtWhenEntry, DataFlowInfo>
|
||||||
|
): KotlinTypeInfo {
|
||||||
|
val coercionStrategy = if (isStatement) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION
|
||||||
|
|
||||||
|
val expressionTypes = hashSetOf<KotlinType>()
|
||||||
|
var commonDataFlowInfo: DataFlowInfo? = null
|
||||||
|
var jumpOutPossible = jumpOutPossibleInSubject
|
||||||
|
val whenValue = DataFlowValueFactory.createDataFlowValue(expression, components.builtIns.nullableAnyType, contextAfterSubject)
|
||||||
|
|
||||||
|
for (whenEntry in expression.entries) {
|
||||||
|
val ifTrueInfo = dataFlowBeforeEntryBody[whenEntry]!!
|
||||||
|
|
||||||
val bodyExpression = whenEntry.expression
|
val bodyExpression = whenEntry.expression
|
||||||
if (bodyExpression != null) {
|
if (bodyExpression != null) {
|
||||||
val scopeToExtend = newWritableScopeImpl(context, LexicalScopeKind.WHEN)
|
val scopeToExtend = newWritableScopeImpl(contextAfterSubject, LexicalScopeKind.WHEN)
|
||||||
val newContext = contextWithExpectedType.replaceScope(scopeToExtend).replaceDataFlowInfo(infosForCondition.thenInfo).replaceContextDependency(INDEPENDENT)
|
val contextForEntry = contextWithExpectedType.replaceScope(scopeToExtend).replaceDataFlowInfo(ifTrueInfo).replaceContextDependency(INDEPENDENT)
|
||||||
val coercionStrategy = if (isStatement) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION
|
val entryTypeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
|
||||||
var typeInfo = components.expressionTypingServices.getBlockReturnedTypeWithWritableScope(
|
scopeToExtend, listOf(bodyExpression), coercionStrategy, contextForEntry)
|
||||||
scopeToExtend, listOf(bodyExpression), coercionStrategy, newContext)
|
|
||||||
loopBreakContinuePossible = loopBreakContinuePossible or typeInfo.jumpOutPossible
|
jumpOutPossible = jumpOutPossible or entryTypeInfo.jumpOutPossible
|
||||||
val type = typeInfo.type
|
|
||||||
if (type != null) {
|
val entryType = entryTypeInfo.type
|
||||||
expressionTypes.add(type)
|
|
||||||
val entryValue = DataFlowValueFactory.createDataFlowValue(bodyExpression, type, context)
|
expressionTypes.addIfNotNull(entryType)
|
||||||
typeInfo = typeInfo.replaceDataFlowInfo(typeInfo.dataFlowInfo.assign(whenValue, entryValue))
|
|
||||||
}
|
val entryDataFlowInfo = if (entryType != null) {
|
||||||
if (commonDataFlowInfo == null) {
|
val entryValue = DataFlowValueFactory.createDataFlowValue(bodyExpression, entryType, contextAfterSubject)
|
||||||
commonDataFlowInfo = typeInfo.dataFlowInfo
|
entryTypeInfo.dataFlowInfo.assign(whenValue, entryValue)
|
||||||
}
|
|
||||||
else {
|
|
||||||
commonDataFlowInfo = commonDataFlowInfo.or(typeInfo.dataFlowInfo)
|
|
||||||
}
|
}
|
||||||
|
else entryTypeInfo.dataFlowInfo
|
||||||
|
|
||||||
|
commonDataFlowInfo = commonDataFlowInfo?.or(entryDataFlowInfo) ?: entryDataFlowInfo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val isExhaustive = WhenChecker.isWhenExhaustive(expression, context.trace)
|
|
||||||
if (commonDataFlowInfo == null) {
|
if (commonDataFlowInfo == null) {
|
||||||
commonDataFlowInfo = context.dataFlowInfo
|
commonDataFlowInfo = contextAfterSubject.dataFlowInfo
|
||||||
}
|
}
|
||||||
else if (expression.elseExpression == null && !isExhaustive) {
|
else if (expression.elseExpression == null && !isExhaustive) {
|
||||||
// Without else expression in non-exhaustive when, we *must* take initial data flow info into account,
|
// Without else expression in non-exhaustive when, we *must* take initial data flow info into account,
|
||||||
// because data flow can bypass all when branches in this case
|
// because data flow can bypass all when branches in this case
|
||||||
commonDataFlowInfo = commonDataFlowInfo.or(context.dataFlowInfo)
|
commonDataFlowInfo = commonDataFlowInfo.or(contextAfterSubject.dataFlowInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
var resultType: KotlinType? = if (expressionTypes.isEmpty()) null else CommonSupertypes.commonSupertype(expressionTypes)
|
var resultType: KotlinType? = if (expressionTypes.isNotEmpty()) {
|
||||||
if (resultType != null) {
|
val commonSupertype = CommonSupertypes.commonSupertype(expressionTypes)
|
||||||
val resultValue = DataFlowValueFactory.createDataFlowValue(expression, resultType, context)
|
val resultValue = DataFlowValueFactory.createDataFlowValue(expression, commonSupertype, contextAfterSubject)
|
||||||
commonDataFlowInfo = commonDataFlowInfo.assign(resultValue, whenValue)
|
commonDataFlowInfo = commonDataFlowInfo.assign(resultValue, whenValue)
|
||||||
if (isExhaustive && expression.elseExpression == null && KotlinBuiltIns.isNothing(resultType)) {
|
if (isExhaustive && expression.elseExpression == null && KotlinBuiltIns.isNothing(commonSupertype)) {
|
||||||
context.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression)
|
contextAfterSubject.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression)
|
||||||
}
|
}
|
||||||
resultType = components.dataFlowAnalyzer.checkType(resultType, expression, contextWithExpectedType)
|
components.dataFlowAnalyzer.checkType(commonSupertype, expression, contextWithExpectedType)
|
||||||
}
|
}
|
||||||
return createTypeInfo(resultType,
|
else null
|
||||||
commonDataFlowInfo,
|
|
||||||
loopBreakContinuePossible,
|
return createTypeInfo(resultType, commonDataFlowInfo, jumpOutPossible, contextWithExpectedType.dataFlowInfo)
|
||||||
contextWithExpectedType.dataFlowInfo)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getDataFlowInfosForEntryCondition(
|
private fun analyzeWhenEntryConditions(
|
||||||
whenEntry: KtWhenEntry,
|
whenEntry: KtWhenEntry,
|
||||||
context: ExpressionTypingContext,
|
context: ExpressionTypingContext,
|
||||||
subjectExpression: KtExpression?,
|
subjectExpression: KtExpression?,
|
||||||
subjectType: KotlinType,
|
subjectType: KotlinType,
|
||||||
subjectDataFlowValue: DataFlowValue): DataFlowInfos {
|
subjectDataFlowValue: DataFlowValue
|
||||||
|
): ConditionalDataFlowInfo {
|
||||||
if (whenEntry.isElse) {
|
if (whenEntry.isElse) {
|
||||||
return DataFlowInfos(context.dataFlowInfo)
|
return ConditionalDataFlowInfo(context.dataFlowInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
var infos: DataFlowInfos? = null
|
var entryInfo: ConditionalDataFlowInfo? = null
|
||||||
var contextForCondition = context
|
var contextForCondition = context
|
||||||
for (condition in whenEntry.conditions) {
|
for (condition in whenEntry.conditions) {
|
||||||
val conditionInfos = checkWhenCondition(subjectExpression, subjectType, condition,
|
val conditionInfo = checkWhenCondition(subjectExpression, subjectType, condition,
|
||||||
contextForCondition, subjectDataFlowValue)
|
contextForCondition, subjectDataFlowValue)
|
||||||
if (infos != null) {
|
entryInfo = entryInfo?.let {
|
||||||
infos = DataFlowInfos(infos.thenInfo.or(conditionInfos.thenInfo), infos.elseInfo.and(conditionInfos.elseInfo))
|
ConditionalDataFlowInfo(it.thenInfo.or(conditionInfo.thenInfo), it.elseInfo.and(conditionInfo.elseInfo))
|
||||||
}
|
} ?: conditionInfo
|
||||||
else {
|
|
||||||
infos = conditionInfos
|
contextForCondition = contextForCondition.replaceDataFlowInfo(conditionInfo.elseInfo)
|
||||||
}
|
|
||||||
contextForCondition = contextForCondition.replaceDataFlowInfo(conditionInfos.elseInfo)
|
|
||||||
}
|
}
|
||||||
return if (infos != null) infos else DataFlowInfos(context.dataFlowInfo)
|
|
||||||
|
return entryInfo ?: ConditionalDataFlowInfo(context.dataFlowInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkWhenCondition(
|
private fun checkWhenCondition(
|
||||||
@@ -184,22 +223,23 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
subjectType: KotlinType,
|
subjectType: KotlinType,
|
||||||
condition: KtWhenCondition,
|
condition: KtWhenCondition,
|
||||||
context: ExpressionTypingContext,
|
context: ExpressionTypingContext,
|
||||||
subjectDataFlowValue: DataFlowValue): DataFlowInfos {
|
subjectDataFlowValue: DataFlowValue
|
||||||
val newDataFlowInfo = Ref(noChange(context))
|
): ConditionalDataFlowInfo {
|
||||||
|
var newDataFlowInfo = noChange(context)
|
||||||
condition.accept(object : KtVisitorVoid() {
|
condition.accept(object : KtVisitorVoid() {
|
||||||
override fun visitWhenConditionInRange(condition: KtWhenConditionInRange) {
|
override fun visitWhenConditionInRange(condition: KtWhenConditionInRange) {
|
||||||
val rangeExpression = condition.rangeExpression ?: return
|
val rangeExpression = condition.rangeExpression ?: return
|
||||||
if (subjectExpression == null) {
|
if (subjectExpression == null) {
|
||||||
context.trace.report(EXPECTED_CONDITION.on(condition))
|
context.trace.report(EXPECTED_CONDITION.on(condition))
|
||||||
val dataFlowInfo = facade.getTypeInfo(rangeExpression, context).dataFlowInfo
|
val dataFlowInfo = facade.getTypeInfo(rangeExpression, context).dataFlowInfo
|
||||||
newDataFlowInfo.set(DataFlowInfos(dataFlowInfo, dataFlowInfo))
|
newDataFlowInfo = ConditionalDataFlowInfo(dataFlowInfo, dataFlowInfo)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val argumentForSubject = CallMaker.makeExternalValueArgument(subjectExpression)
|
val argumentForSubject = CallMaker.makeExternalValueArgument(subjectExpression)
|
||||||
val typeInfo = facade.checkInExpression(condition, condition.operationReference,
|
val typeInfo = facade.checkInExpression(condition, condition.operationReference,
|
||||||
argumentForSubject, rangeExpression, context)
|
argumentForSubject, rangeExpression, context)
|
||||||
val dataFlowInfo = typeInfo.dataFlowInfo
|
val dataFlowInfo = typeInfo.dataFlowInfo
|
||||||
newDataFlowInfo.set(DataFlowInfos(dataFlowInfo, dataFlowInfo))
|
newDataFlowInfo = ConditionalDataFlowInfo(dataFlowInfo, dataFlowInfo)
|
||||||
val type = typeInfo.type
|
val type = typeInfo.type
|
||||||
if (type == null || !isBoolean(type)) {
|
if (type == null || !isBoolean(type)) {
|
||||||
context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition))
|
context.trace.report(TYPE_MISMATCH_IN_RANGE.on(condition))
|
||||||
@@ -213,10 +253,10 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
if (condition.typeReference != null) {
|
if (condition.typeReference != null) {
|
||||||
val result = checkTypeForIs(context, subjectType, condition.typeReference, subjectDataFlowValue)
|
val result = checkTypeForIs(context, subjectType, condition.typeReference, subjectDataFlowValue)
|
||||||
if (condition.isNegated) {
|
if (condition.isNegated) {
|
||||||
newDataFlowInfo.set(DataFlowInfos(result.elseInfo, result.thenInfo))
|
newDataFlowInfo = ConditionalDataFlowInfo(result.elseInfo, result.thenInfo)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
newDataFlowInfo.set(result)
|
newDataFlowInfo = result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,8 +264,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) {
|
override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) {
|
||||||
val expression = condition.expression
|
val expression = condition.expression
|
||||||
if (expression != null) {
|
if (expression != null) {
|
||||||
newDataFlowInfo.set(checkTypeForExpressionCondition(context, expression, subjectType, subjectExpression == null,
|
newDataFlowInfo = checkTypeForExpressionCondition(context, expression, subjectType, subjectExpression == null,
|
||||||
subjectDataFlowValue))
|
subjectDataFlowValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,10 +273,10 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
context.trace.report(UNSUPPORTED.on(element, javaClass.canonicalName))
|
context.trace.report(UNSUPPORTED.on(element, javaClass.canonicalName))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return newDataFlowInfo.get()
|
return newDataFlowInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DataFlowInfos(val thenInfo: DataFlowInfo, val elseInfo: DataFlowInfo = thenInfo)
|
private class ConditionalDataFlowInfo(val thenInfo: DataFlowInfo, val elseInfo: DataFlowInfo = thenInfo)
|
||||||
|
|
||||||
private fun checkTypeForExpressionCondition(
|
private fun checkTypeForExpressionCondition(
|
||||||
context: ExpressionTypingContext,
|
context: ExpressionTypingContext,
|
||||||
@@ -244,7 +284,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
subjectType: KotlinType,
|
subjectType: KotlinType,
|
||||||
conditionExpected: Boolean,
|
conditionExpected: Boolean,
|
||||||
subjectDataFlowValue: DataFlowValue
|
subjectDataFlowValue: DataFlowValue
|
||||||
): DataFlowInfos {
|
): ConditionalDataFlowInfo {
|
||||||
var newContext = context
|
var newContext = context
|
||||||
if (expression == null) {
|
if (expression == null) {
|
||||||
return noChange(newContext)
|
return noChange(newContext)
|
||||||
@@ -260,14 +300,14 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
else {
|
else {
|
||||||
val ifInfo = components.dataFlowAnalyzer.extractDataFlowInfoFromCondition(expression, true, newContext)
|
val ifInfo = components.dataFlowAnalyzer.extractDataFlowInfoFromCondition(expression, true, newContext)
|
||||||
val elseInfo = components.dataFlowAnalyzer.extractDataFlowInfoFromCondition(expression, false, newContext)
|
val elseInfo = components.dataFlowAnalyzer.extractDataFlowInfoFromCondition(expression, false, newContext)
|
||||||
return DataFlowInfos(ifInfo, elseInfo)
|
return ConditionalDataFlowInfo(ifInfo, elseInfo)
|
||||||
}
|
}
|
||||||
return noChange(newContext)
|
return noChange(newContext)
|
||||||
}
|
}
|
||||||
checkTypeCompatibility(newContext, type, subjectType, expression)
|
checkTypeCompatibility(newContext, type, subjectType, expression)
|
||||||
val expressionDataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, newContext)
|
val expressionDataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, newContext)
|
||||||
var result = noChange(newContext)
|
var result = noChange(newContext)
|
||||||
result = DataFlowInfos(
|
result = ConditionalDataFlowInfo(
|
||||||
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
|
result.thenInfo.equate(subjectDataFlowValue, expressionDataFlowValue),
|
||||||
result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue))
|
result.elseInfo.disequate(subjectDataFlowValue, expressionDataFlowValue))
|
||||||
return result
|
return result
|
||||||
@@ -278,7 +318,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
subjectType: KotlinType,
|
subjectType: KotlinType,
|
||||||
typeReferenceAfterIs: KtTypeReference?,
|
typeReferenceAfterIs: KtTypeReference?,
|
||||||
subjectDataFlowValue: DataFlowValue
|
subjectDataFlowValue: DataFlowValue
|
||||||
): DataFlowInfos {
|
): ConditionalDataFlowInfo {
|
||||||
if (typeReferenceAfterIs == null) {
|
if (typeReferenceAfterIs == null) {
|
||||||
return noChange(context)
|
return noChange(context)
|
||||||
}
|
}
|
||||||
@@ -303,10 +343,10 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
|
|||||||
if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, KotlinTypeChecker.DEFAULT)) {
|
if (CastDiagnosticsUtil.isCastErased(subjectType, targetType, KotlinTypeChecker.DEFAULT)) {
|
||||||
context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType))
|
context.trace.report(Errors.CANNOT_CHECK_FOR_ERASED.on(typeReferenceAfterIs, targetType))
|
||||||
}
|
}
|
||||||
return DataFlowInfos(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, targetType), context.dataFlowInfo)
|
return ConditionalDataFlowInfo(context.dataFlowInfo.establishSubtyping(subjectDataFlowValue, targetType), context.dataFlowInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun noChange(context: ExpressionTypingContext) = DataFlowInfos(context.dataFlowInfo, context.dataFlowInfo)
|
private fun noChange(context: ExpressionTypingContext) = ConditionalDataFlowInfo(context.dataFlowInfo, context.dataFlowInfo)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (a: SubjectType) is Type
|
* (a: SubjectType) is Type
|
||||||
|
|||||||
Reference in New Issue
Block a user