diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 7688522303d..dbcdaddbcc4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -809,6 +809,7 @@ public interface Errors { DiagnosticFactory0 REIFIED_TYPE_PARAMETER_IN_OVERRIDE = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 INLINE_CALL_CYCLE = DiagnosticFactory1.create(ERROR, DEFAULT); DiagnosticFactory0 NON_LOCAL_RETURN_IN_DISABLED_INLINE = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 INLINE_PROPERTY_WITH_BACKING_FIELD = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 NON_LOCAL_SUSPENSION_POINT = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index b726e8979bb..2491c2c4616 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -757,6 +757,7 @@ public class DefaultErrorMessages { MAP.put(USAGE_IS_NOT_INLINABLE, "Illegal usage of inline-parameter ''{0}'' in ''{1}''. Add ''noinline'' modifier to the parameter declaration", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); MAP.put(NULLABLE_INLINE_PARAMETER, "Inline-parameter ''{0}'' of ''{1}'' must not be nullable. Add ''noinline'' modifier to the parameter declaration or make its type not nullable", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); MAP.put(RECURSION_IN_INLINE, "Inline function ''{1}'' cannot be recursive", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES); + MAP.put(INLINE_PROPERTY_WITH_BACKING_FIELD, "Inline property ''{9}'' cannot have backing field"); //Inline non locals MAP.put(NON_LOCAL_RETURN_NOT_ALLOWED, "Can''t inline ''{0}'' here: it may contain non-local returns. Add ''crossinline'' modifier to parameter declaration ''{0}''", ELEMENT_TEXT); MAP.put(INLINE_CALL_CYCLE, "The ''{0}'' invocation is a part of inline cycle", NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionAnalyzerExtension.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnalyzerExtensions.java similarity index 53% rename from compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionAnalyzerExtension.java rename to compiler/frontend/src/org/jetbrains/kotlin/resolve/AnalyzerExtensions.java index afbb08600c0..c605c74c26a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionAnalyzerExtension.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnalyzerExtensions.java @@ -17,9 +17,13 @@ package org.jetbrains.kotlin.resolve; import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; +import org.jetbrains.kotlin.descriptors.PropertyDescriptor; import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor; +import org.jetbrains.kotlin.psi.KtCallableDeclaration; import org.jetbrains.kotlin.psi.KtNamedFunction; +import org.jetbrains.kotlin.psi.KtProperty; import org.jetbrains.kotlin.resolve.inline.InlineAnalyzerExtension; import org.jetbrains.kotlin.resolve.inline.InlineUtil; @@ -27,15 +31,15 @@ import java.util.Collections; import java.util.List; import java.util.Map; -public class FunctionAnalyzerExtension { +public class AnalyzerExtensions { public interface AnalyzerExtension { - void process(@NotNull FunctionDescriptor descriptor, @NotNull KtNamedFunction function, @NotNull BindingTrace trace); + void process(@NotNull CallableMemberDescriptor descriptor, @NotNull KtCallableDeclaration functionOrProperty, @NotNull BindingTrace trace); } @NotNull private final BindingTrace trace; - public FunctionAnalyzerExtension(@NotNull BindingTrace trace) { + public AnalyzerExtensions(@NotNull BindingTrace trace) { this.trace = trace; } @@ -44,16 +48,33 @@ public class FunctionAnalyzerExtension { KtNamedFunction function = entry.getKey(); SimpleFunctionDescriptor functionDescriptor = entry.getValue(); - for (AnalyzerExtension extension : getExtensions(functionDescriptor)) { + for (AnalyzerExtension extension : getFunctionExtensions(functionDescriptor)) { extension.process(functionDescriptor, function, trace); } } + + for (Map.Entry entry : bodiesResolveContext.getProperties().entrySet()) { + KtProperty function = entry.getKey(); + PropertyDescriptor propertyDescriptor = entry.getValue(); + + for (AnalyzerExtension extension : getPropertyExtensions(propertyDescriptor)) { + extension.process(propertyDescriptor, function, trace); + } + } } @NotNull - private static List getExtensions(@NotNull FunctionDescriptor functionDescriptor) { + private static List getFunctionExtensions(@NotNull FunctionDescriptor functionDescriptor) { if (InlineUtil.isInline(functionDescriptor)) { - return Collections.singletonList(InlineAnalyzerExtension.INSTANCE); + return Collections.singletonList(InlineAnalyzerExtension.INSTANCE); + } + return Collections.emptyList(); + } + + @NotNull + private static List getPropertyExtensions(@NotNull PropertyDescriptor propertyDescriptor) { + if (InlineUtil.hasInlineAccessors(propertyDescriptor)) { + return Collections.singletonList(InlineAnalyzerExtension.INSTANCE); } return Collections.emptyList(); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java index cca0814efae..c95abf7ec82 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java @@ -66,7 +66,7 @@ public class BodyResolver { @NotNull private final DeclarationsChecker declarationsChecker; @NotNull private final AnnotationResolver annotationResolver; @NotNull private final DelegatedPropertyResolver delegatedPropertyResolver; - @NotNull private final FunctionAnalyzerExtension functionAnalyzerExtension; + @NotNull private final AnalyzerExtensions analyzerExtensions; @NotNull private final ValueParameterResolver valueParameterResolver; @NotNull private final BodyResolveCache bodyResolveCache; @NotNull private final KotlinBuiltIns builtIns; @@ -81,7 +81,7 @@ public class BodyResolver { @NotNull DeclarationsChecker declarationsChecker, @NotNull DelegatedPropertyResolver delegatedPropertyResolver, @NotNull ExpressionTypingServices expressionTypingServices, - @NotNull FunctionAnalyzerExtension functionAnalyzerExtension, + @NotNull AnalyzerExtensions analyzerExtensions, @NotNull BindingTrace trace, @NotNull ValueParameterResolver valueParameterResolver, @NotNull AnnotationChecker annotationChecker, @@ -96,7 +96,7 @@ public class BodyResolver { this.declarationsChecker = declarationsChecker; this.delegatedPropertyResolver = delegatedPropertyResolver; this.expressionTypingServices = expressionTypingServices; - this.functionAnalyzerExtension = functionAnalyzerExtension; + this.analyzerExtensions = analyzerExtensions; this.annotationChecker = annotationChecker; this.overloadChecker = overloadChecker; this.trace = new ObservableBindingTrace(trace); @@ -239,7 +239,7 @@ public class BodyResolver { resolveBehaviorDeclarationBodies(c); controlFlowAnalyzer.process(c); declarationsChecker.process(c); - functionAnalyzerExtension.process(c); + analyzerExtensions.process(c); } private void resolveSuperTypeEntryLists(@NotNull BodiesResolveContext c) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineAnalyzerExtension.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineAnalyzerExtension.kt index 9125554daa4..771761bd203 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineAnalyzerExtension.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineAnalyzerExtension.kt @@ -20,20 +20,43 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.AnalyzerExtensions +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace -import org.jetbrains.kotlin.resolve.FunctionAnalyzerExtension import org.jetbrains.kotlin.resolve.annotations.isInlineOnlyOrReified import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue -object InlineAnalyzerExtension : FunctionAnalyzerExtension.AnalyzerExtension { +object InlineAnalyzerExtension : AnalyzerExtensions.AnalyzerExtension { - override fun process(descriptor: FunctionDescriptor, function: KtNamedFunction, trace: BindingTrace) { + override fun process(descriptor: CallableMemberDescriptor, functionOrProperty: KtCallableDeclaration, trace: BindingTrace) { assert(InlineUtil.isInline(descriptor)) { "This method should be invoked on inline function: " + descriptor } - checkDefaults(descriptor, function, trace) - checkModalityAndOverrides(descriptor, function, trace) - checkHasInlinableAndNullability(descriptor, function, trace) + checkModalityAndOverrides(descriptor, functionOrProperty, trace) + notSupportedInInlineCheck(descriptor, functionOrProperty, trace) + if (descriptor is FunctionDescriptor) { + assert (functionOrProperty is KtNamedFunction) { + "Function descriptor $descriptor should have corresponded KtNamedFunction, but has $functionOrProperty" + } + checkDefaults(descriptor, functionOrProperty as KtNamedFunction, trace) + checkHasInlinableAndNullability(descriptor, functionOrProperty, trace) + } + else { + assert (descriptor is PropertyDescriptor) { + "PropertyDescriptor expected, but was $descriptor" + } + assert (functionOrProperty is KtProperty) { + "Property descriptor $descriptor should have corresponded KtProperty, but has $functionOrProperty" + } + + val hasBackingField = java.lang.Boolean.TRUE.equals(trace.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor as PropertyDescriptor)) + if (hasBackingField || (functionOrProperty as KtProperty).delegateExpression != null) { + trace.report(Errors.INLINE_PROPERTY_WITH_BACKING_FIELD.on(functionOrProperty)) + } + } + } + + private fun notSupportedInInlineCheck(descriptor: CallableMemberDescriptor, functionOrProperty: KtCallableDeclaration, trace: BindingTrace) { val visitor = object : KtVisitorVoid() { override fun visitKtElement(element: KtElement) { super.visitKtElement(element) @@ -54,7 +77,7 @@ object InlineAnalyzerExtension : FunctionAnalyzerExtension.AnalyzerExtension { } } - function.acceptChildren(visitor) + functionOrProperty.acceptChildren(visitor) } private fun checkDefaults( @@ -74,22 +97,22 @@ object InlineAnalyzerExtension : FunctionAnalyzerExtension.AnalyzerExtension { } private fun checkModalityAndOverrides( - functionDescriptor: FunctionDescriptor, - function: KtFunction, + callableDescriptor: CallableMemberDescriptor, + functionOrProperty: KtCallableDeclaration, trace: BindingTrace) { - if (functionDescriptor.containingDeclaration is PackageFragmentDescriptor) { + if (callableDescriptor.containingDeclaration is PackageFragmentDescriptor) { return } - if (Visibilities.isPrivate(functionDescriptor.visibility)) { + if (Visibilities.isPrivate(callableDescriptor.visibility)) { return } - val overridesAnything = functionDescriptor.overriddenDescriptors.isNotEmpty() + val overridesAnything = callableDescriptor.overriddenDescriptors.isNotEmpty() if (overridesAnything) { - val ktTypeParameters = function.typeParameters - for (typeParameter in functionDescriptor.typeParameters) { + val ktTypeParameters = functionOrProperty.typeParameters + for (typeParameter in callableDescriptor.typeParameters) { if (typeParameter.isReified) { val ktTypeParameter = ktTypeParameters[typeParameter.index] val reportOn = ktTypeParameter.modifierList?.getModifier(KtTokens.REIFIED_KEYWORD) ?: ktTypeParameter @@ -98,17 +121,16 @@ object InlineAnalyzerExtension : FunctionAnalyzerExtension.AnalyzerExtension { } } - if (functionDescriptor.isEffectivelyFinal()) { + if (callableDescriptor.isEffectivelyFinal()) { if (overridesAnything) { - trace.report(Errors.OVERRIDE_BY_INLINE.on(function)) + trace.report(Errors.OVERRIDE_BY_INLINE.on(functionOrProperty)) } return } - - trace.report(Errors.DECLARATION_CANT_BE_INLINED.on(function)) + trace.report(Errors.DECLARATION_CANT_BE_INLINED.on(functionOrProperty)) } - private fun FunctionDescriptor.isEffectivelyFinal(): Boolean = + private fun CallableMemberDescriptor.isEffectivelyFinal(): Boolean = modality == Modality.FINAL || containingDeclaration.let { containingDeclaration -> containingDeclaration is ClassDescriptor && containingDeclaration.modality == Modality.FINAL diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java index 10aed6dd47e..0ffa0f436e0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/inline/InlineUtil.java @@ -47,6 +47,12 @@ public class InlineUtil { return descriptor instanceof FunctionDescriptor && getInlineStrategy((FunctionDescriptor) descriptor).isInline(); } + public static boolean hasInlineAccessors(@Nullable PropertyDescriptor propertyDescriptor) { + PropertyGetterDescriptor getter = propertyDescriptor.getGetter(); + PropertySetterDescriptor setter = propertyDescriptor.getSetter(); + return getter != null && getter.isInline() || setter != null && setter.isInline(); + } + public static boolean isInlineProperty(@Nullable DeclarationDescriptor descriptor) { if (!(descriptor instanceof PropertyDescriptor)) return false; diff --git a/compiler/testData/diagnostics/tests/inline/property/propertyWithBackingField.kt b/compiler/testData/diagnostics/tests/inline/property/propertyWithBackingField.kt new file mode 100644 index 00000000000..1b0cdd68f62 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/property/propertyWithBackingField.kt @@ -0,0 +1,21 @@ +// WITH_REFLECT + +import kotlin.reflect.KProperty + +class Delegate { + operator fun getValue(t: Any?, p: KProperty<*>): Int = 1 +} + +open class A { + inline val z1 = 1 + + val z1_1 = 1 + inline get() = field + 1 + + inline var z2 = 1 + + var z2_1 = 1 + inline set(p: Int) {} + + inline val z by Delegate() +} diff --git a/compiler/testData/diagnostics/tests/inline/property/propertyWithBackingField.txt b/compiler/testData/diagnostics/tests/inline/property/propertyWithBackingField.txt new file mode 100644 index 00000000000..827aae081f5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/property/propertyWithBackingField.txt @@ -0,0 +1,21 @@ +package + +public open class A { + public constructor A() + public final val z: kotlin.Int + public final val z1: kotlin.Int = 1 + public final val z1_1: kotlin.Int = 1 + public final var z2: kotlin.Int + public final var z2_1: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Delegate { + public constructor Delegate() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun getValue(/*0*/ t: kotlin.Any?, /*1*/ p: kotlin.reflect.KProperty<*>): kotlin.Int + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/inline/property/unsupportedConstruction.kt b/compiler/testData/diagnostics/tests/inline/property/unsupportedConstruction.kt new file mode 100644 index 00000000000..4c0037fd291 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/property/unsupportedConstruction.kt @@ -0,0 +1,20 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE + +inline val z: Int + get() { + + class A { + fun a() { + class AInner {} + } + } + + object B{ + object BInner {} + } + + fun local() { + fun localInner() {} + } + return 1 +} diff --git a/compiler/testData/diagnostics/tests/inline/property/unsupportedConstruction.txt b/compiler/testData/diagnostics/tests/inline/property/unsupportedConstruction.txt new file mode 100644 index 00000000000..adbdb8cef19 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/property/unsupportedConstruction.txt @@ -0,0 +1,3 @@ +package + +public val z: kotlin.Int diff --git a/compiler/testData/diagnostics/tests/inline/property/virtualProperty.kt b/compiler/testData/diagnostics/tests/inline/property/virtualProperty.kt new file mode 100644 index 00000000000..4789da733c2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/property/virtualProperty.kt @@ -0,0 +1,38 @@ +final class FinalProperty { + inline val valProp: Int + get() = 1 + + val valProp_1: Int + inline get() = 1 + + inline var varProp: Int + get() = 1 + set(p: Int) {} + + var varProp_2: Int + get() = 1 + inline set(p: Int) {} +} + + +open class OpenProperty { + inline open val valProp: Int + get() = 1 + + open val valProp_1: Int + inline get() = 1 + + inline open var varProp: Int + get() = 1 + set(p: Int) {} + + open var varProp_2: Int + get() = 1 + inline set(p: Int) {} +} + + +interface AbstractProperty { + inline abstract val valProp: Int + inline abstract var varProp: Int +} diff --git a/compiler/testData/diagnostics/tests/inline/property/virtualProperty.txt b/compiler/testData/diagnostics/tests/inline/property/virtualProperty.txt new file mode 100644 index 00000000000..829f1d1aabb --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/property/virtualProperty.txt @@ -0,0 +1,31 @@ +package + +public interface AbstractProperty { + public abstract val valProp: kotlin.Int + public abstract var varProp: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class FinalProperty { + public constructor FinalProperty() + public final val valProp: kotlin.Int + public final val valProp_1: kotlin.Int + public final var varProp: kotlin.Int + public final var varProp_2: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public open class OpenProperty { + public constructor OpenProperty() + public open val valProp: kotlin.Int + public open val valProp_1: kotlin.Int + public open var varProp: kotlin.Int + public open var varProp_2: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 844a0b513bd..6a1f40a2e7e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -10159,6 +10159,24 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/property/invoke.kt"); doTest(fileName); } + + @TestMetadata("propertyWithBackingField.kt") + public void testPropertyWithBackingField() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/property/propertyWithBackingField.kt"); + doTest(fileName); + } + + @TestMetadata("unsupportedConstruction.kt") + public void testUnsupportedConstruction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/property/unsupportedConstruction.kt"); + doTest(fileName); + } + + @TestMetadata("virtualProperty.kt") + public void testVirtualProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inline/property/virtualProperty.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/inline/regressions")