diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index 164d17abf0c..bb9860361d5 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -32,6 +32,7 @@ fun resolveArgumentExpression( expectedTypeRef: FirTypeRef, sink: CheckerSink, isReceiver: Boolean, + isSafeCall: Boolean, acceptLambdaAtoms: (PostponedResolvedAtomMarker) -> Unit, typeProvider: (FirExpression) -> FirTypeRef? ) { @@ -42,6 +43,7 @@ fun resolveArgumentExpression( expectedType, sink, isReceiver, + isSafeCall, typeProvider ) // TODO:! @@ -57,6 +59,7 @@ fun resolveArgumentExpression( expectedTypeRef, sink, isReceiver, + isSafeCall, acceptLambdaAtoms, typeProvider ) @@ -67,10 +70,11 @@ fun resolveArgumentExpression( expectedTypeRef, sink, isReceiver, + isSafeCall, acceptLambdaAtoms, typeProvider ) - else -> resolvePlainExpressionArgument(csBuilder, argument, expectedType, sink, isReceiver, typeProvider) + else -> resolvePlainExpressionArgument(csBuilder, argument, expectedType, sink, isReceiver, isSafeCall, typeProvider) } } @@ -80,11 +84,12 @@ fun resolvePlainExpressionArgument( expectedType: ConeKotlinType?, sink: CheckerSink, isReceiver: Boolean, + isSafeCall: Boolean, typeProvider: (FirExpression) -> FirTypeRef? ) { if (expectedType == null) return val argumentType = typeProvider(argument)?.coneTypeSafe() ?: return - resolvePlainArgumentType(csBuilder, argumentType, expectedType, sink, isReceiver) + resolvePlainArgumentType(csBuilder, argumentType, expectedType, sink, isReceiver, isSafeCall) } fun resolvePlainArgumentType( @@ -92,12 +97,20 @@ fun resolvePlainArgumentType( argumentType: ConeKotlinType, expectedType: ConeKotlinType, sink: CheckerSink, - isReceiver: Boolean + isReceiver: Boolean, + isSafeCall: Boolean ) { val position = SimpleConstraintSystemConstraintPosition //TODO + val nullableExpectedType = expectedType.withNullability(ConeNullability.NULLABLE) + if (isReceiver && isSafeCall) { + if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, nullableExpectedType, position)) { + sink.reportApplicability(CandidateApplicability.WRONG_RECEIVER) // TODO + } + return + } + if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) { - val nullableExpectedType = expectedType.withNullability(ConeNullability.NULLABLE) if (!isReceiver) { if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, nullableExpectedType, position)) { csBuilder.addSubtypeConstraint(argumentType, expectedType, position) @@ -119,6 +132,7 @@ internal fun Candidate.resolveArgument( argument: FirExpression, parameter: FirValueParameter, isReceiver: Boolean, + isSafeCall: Boolean, typeProvider: (FirExpression) -> FirTypeRef?, sink: CheckerSink ) { @@ -131,6 +145,7 @@ internal fun Candidate.resolveArgument( parameter.returnTypeRef, sink, isReceiver, + isSafeCall, { this.postponedAtoms += it }, typeProvider ) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt index 4cfe8a78223..1abf7aeddf7 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallResolver.kt @@ -36,6 +36,7 @@ class CallInfo( val explicitReceiver: FirExpression?, val arguments: List, + val isSafeCall: Boolean, val typeArguments: List, val session: FirSession, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt index 9d0f24f30fe..78243fb809a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArgumentsAnalyzer.kt @@ -109,6 +109,7 @@ class PostponedArgumentsAnalyzer( lambda.atom.returnTypeRef, // TODO: proper ref checkerSink, false, + false, { atom = it }, typeProvider ) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt index 266fe49e9f1..f5fa7abfc35 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolverParts.kt @@ -103,10 +103,15 @@ internal sealed class CheckReceivers : ResolutionStage() { if (receiverParameterValue != null) { if (explicitReceiverExpression != null && explicitReceiverKind.shouldBeResolvedAsExplicit()) { resolveArgumentExpression( - candidate.csBuilder, explicitReceiverExpression, + candidate.csBuilder, + explicitReceiverExpression, candidate.substitutor.substituteOrSelf(receiverParameterValue.type), explicitReceiverExpression.typeRef, - sink, isReceiver = true, typeProvider = callInfo.typeProvider, acceptLambdaAtoms = { candidate.postponedAtoms += it } + sink, + isReceiver = true, + isSafeCall = callInfo.isSafeCall, + typeProvider = callInfo.typeProvider, + acceptLambdaAtoms = { candidate.postponedAtoms += it } ) } } @@ -132,7 +137,14 @@ internal object CheckArguments : CheckerStage() { val argumentMapping = candidate.argumentMapping ?: throw IllegalStateException("Argument should be already mapped while checking arguments!") for ((argument, parameter) in argumentMapping) { - candidate.resolveArgument(argument, parameter, isReceiver = false, typeProvider = callInfo.typeProvider, sink = sink) + candidate.resolveArgument( + argument, + parameter, + isReceiver = false, + isSafeCall = false, + typeProvider = callInfo.typeProvider, + sink = sink + ) } if (candidate.system.hasContradiction) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt index c6fab60d0bc..0160e0a0d9c 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirBodyResolveTransformer.kt @@ -248,7 +248,16 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn val receiver = qualifiedAccess.explicitReceiver?.transformSingle(this, noExpectedType) - val info = CallInfo(CallKind.VariableAccess, receiver, emptyList(), emptyList(), session, file, container!!) { it.resultType } + val info = CallInfo( + CallKind.VariableAccess, + receiver, + emptyList(), + qualifiedAccess.safe, + emptyList(), + session, + file, + container!! + ) { it.resultType } val resolver = CallResolver(jump, inferenceComponents) resolver.callInfo = info resolver.scopes = (scopes + localScopes).asReversed() @@ -358,7 +367,16 @@ open class FirBodyResolveTransformer(val session: FirSession, val implicitTypeOn val arguments = functionCall.arguments val typeArguments = functionCall.typeArguments - val info = CallInfo(CallKind.Function, explicitReceiver, arguments, typeArguments, session, file, container!!) { it.resultType } + val info = CallInfo( + CallKind.Function, + explicitReceiver, + arguments, + functionCall.safe, + typeArguments, + session, + file, + container!! + ) { it.resultType } val resolver = CallResolver(jump, inferenceComponents) resolver.callInfo = info resolver.scopes = (scopes + localScopes).asReversed() diff --git a/compiler/testData/ir/irText/expressions/bangbang.fir.txt b/compiler/testData/ir/irText/expressions/bangbang.fir.txt index 2711ddf53f8..fa1e4ff69a8 100644 --- a/compiler/testData/ir/irText/expressions/bangbang.fir.txt +++ b/compiler/testData/ir/irText/expressions/bangbang.fir.txt @@ -16,21 +16,21 @@ FILE fqName: fileName:/bangbang.kt BRANCH if: CONST Boolean type=kotlin.Boolean value=true then: GET_VAR 'val : kotlin.Any? [val] declared in .test1' type=kotlin.Any? origin=null - FUN name:test2 visibility:public modality:FINAL <> (a:kotlin.Any?) returnType:IrErrorType + FUN name:test2 visibility:public modality:FINAL <> (a:kotlin.Any?) returnType:kotlin.Int VALUE_PARAMETER name:a index:0 type:kotlin.Any? BLOCK_BODY - RETURN type=kotlin.Nothing from='public final fun test2 (a: kotlin.Any?): IrErrorType declared in ' - BLOCK type=IrErrorType origin=EXCLEXCL - VAR name: type:IrErrorType [val] - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - WHEN type=IrErrorType origin=EXCLEXCL + RETURN type=kotlin.Nothing from='public final fun test2 (a: kotlin.Any?): kotlin.Int declared in ' + BLOCK type=kotlin.Int origin=EXCLEXCL + VAR name: type:kotlin.Int [val] + CALL 'public open fun hashCode (): kotlin.Int declared in kotlin.Any' type=kotlin.Int origin=null + $this: GET_VAR 'a: kotlin.Any? declared in .test2' type=kotlin.Any? origin=null + WHEN type=kotlin.Int origin=EXCLEXCL BRANCH if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ - arg0: GET_VAR 'val : IrErrorType [val] declared in .test2' type=IrErrorType origin=null + arg0: GET_VAR 'val : kotlin.Int [val] declared in .test2' type=kotlin.Int origin=null arg1: CONST Null type=kotlin.Nothing? value=null then: THROW type=kotlin.Nothing ERROR_CALL 'Unresolved reference: #' type=IrErrorType BRANCH if: CONST Boolean type=kotlin.Boolean value=true - then: GET_VAR 'val : IrErrorType [val] declared in .test2' type=IrErrorType origin=null - + then: GET_VAR 'val : kotlin.Int [val] declared in .test2' type=kotlin.Int origin=null diff --git a/compiler/testData/ir/irText/expressions/chainOfSafeCalls.fir.txt b/compiler/testData/ir/irText/expressions/chainOfSafeCalls.fir.txt index 15b3c854e8b..0f1034a20ed 100644 --- a/compiler/testData/ir/irText/expressions/chainOfSafeCalls.fir.txt +++ b/compiler/testData/ir/irText/expressions/chainOfSafeCalls.fir.txt @@ -28,9 +28,12 @@ FILE fqName: fileName:/chainOfSafeCalls.kt overridden: public open fun toString (): kotlin.String declared in kotlin.Any $this: VALUE_PARAMETER name: type:kotlin.Any - FUN name:test visibility:public modality:FINAL <> (nc:.C?) returnType:IrErrorType + FUN name:test visibility:public modality:FINAL <> (nc:.C?) returnType:.C VALUE_PARAMETER name:nc index:0 type:.C? BLOCK_BODY - RETURN type=kotlin.Nothing from='public final fun test (nc: .C?): IrErrorType declared in ' - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - + RETURN type=kotlin.Nothing from='public final fun test (nc: .C?): .C declared in ' + CALL 'public final fun foo (): .C declared in .C' type=.C origin=null + $this: CALL 'public final fun foo (): .C declared in .C' type=.C origin=null + $this: CALL 'public final fun bar (): .C? declared in .C' type=.C? origin=null + $this: CALL 'public final fun foo (): .C declared in .C' type=.C origin=null + $this: GET_VAR 'nc: .C? declared in .test' type=.C? origin=null diff --git a/compiler/testData/ir/irText/expressions/kt30020.fir.txt b/compiler/testData/ir/irText/expressions/kt30020.fir.txt index 0145be535f2..685763a3d8b 100644 --- a/compiler/testData/ir/irText/expressions/kt30020.fir.txt +++ b/compiler/testData/ir/irText/expressions/kt30020.fir.txt @@ -55,20 +55,21 @@ FILE fqName: fileName:/kt30020.kt if: CONST Boolean type=kotlin.Boolean value=true then: GET_VAR 'val : kotlin.collections.MutableList [val] declared in .test' type=kotlin.collections.MutableList origin=null ERROR_CALL 'Unresolved reference: R|/|' type=IrErrorType - VAR name: type:IrErrorType [val] - BLOCK type=IrErrorType origin=EXCLEXCL - VAR name: type:IrErrorType [val] - ERROR_CALL 'Unresolved reference: #' type=IrErrorType - WHEN type=IrErrorType origin=EXCLEXCL + VAR name: type:kotlin.collections.MutableList [val] + BLOCK type=kotlin.collections.MutableList origin=EXCLEXCL + VAR name: type:kotlin.collections.MutableList [val] + CALL 'public abstract fun f (): kotlin.collections.MutableList declared in .X' type=kotlin.collections.MutableList origin=null + $this: GET_VAR 'nx: .X? declared in .test' type=.X? origin=null + WHEN type=kotlin.collections.MutableList origin=EXCLEXCL BRANCH if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ - arg0: GET_VAR 'val : IrErrorType [val] declared in .test' type=IrErrorType origin=null + arg0: GET_VAR 'val : kotlin.collections.MutableList [val] declared in .test' type=kotlin.collections.MutableList origin=null arg1: CONST Null type=kotlin.Nothing? value=null then: THROW type=kotlin.Nothing CONSTRUCTOR_CALL 'public constructor () declared in kotlin.KotlinNullPointerException' type=kotlin.KotlinNullPointerException origin=null BRANCH if: CONST Boolean type=kotlin.Boolean value=true - then: GET_VAR 'val : IrErrorType [val] declared in .test' type=IrErrorType origin=null + then: GET_VAR 'val : kotlin.collections.MutableList [val] declared in .test' type=kotlin.collections.MutableList origin=null ERROR_CALL 'Unresolved reference: R|/|' type=IrErrorType FUN name:testExtensionReceiver visibility:public modality:FINAL <> () returnType:kotlin.Unit BLOCK_BODY diff --git a/compiler/testData/ir/irText/expressions/safeCallWithIncrementDecrement.fir.txt b/compiler/testData/ir/irText/expressions/safeCallWithIncrementDecrement.fir.txt index 03e08af3554..e2f47582a93 100644 --- a/compiler/testData/ir/irText/expressions/safeCallWithIncrementDecrement.fir.txt +++ b/compiler/testData/ir/irText/expressions/safeCallWithIncrementDecrement.fir.txt @@ -31,7 +31,8 @@ FILE fqName:test fileName:/safeCallWithIncrementDecrement.kt FUN name:inc visibility:public modality:FINAL <> () returnType:kotlin.Int? BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun inc (): kotlin.Int? declared in test' - CALL 'public final fun inc (): kotlin.Int? declared in test' type=kotlin.Int? origin=null + CALL 'public final fun inc (): kotlin.Int declared in kotlin.Int' type=kotlin.Int origin=null + $this: ERROR_CALL 'Unresolved reference: this#' type=kotlin.Int? FUN name:get visibility:public modality:FINAL <> (index:kotlin.Int) returnType:kotlin.Int VALUE_PARAMETER name:index index:0 type:kotlin.Int BLOCK_BODY diff --git a/compiler/testData/ir/irText/expressions/safeCalls.fir.txt b/compiler/testData/ir/irText/expressions/safeCalls.fir.txt index 382bb04c3b0..980942329f4 100644 --- a/compiler/testData/ir/irText/expressions/safeCalls.fir.txt +++ b/compiler/testData/ir/irText/expressions/safeCalls.fir.txt @@ -67,24 +67,25 @@ FILE fqName: fileName:/safeCalls.kt VALUE_PARAMETER name:x index:0 type:kotlin.String? BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun test2 (x: kotlin.String?): IrErrorType declared in ' - ERROR_CALL 'Unresolved reference: #' type=IrErrorType + ERROR_CALL 'Unresolved reference: #' type=IrErrorType FUN name:test3 visibility:public modality:FINAL <> (x:kotlin.String?, y:kotlin.Any?) returnType:IrErrorType VALUE_PARAMETER name:x index:0 type:kotlin.String? VALUE_PARAMETER name:y index:1 type:kotlin.Any? BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun test3 (x: kotlin.String?, y: kotlin.Any?): IrErrorType declared in ' - ERROR_CALL 'Unresolved reference: #' type=IrErrorType + ERROR_CALL 'Unresolved reference: #' type=IrErrorType GET_VAR 'y: kotlin.Any? declared in .test3' type=kotlin.Any? origin=null FUN name:test4 visibility:public modality:FINAL <> (x:.Ref?) returnType:kotlin.Unit VALUE_PARAMETER name:x index:0 type:.Ref? BLOCK_BODY SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:value type:kotlin.Int visibility:public ' type=kotlin.Int origin=null value: CONST Int type=kotlin.Int value=0 - FUN name:test5 visibility:public modality:FINAL <> (s:kotlin.String?) returnType:IrErrorType + FUN name:test5 visibility:public modality:FINAL <> (s:kotlin.String?) returnType:kotlin.Int VALUE_PARAMETER name:s index:0 type:kotlin.String? BLOCK_BODY - RETURN type=kotlin.Nothing from='public final fun test5 (s: kotlin.String?): IrErrorType declared in ' - ERROR_CALL 'Unresolved reference: #' type=IrErrorType + RETURN type=kotlin.Nothing from='public final fun test5 (s: kotlin.String?): kotlin.Int declared in ' + CALL 'public open fun extLength (): kotlin.Int declared in .IHost' type=kotlin.Int origin=null + $this: GET_VAR 's: kotlin.String? declared in .test5' type=kotlin.String? origin=null FUN name:foo visibility:public modality:FINAL <> () returnType:kotlin.Int BLOCK_BODY RETURN type=kotlin.Nothing from='public final fun foo (): kotlin.Int declared in ' diff --git a/compiler/testData/ir/irText/expressions/temporaryInEnumEntryInitializer.fir.txt b/compiler/testData/ir/irText/expressions/temporaryInEnumEntryInitializer.fir.txt index c83299490ba..79d1b7c88e1 100644 --- a/compiler/testData/ir/irText/expressions/temporaryInEnumEntryInitializer.fir.txt +++ b/compiler/testData/ir/irText/expressions/temporaryInEnumEntryInitializer.fir.txt @@ -31,7 +31,8 @@ FILE fqName: fileName:/temporaryInEnumEntryInitializer.kt CONSTRUCTOR visibility:public <> () returnType:.En.ENTRY [primary] BLOCK_BODY DELEGATING_CONSTRUCTOR_CALL 'private constructor (x: kotlin.String?) [primary] declared in .En' - x: CALL 'public final fun toString (): kotlin.String declared in kotlin' type=kotlin.String origin=null + x: CALL 'public open fun toString (): kotlin.String declared in kotlin.Any' type=kotlin.String origin=null + $this: CALL 'public final fun (): kotlin.Any? declared in ' type=kotlin.Any? origin=null INSTANCE_INITIALIZER_CALL classDescriptor='CLASS ENUM_ENTRY name:ENTRY modality:FINAL visibility:public superTypes:[.En]' FUN FAKE_OVERRIDE name:clone visibility:protected modality:FINAL <> ($this:kotlin.Enum) returnType:kotlin.Any overridden: diff --git a/compiler/testData/ir/irText/expressions/temporaryInInitBlock.fir.txt b/compiler/testData/ir/irText/expressions/temporaryInInitBlock.fir.txt index e26ead6d7dd..783750fa5ef 100644 --- a/compiler/testData/ir/irText/expressions/temporaryInInitBlock.fir.txt +++ b/compiler/testData/ir/irText/expressions/temporaryInInitBlock.fir.txt @@ -18,7 +18,8 @@ FILE fqName: fileName:/temporaryInInitBlock.kt ANONYMOUS_INITIALIZER isStatic=false BLOCK_BODY SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:s type:kotlin.String? visibility:public [final] ' type=kotlin.String? origin=null - value: CALL 'public final fun toString (): kotlin.String declared in kotlin' type=kotlin.String origin=null + value: CALL 'public open fun toString (): kotlin.String declared in kotlin.Any' type=kotlin.String origin=null + $this: GET_VAR 'x: kotlin.Any? declared in .C.' type=kotlin.Any? origin=null FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean overridden: public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in kotlin.Any