Skip to content

Commit

Permalink
K1: introduce BUILDER_INFERENCE_STUB_PARAMETER_TYPE to prevent compil…
Browse files Browse the repository at this point in the history
…er crashes

This diagnostic is reported in rare situations when
StubTypeForBuilderInference is kept as a parameter type
of for loop or lambda. Before this commit, we had in K1
"Could not load module <error module>" from IrLinker instead.

Related to: KT-52757, KT-53109, KT-63841, KT-64066
#KT-53478 Fixed

(cherry picked from commit 678816f)
  • Loading branch information
mglukhikh authored and qodana-bot committed Feb 28, 2024
1 parent 1deb116 commit c262b97
Show file tree
Hide file tree
Showing 33 changed files with 661 additions and 7 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,7 @@ enum BadNamedArgumentsTarget {
DiagnosticFactory4<PsiElement, KotlinType, String, String, BuilderLambdaLabelingInfo> STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY = DiagnosticFactory4.create(ERROR);
DiagnosticFactory2<PsiElement, Name, Name> BUILDER_INFERENCE_MULTI_LAMBDA_RESTRICTION = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<PsiElement, Name, Name> BUILDER_INFERENCE_STUB_RECEIVER = DiagnosticFactory2.create(ERROR);
DiagnosticFactory1<KtDeclaration, Name> BUILDER_INFERENCE_STUB_PARAMETER_TYPE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory4<KtElement, Name, Name, KotlinType, KotlinType> UPPER_BOUND_VIOLATION_IN_CONSTRAINT = DiagnosticFactory4.create(ERROR);

DiagnosticFactory1<PsiElement, Collection<? extends ResolvedCall<?>>> NONE_APPLICABLE = DiagnosticFactory1.create(ERROR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,7 @@ public static DiagnosticRenderer getRendererForDiagnostic(@NotNull UnboundDiagno
MAP.put(STUB_TYPE_IN_RECEIVER_CAUSES_AMBIGUITY, "The type of a receiver hasn''t been inferred yet. To disambiguate this call, explicitly cast it to `{0}` if you want the builder''s type parameter(s) `{1}` to be inferred to `{2}`.", RENDER_TYPE, STRING, STRING, null);
MAP.put(BUILDER_INFERENCE_MULTI_LAMBDA_RESTRICTION, "Unstable inference behaviour with multiple lambdas. Please either specify the type argument for generic parameter `{0}` of `{1}` explicitly", TO_STRING, TO_STRING);
MAP.put(BUILDER_INFERENCE_STUB_RECEIVER, "The type of a receiver hasn''t been inferred yet. Please specify type argument for generic parameter `{0}` of `{1}` explicitly", TO_STRING, TO_STRING);
MAP.put(BUILDER_INFERENCE_STUB_PARAMETER_TYPE, "The type of parameter `{0}` cannot be inferred by builder inference", TO_STRING);
MAP.put(UPPER_BOUND_VIOLATION_IN_CONSTRAINT, "Upper bound violation for generic parameter `{0}` of `{1}`: {3} is not a subtype of {2}", TO_STRING, TO_STRING, RENDER_TYPE, RENDER_TYPE);
MAP.put(NONE_APPLICABLE, "None of the following functions can be called with the arguments supplied: {0}", AMBIGUOUS_CALLS);
MAP.put(CANNOT_COMPLETE_RESOLVE, "Cannot choose among the following candidates without completing type inference: {0}", AMBIGUOUS_CALLS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
EnumEntriesRedeclarationChecker,
VolatileAnnotationChecker,
ActualTypealiasToSpecialAnnotationChecker,
StubForBuilderInferenceParameterTypeChecker,
)

private val DEFAULT_CALL_CHECKERS = listOf(
Expand All @@ -80,7 +81,7 @@ private val DEFAULT_CALL_CHECKERS = listOf(
NewSchemeOfIntegerOperatorResolutionChecker, EnumEntryVsCompanionPriorityCallChecker, CompanionInParenthesesLHSCallChecker,
ResolutionToPrivateConstructorOfSealedClassChecker, EqualityCallChecker, UnsupportedUntilOperatorChecker,
BuilderInferenceAssignmentChecker, IncorrectCapturedApproximationCallChecker, CompanionIncorrectlyUnboundedWhenUsedAsLHSCallChecker,
CustomEnumEntriesMigrationCallChecker, EnumEntriesUnsupportedChecker
CustomEnumEntriesMigrationCallChecker, EnumEntriesUnsupportedChecker,
)
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.getType
import org.jetbrains.kotlin.types.StubTypeForBuilderInference
Expand All @@ -24,18 +26,27 @@ object BuilderInferenceAssignmentChecker : CallChecker {
val resultingDescriptor = resolvedCall.resultingDescriptor
if (resultingDescriptor !is PropertyDescriptor) return
if (context.languageVersionSettings.supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction)) return
if (resolvedCall.candidateDescriptor.returnType !is StubTypeForBuilderInference) return
val callElement = resolvedCall.call.callElement
if (callElement !is KtNameReferenceExpression) return
val binaryExpression = callElement.getParentOfType<KtBinaryExpression>(strict = true) ?: return
if (binaryExpression.operationToken != KtTokens.EQ) return
if (!BasicExpressionTypingVisitor.isLValue(callElement, binaryExpression)) return

val leftType = resultingDescriptor.returnType?.takeIf { !it.isError } ?: return
val right = binaryExpression.right ?: return
val rightType = right.getType(context.trace.bindingContext) ?: return

if (isAssignmentCorrectWithDataFlowInfo(leftType, right, rightType, context)) return
context.trace.report(Errors.TYPE_MISMATCH.on(right, leftType, rightType))
if (resolvedCall.candidateDescriptor.returnType is StubTypeForBuilderInference) {
val leftType = resultingDescriptor.returnType?.takeIf { !it.isError } ?: return
val rightType = right.getType(context.trace.bindingContext) ?: return

if (isAssignmentCorrectWithDataFlowInfo(leftType, right, rightType, context)) return
context.trace.report(Errors.TYPE_MISMATCH.on(right, leftType, rightType))
} else if (right is KtLambdaExpression) {
val functionLiteral = right.functionLiteral
val functionDescriptor = context.trace.get(BindingContext.FUNCTION, right.functionLiteral) ?: return
for ((index, valueParameterDescriptor) in functionDescriptor.valueParameters.withIndex()) {
if (valueParameterDescriptor.type !is StubTypeForBuilderInference) continue
val target = functionLiteral.valueParameters.getOrNull(index) ?: functionLiteral
context.trace.report(Errors.BUILDER_INFERENCE_STUB_PARAMETER_TYPE.on(target, valueParameterDescriptor.name))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/

package org.jetbrains.kotlin.resolve.checkers

import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.types.StubTypeForBuilderInference

object StubForBuilderInferenceParameterTypeChecker : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
if (declaration !is KtParameter ||
descriptor !is ValueDescriptor ||
descriptor.returnType !is StubTypeForBuilderInference
) return
if (context.languageVersionSettings.supportsFeature(LanguageFeature.NoBuilderInferenceWithoutAnnotationRestriction)) return

context.trace.report(Errors.BUILDER_INFERENCE_STUB_PARAMETER_TYPE.on(declaration, declaration.nameAsSafeName))
}
}
19 changes: 19 additions & 0 deletions compiler/testData/codegen/box/builderInference/issues/kt53478.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// ISSUE: KT-53478
// IGNORE_BACKEND_K1: ANY
// Reason: red code

class UncompilingClass<T : Any>(
val block: (UncompilingClass<T>.() -> Unit)? = null,
) {

var uncompilingFun: ((T) -> Unit)? = null
}

fun handleInt(arg: Int) = Unit

fun box(): String {
val obj = UncompilingClass {
uncompilingFun = { handleInt(it) }
}
return "OK"
}
13 changes: 13 additions & 0 deletions compiler/testData/codegen/box/builderInference/issues/kt64066.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// WITH_STDLIB
// ISSUE: KT-64066
// IGNORE_BACKEND_K1: ANY
// Reason: red code

fun box(): String {
val map = buildMap {
put(1, 1)
for (v in values) {}
}
if (map[1] != 1) return "FAIL"
return "OK"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// ISSUE: KT-53478

class UncompilingClass<T : Any>(
val block: (UncompilingClass<T>.() -> Unit)? = null,
) {

var uncompilingFun: ((T) -> Unit)? = null
}

fun handleInt(arg: Int) = Unit

fun box() {
val obj = UncompilingClass {
uncompilingFun = { handleInt(it) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// ISSUE: KT-53478

class UncompilingClass<T : Any>(
val block: (UncompilingClass<T>.() -> Unit)? = null,
) {

var uncompilingFun: ((T) -> Unit)? = null
}

fun handleInt(arg: Int) = Unit

fun box() {
val obj = UncompilingClass {
uncompilingFun = <!BUILDER_INFERENCE_STUB_PARAMETER_TYPE!>{ handleInt(it) }<!>
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// WITH_STDLIB
// ISSUE: KT-64066

fun box() {
val map = buildMap {
put(1, 1)
for (v in values) {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// WITH_STDLIB
// ISSUE: KT-64066

fun box() {
val map = buildMap {
put(1, 1)
for (<!BUILDER_INFERENCE_STUB_PARAMETER_TYPE!>v<!> in values) {}
}
}
Loading

0 comments on commit c262b97

Please sign in to comment.