diff --git a/compiler/frontend/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationSplitter.kt b/compiler/frontend/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationSplitter.kt index 331a847369d..b2074203141 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationSplitter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationSplitter.kt @@ -127,6 +127,7 @@ public class AnnotationSplitter( } override fun isEmpty() = annotations.isEmpty() + override fun hasAnnotation(fqName: FqName) = annotations.hasAnnotation(fqName) override fun findAnnotation(fqName: FqName) = annotations.findAnnotation(fqName) override fun getUseSiteTargetedAnnotations() = annotations.getUseSiteTargetedAnnotations() override fun getAllAnnotations() = annotations.getAllAnnotations() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java index 8efd2dbb445..0b4a3fa59b0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/BasicExpressionTypingVisitor.java @@ -63,6 +63,7 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.resolve.scopes.utils.ScopeUtilsKt; import org.jetbrains.kotlin.types.*; +import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.ResolveConstruct; import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt; import org.jetbrains.kotlin.types.expressions.unqualifiedSuper.UnqualifiedSuperKt; @@ -830,7 +831,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { // See also CallExpressionResolver.getSimpleNameExpressionTypeInfo, .getQualifiedExpressionTypeInfo Call call = createCallForSpecialConstruction(expression, expression.getOperationReference(), Collections.singletonList(baseExpression)); components.controlStructureTypingUtils.resolveSpecialConstructionAsCall( - call, "ExclExcl", Collections.singletonList("baseExpr"), Collections.singletonList(true), context, null); + call, ResolveConstruct.EXCL_EXCL, Collections.singletonList("baseExpr"), Collections.singletonList(true), context, null); JetTypeInfo baseTypeInfo = BindingContextUtils.getRecordedTypeInfo(baseExpression, context.trace.getBindingContext()); if (ArgumentTypeResolver.isFunctionLiteralArgument(baseExpression, context)) { @@ -1163,7 +1164,7 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor { Call call = createCallForSpecialConstruction(expression, expression.getOperationReference(), Lists.newArrayList(left, right)); ResolvedCall resolvedCall = components.controlStructureTypingUtils.resolveSpecialConstructionAsCall( - call, "Elvis", Lists.newArrayList("left", "right"), Lists.newArrayList(true, false), contextWithExpectedType, null); + call, ResolveConstruct.ELVIS, Lists.newArrayList("left", "right"), Lists.newArrayList(true, false), contextWithExpectedType, null); JetTypeInfo leftTypeInfo = BindingContextUtils.getRecordedTypeInfo(left, context.trace.getBindingContext()); if (ArgumentTypeResolver.isFunctionLiteralArgument(left, context)) { context.trace.report(USELESS_ELVIS_ON_FUNCTION_LITERAL.on(expression.getOperationReference())); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingUtils.java index b39193445fb..465a11cbb4b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingUtils.java @@ -47,10 +47,12 @@ import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind; import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate; import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy; import org.jetbrains.kotlin.resolve.calls.util.CallMaker; +import org.jetbrains.kotlin.resolve.descriptorUtil.AnnotationsForResolveKt; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.types.Variance; +import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; import java.util.*; @@ -61,6 +63,20 @@ import static org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.Co public class ControlStructureTypingUtils { private static final Logger LOG = Logger.getInstance(ControlStructureTypingUtils.class); + public enum ResolveConstruct { + IF("if"), ELVIS("elvis"), EXCL_EXCL("ExclExcl"); + + private final String name; + + ResolveConstruct(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + private final CallResolver callResolver; private final DataFlowAnalyzer dataFlowAnalyzer; private final ModuleDescriptor moduleDescriptor; @@ -77,15 +93,15 @@ public class ControlStructureTypingUtils { /*package*/ ResolvedCall resolveSpecialConstructionAsCall( @NotNull Call call, - @NotNull String constructionName, + @NotNull ResolveConstruct construct, @NotNull List argumentNames, @NotNull List isArgumentNullable, @NotNull ExpressionTypingContext context, @Nullable MutableDataFlowInfoForArguments dataFlowInfoForArguments ) { SimpleFunctionDescriptorImpl function = createFunctionDescriptorForSpecialConstruction( - constructionName.toUpperCase(), argumentNames, isArgumentNullable); - TracingStrategy tracing = createTracingForSpecialConstruction(call, constructionName, context); + construct, argumentNames, isArgumentNullable); + TracingStrategy tracing = createTracingForSpecialConstruction(call, construct.getName(), context); ResolutionCandidate resolutionCandidate = ResolutionCandidate.create(call, function); OverloadResolutionResults results = callResolver.resolveCallWithKnownCandidate( call, tracing, context, resolutionCandidate, dataFlowInfoForArguments); @@ -94,12 +110,13 @@ public class ControlStructureTypingUtils { } private SimpleFunctionDescriptorImpl createFunctionDescriptorForSpecialConstruction( - @NotNull String constructionName, + @NotNull ResolveConstruct construct, @NotNull List argumentNames, @NotNull List isArgumentNullable ) { assert argumentNames.size() == isArgumentNullable.size(); + String constructionName = construct.getName().toUpperCase(); Name specialFunctionName = Name.identifier(""); SimpleFunctionDescriptorImpl function = SimpleFunctionDescriptorImpl.create( @@ -126,12 +143,13 @@ public class ControlStructureTypingUtils { ); valueParameters.add(valueParameter); } + KotlinType returnType = construct != ResolveConstruct.ELVIS ? type : TypeUtilsKt.replaceAnnotations(type, AnnotationsForResolveKt.getExactInAnnotations()); function.initialize( null, null, Lists.newArrayList(typeParameter), valueParameters, - type, + returnType, Modality.FINAL, Visibilities.PUBLIC ); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java index a7c89413b17..351ac6939e9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -41,6 +41,7 @@ import org.jetbrains.kotlin.types.CommonSupertypes; import org.jetbrains.kotlin.types.ErrorUtils; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.TypeUtils; +import org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.ResolveConstruct; import org.jetbrains.kotlin.types.checker.KotlinTypeChecker; import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt; @@ -128,7 +129,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { MutableDataFlowInfoForArguments dataFlowInfoForArguments = createDataFlowInfoForArgumentsForIfCall(callForIf, thenInfo, elseInfo); ResolvedCall resolvedCall = components.controlStructureTypingUtils.resolveSpecialConstructionAsCall( - callForIf, "If", Lists.newArrayList("thenBranch", "elseBranch"), + callForIf, ResolveConstruct.IF, Lists.newArrayList("thenBranch", "elseBranch"), Lists.newArrayList(false, false), contextWithExpectedType, dataFlowInfoForArguments); diff --git a/compiler/testData/diagnostics/tests/controlStructures/lambdasInExclExclAndElvis.kt b/compiler/testData/diagnostics/tests/controlStructures/lambdasInExclExclAndElvis.kt index d33b0cecc2b..4f95279d5ea 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/lambdasInExclExclAndElvis.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/lambdasInExclExclAndElvis.kt @@ -8,8 +8,8 @@ fun test() { // KT-KT-9070 { } ?: 1 - use({ 2 } ?: 1); + use({ 2 } ?: 1); 1 ?: { } - use(1 ?: { }) + use(1 ?: { }) } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/specialConstructions/inferenceForElvis.kt b/compiler/testData/diagnostics/tests/resolve/specialConstructions/inferenceForElvis.kt new file mode 100644 index 00000000000..ceee712d748 --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/specialConstructions/inferenceForElvis.kt @@ -0,0 +1,8 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE +// !CHECK_TYPE + +fun foo(f: (T) -> Unit): T? = null // T is used only as return type +fun test() { + val x = foo { it checkType { _() }} ?: "" // foo() is inferred as foo, which isn't very good + val y: Any = foo { it checkType { _() } } ?: "" // but for now it's fixed by specifying expected type +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/resolve/specialConstructions/inferenceForElvis.txt b/compiler/testData/diagnostics/tests/resolve/specialConstructions/inferenceForElvis.txt new file mode 100644 index 00000000000..5605357ed7c --- /dev/null +++ b/compiler/testData/diagnostics/tests/resolve/specialConstructions/inferenceForElvis.txt @@ -0,0 +1,4 @@ +package + +public fun foo(/*0*/ f: (T) -> kotlin.Unit): T? +public fun test(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index c28c695f727..c8aa765edc8 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -13088,6 +13088,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("inferenceForElvis.kt") + public void testInferenceForElvis() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/specialConstructions/inferenceForElvis.kt"); + doTest(fileName); + } + @TestMetadata("multipleSuperClasses.kt") public void testMultipleSuperClasses() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/resolve/specialConstructions/multipleSuperClasses.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt index 23231df3b99..cdf5ad7813f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt @@ -80,6 +80,11 @@ class FilteredAnnotations( private val delegate: Annotations, private val fqNameFilter: (FqName) -> Boolean ) : Annotations { + + override fun hasAnnotation(fqName: FqName) = + if (fqNameFilter(fqName)) delegate.hasAnnotation(fqName) + else false + override fun findAnnotation(fqName: FqName) = if (fqNameFilter(fqName)) delegate.findAnnotation(fqName) else null @@ -115,6 +120,8 @@ class CompositeAnnotations( override fun isEmpty() = delegates.all { it.isEmpty() } + override fun hasAnnotation(fqName: FqName) = delegates.asSequence().any { it.hasAnnotation(fqName) } + override fun findAnnotation(fqName: FqName) = delegates.asSequence().map { it.findAnnotation(fqName) }.filterNotNull().firstOrNull() override fun findExternalAnnotation(fqName: FqName) = delegates.asSequence().map { it.findExternalAnnotation(fqName) }.filterNotNull().firstOrNull() diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt index c4ba2bc8f38..3235f579174 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt @@ -18,6 +18,8 @@ package org.jetbrains.kotlin.resolve.descriptorUtil import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.types.KotlinType @@ -40,4 +42,22 @@ public fun CallableDescriptor.hasLowPriorityInOverloadResolution(): Boolean = an private val ONLY_INPUT_TYPES_FQ_NAME = FqName("kotlin.internal.OnlyInputTypes") -public fun TypeParameterDescriptor.hasOnlyInputTypesAnnotation(): Boolean = annotations.hasAnnotation(ONLY_INPUT_TYPES_FQ_NAME) \ No newline at end of file +public fun TypeParameterDescriptor.hasOnlyInputTypesAnnotation(): Boolean = annotations.hasAnnotation(ONLY_INPUT_TYPES_FQ_NAME) + +public fun getExactInAnnotations(): Annotations = AnnotationsWithOnly(EXACT_ANNOTATION_FQ_NAME) + +private class AnnotationsWithOnly(val presentAnnotation: FqName): Annotations { + override fun iterator(): Iterator = listOf().iterator() + + override fun isEmpty(): Boolean = false + + override fun hasAnnotation(fqName: FqName): Boolean = fqName == this.presentAnnotation + + override fun findAnnotation(fqName: FqName): AnnotationDescriptor? = null + + override fun findExternalAnnotation(fqName: FqName): AnnotationDescriptor? = null + + override fun getUseSiteTargetedAnnotations(): List = emptyList() + + override fun getAllAnnotations(): List = emptyList() +} \ No newline at end of file diff --git a/js/js.translator/testData/expression/misc/cases/elvisReturnSimple.kt b/js/js.translator/testData/expression/misc/cases/elvisReturnSimple.kt index a1998895157..26b67939e3c 100644 --- a/js/js.translator/testData/expression/misc/cases/elvisReturnSimple.kt +++ b/js/js.translator/testData/expression/misc/cases/elvisReturnSimple.kt @@ -12,7 +12,7 @@ fun stringLen(s : String?) : Int { } fun stringReturnInLeftLen(s : String?) : Int { - val s1 : String = (if (s != null) { return s.length() } else { null }) ?: return 0 + val s1 = (if (s != null) { return s.length() } else { null }) ?: return 0 } fun box(): String {