diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index e52ca807130..ad7ddb9d3c1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1722,7 +1722,8 @@ public class ExpressionCodegen extends KtVisitor impleme receiver = StackValue.receiverWithoutReceiverArgument(receiver); } - return intermediateValueForProperty(propertyDescriptor, directToField, directToField, superCallTarget, false, receiver, resolvedCall); + return intermediateValueForProperty(propertyDescriptor, directToField, directToField, superCallTarget, false, receiver, + resolvedCall, false); } if (descriptor instanceof TypeAliasDescriptor) { @@ -1874,7 +1875,7 @@ public class ExpressionCodegen extends KtVisitor impleme @Nullable ClassDescriptor superCallTarget, @NotNull StackValue receiver ) { - return intermediateValueForProperty(propertyDescriptor, forceField, false, superCallTarget, false, receiver, null); + return intermediateValueForProperty(propertyDescriptor, forceField, false, superCallTarget, false, receiver, null, false); } private CodegenContext getBackingFieldContext( @@ -1892,9 +1893,14 @@ public class ExpressionCodegen extends KtVisitor impleme return context.getParentContext(); // For companion object property, backing field lives in object containing class // Otherwise, it lives in its containing declaration - case IN_CLASS_COMPANION: return context.findParentContextWithDescriptor(containingDeclaration.getContainingDeclaration()); - case FIELD_FROM_LOCAL: return context.findParentContextWithDescriptor(containingDeclaration); - default: throw new IllegalStateException(); + case IN_CLASS_COMPANION: + return context.findParentContextWithDescriptor(containingDeclaration.getContainingDeclaration()); + case FIELD_FROM_LOCAL: + return context.findParentContextWithDescriptor(containingDeclaration); + case LATEINIT_INTRINSIC: + return context.findParentContextWithDescriptor(containingDeclaration); + default: + throw new IllegalStateException("Unknown field accessor kind: " + accessorKind); } } @@ -1905,7 +1911,8 @@ public class ExpressionCodegen extends KtVisitor impleme @Nullable ClassDescriptor superCallTarget, boolean skipAccessorsForPrivateFieldInOuterClass, @NotNull StackValue receiver, - @Nullable ResolvedCall resolvedCall + @Nullable ResolvedCall resolvedCall, + boolean skipLateinitAssertion ) { if (propertyDescriptor instanceof SyntheticJavaPropertyDescriptor) { return intermediateValueForSyntheticExtensionProperty((SyntheticJavaPropertyDescriptor) propertyDescriptor, receiver); @@ -1917,14 +1924,22 @@ public class ExpressionCodegen extends KtVisitor impleme DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration(); - FieldAccessorKind fieldAccessorKind = FieldAccessorKind.NORMAL; boolean isBackingFieldInClassCompanion = JvmAbi.isPropertyWithBackingFieldInOuterClass(propertyDescriptor); - if (isBackingFieldInClassCompanion && (forceField || propertyDescriptor.isConst() && Visibilities.isPrivate(propertyDescriptor.getVisibility()))) { + FieldAccessorKind fieldAccessorKind; + if (skipLateinitAssertion) { + fieldAccessorKind = FieldAccessorKind.LATEINIT_INTRINSIC; + } + else if (isBackingFieldInClassCompanion && + (forceField || propertyDescriptor.isConst() && Visibilities.isPrivate(propertyDescriptor.getVisibility()))) { fieldAccessorKind = FieldAccessorKind.IN_CLASS_COMPANION; } - else if (syntheticBackingField && context.getFirstCrossInlineOrNonInlineContext().getParentContext().getContextDescriptor() != containingDeclaration) { + else if ((syntheticBackingField && + context.getFirstCrossInlineOrNonInlineContext().getParentContext().getContextDescriptor() != containingDeclaration)) { fieldAccessorKind = FieldAccessorKind.FIELD_FROM_LOCAL; } + else { + fieldAccessorKind = FieldAccessorKind.NORMAL; + } boolean isStaticBackingField = DescriptorUtils.isStaticDeclaration(propertyDescriptor) || AsmUtil.isInstancePropertyWithStaticBackingField(propertyDescriptor); boolean isSuper = superCallTarget != null; @@ -1937,15 +1952,24 @@ public class ExpressionCodegen extends KtVisitor impleme CallableMethod callableSetter = null; CodegenContext backingFieldContext = getBackingFieldContext(fieldAccessorKind, containingDeclaration); - DeclarationDescriptor ownerDescriptor = containingDeclaration; + boolean isPrivateProperty = (AsmUtil.getVisibilityForBackingField(propertyDescriptor, isDelegatedProperty) & ACC_PRIVATE) != 0; + DeclarationDescriptor ownerDescriptor; boolean skipPropertyAccessors; PropertyDescriptor originalPropertyDescriptor = DescriptorUtils.unwrapFakeOverride(propertyDescriptor); - if (fieldAccessorKind != FieldAccessorKind.NORMAL) { - int flags = AsmUtil.getVisibilityForBackingField(propertyDescriptor, isDelegatedProperty); + if (fieldAccessorKind == FieldAccessorKind.LATEINIT_INTRINSIC) { + skipPropertyAccessors = !isPrivateProperty || context.getClassOrPackageParentContext() == backingFieldContext; + + if (!skipPropertyAccessors) { + propertyDescriptor = (AccessorForPropertyBackingField) + backingFieldContext.getAccessor(propertyDescriptor, fieldAccessorKind, delegateType, superCallTarget); + } + ownerDescriptor = propertyDescriptor; + } + else if (fieldAccessorKind == FieldAccessorKind.IN_CLASS_COMPANION || fieldAccessorKind == FieldAccessorKind.FIELD_FROM_LOCAL) { boolean isInlinedConst = propertyDescriptor.isConst() && state.getShouldInlineConstVals(); - skipPropertyAccessors = isInlinedConst || (flags & ACC_PRIVATE) == 0 || skipAccessorsForPrivateFieldInOuterClass; + skipPropertyAccessors = isInlinedConst || !isPrivateProperty || skipAccessorsForPrivateFieldInOuterClass; if (!skipPropertyAccessors) { //noinspection ConstantConditions @@ -1956,12 +1980,13 @@ public class ExpressionCodegen extends KtVisitor impleme "Unexpected accessor descriptor: " + propertyDescriptor; ownerDescriptor = propertyDescriptor; } + else { + ownerDescriptor = containingDeclaration; + } } else { - if (!isBackingFieldInClassCompanion) { - ownerDescriptor = propertyDescriptor; - } skipPropertyAccessors = forceField; + ownerDescriptor = isBackingFieldInClassCompanion ? containingDeclaration : propertyDescriptor; } if (!skipPropertyAccessors) { @@ -2005,10 +2030,11 @@ public class ExpressionCodegen extends KtVisitor impleme fieldName = KotlinTypeMapper.mapDefaultFieldName(propertyDescriptor, isDelegatedProperty); } - return StackValue.property(propertyDescriptor, backingFieldOwner, - typeMapper.mapType( - isDelegatedProperty && forceField ? delegateType : propertyDescriptor.getOriginal().getType()), - isStaticBackingField, fieldName, callableGetter, callableSetter, receiver, this, resolvedCall); + return StackValue.property( + propertyDescriptor, backingFieldOwner, + typeMapper.mapType(isDelegatedProperty && forceField ? delegateType : propertyDescriptor.getOriginal().getType()), + isStaticBackingField, fieldName, callableGetter, callableSetter, receiver, this, resolvedCall, skipLateinitAssertion + ); } @NotNull @@ -2022,7 +2048,8 @@ public class ExpressionCodegen extends KtVisitor impleme FunctionDescriptor setMethod = propertyDescriptor.getSetMethod(); CallableMethod callableSetter = setMethod != null ? typeMapper.mapToCallableMethod(context.accessibleDescriptor(setMethod, null), false) : null; - return StackValue.property(propertyDescriptor, null, type, false, null, callableGetter, callableSetter, receiver, this, null); + return StackValue.property(propertyDescriptor, null, type, false, null, callableGetter, callableSetter, receiver, this, + null, false); } @Override @@ -2226,7 +2253,9 @@ public class ExpressionCodegen extends KtVisitor impleme ) { boolean isSuspendCall = CoroutineCodegenUtilKt.isSuspendNoInlineCall(resolvedCall); boolean isConstructor = resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor; - putReceiverAndInlineMarkerIfNeeded(callableMethod, resolvedCall, receiver, isSuspendCall, isConstructor); + if (!(callableMethod instanceof IntrinsicWithSpecialReceiver)) { + putReceiverAndInlineMarkerIfNeeded(callableMethod, resolvedCall, receiver, isSuspendCall, isConstructor); + } callGenerator.processAndPutHiddenParameters(false); @@ -2356,12 +2385,15 @@ public class ExpressionCodegen extends KtVisitor impleme ) { if (callElement == null) return defaultCallGenerator; + boolean isIntrinsic = descriptor instanceof CallableMemberDescriptor && + state.getIntrinsics().getIntrinsic((CallableMemberDescriptor) descriptor) != null; + + boolean isInline = (InlineUtil.isInline(descriptor) && !isIntrinsic) || InlineUtil.isArrayConstructorWithLambda(descriptor); + // We should inline callable containing reified type parameters even if inline is disabled // because they may contain something to reify and straight call will probably fail at runtime - boolean isInline = (!state.isInlineDisabled() || InlineUtil.containsReifiedTypeParameters(descriptor)) && - (InlineUtil.isInline(descriptor) || InlineUtil.isArrayConstructorWithLambda(descriptor)); - - if (!isInline) return defaultCallGenerator; + boolean shouldInline = isInline && (!state.isInlineDisabled() || InlineUtil.containsReifiedTypeParameters(descriptor)); + if (!shouldInline) return defaultCallGenerator; FunctionDescriptor original = CoroutineCodegenUtilKt.getOriginalSuspendFunctionView( @@ -3606,13 +3638,8 @@ public class ExpressionCodegen extends KtVisitor impleme if (asProperty && variableDescriptor instanceof PropertyDescriptor) { StackValue.Property propertyValue = intermediateValueForProperty( - (PropertyDescriptor) variableDescriptor, - true, - false, - null, - true, - StackValue.LOCAL_0, - null); + (PropertyDescriptor) variableDescriptor, true, false, null, true, StackValue.LOCAL_0, null, false + ); propertyValue.store(invokeFunction(call, resolvedCall, receiverStackValue), v); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index 6f22c43fca6..23044ae8192 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -835,7 +835,9 @@ public class FunctionCodegen { } } - private static String renderByteCodeIfAvailable(MethodVisitor mv) { + @SuppressWarnings("WeakerAccess") // Useful in debug + @Nullable + public static String renderByteCodeIfAvailable(@NotNull MethodVisitor mv) { String bytecode = null; if (mv instanceof TransformationMethodVisitor) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java index 3c3e75c8372..f9b35847729 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java @@ -522,7 +522,8 @@ public abstract class MemberCodegen provideDelegateResolvedCall = bindingContext.get(PROVIDE_DELEGATE_RESOLVED_CALL, propertyDescriptor); if (provideDelegateResolvedCall == null) { @@ -743,9 +744,9 @@ public abstract class MemberCodegen primitiveCastMethods = OperatorConventions.NUMBER_CONVERSIONS.asList(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/LateinitIntrinsics.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/LateinitIntrinsics.kt new file mode 100644 index 00000000000..ce6015f2018 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/LateinitIntrinsics.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.intrinsics + +import org.jetbrains.kotlin.codegen.ExpressionCodegen +import org.jetbrains.kotlin.codegen.StackValue +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.psi.KtCallableReferenceExpression +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.Type + +object LateinitIsInitialized : IntrinsicPropertyGetter() { + override fun generate(resolvedCall: ResolvedCall<*>?, codegen: ExpressionCodegen, returnType: Type, receiver: StackValue): StackValue? { + val value = getStackValue(resolvedCall ?: return null, codegen) ?: return null + return StackValue.compareWithNull(value, Opcodes.IFNULL) + } +} + +private fun getStackValue(resolvedCall: ResolvedCall<*>, codegen: ExpressionCodegen): StackValue? { + val expression = + (resolvedCall.extensionReceiver as? ExpressionReceiver)?.expression as? KtCallableReferenceExpression ?: return null + + // TODO: support properties imported from objects as soon as KT-18982 is fixed + val receiver = expression.receiverExpression?.let(codegen::gen) ?: StackValue.none() + + val target = expression.callableReference.getResolvedCallWithAssert(codegen.bindingContext).resultingDescriptor + return codegen.intermediateValueForProperty(target as PropertyDescriptor, true, false, null, false, receiver, null, true) +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/syntheticAccessorUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/syntheticAccessorUtil.kt index 685bac7a3ff..c8bde4da6b1 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/syntheticAccessorUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/syntheticAccessorUtil.kt @@ -23,6 +23,7 @@ enum class FieldAccessorKind(val suffix: String) { NORMAL("p"), IN_CLASS_COMPANION("cp"), FIELD_FROM_LOCAL("lp"), + LATEINIT_INTRINSIC("li"), } private fun CallableMemberDescriptor.getJvmName() = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 3621fc7d0c6..bb871cc3ca4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -484,6 +484,10 @@ public interface Errors { DiagnosticFactory0 BACKING_FIELD_IN_INTERFACE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory1 INAPPLICABLE_LATEINIT_MODIFIER = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION = DiagnosticFactory0.create(ERROR); + DiagnosticFactory1 LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = DiagnosticFactory2.create(ERROR, ABSTRACT_MODIFIER); 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 9ea3acffe54..9fe678bcdbc 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -227,6 +227,10 @@ public class DefaultErrorMessages { MAP.put(DELEGATED_PROPERTY_IN_INTERFACE, "Delegated properties are not allowed in interfaces"); MAP.put(INAPPLICABLE_LATEINIT_MODIFIER, "''lateinit'' modifier {0}", STRING); + MAP.put(LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL, "This declaration can only be called on a property literal (e.g. 'Foo::bar')"); + MAP.put(LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT, "This declaration can only be called on a reference to a lateinit property"); + MAP.put(LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION, "This declaration can not be used inside an inline function"); + MAP.put(LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY, "Backing field of ''{0}'' is not accessible at this point", COMPACT); MAP.put(GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY, "Getter visibility must be the same as property visibility"); MAP.put(SETTER_VISIBILITY_INCONSISTENT_WITH_PROPERTY_VISIBILITY, "Setter visibility must be the same or less permissive than property visibility"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index 4eddfd97b15..a04be32db72 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -97,7 +97,7 @@ private val DEFAULT_CALL_CHECKERS = listOf( DeprecatedCallChecker, CallReturnsArrayOfNothingChecker(), InfixCallChecker(), OperatorCallChecker(), ConstructorHeaderCallChecker, ProtectedConstructorCallChecker, ApiVersionCallChecker, CoroutineSuspendCallChecker, BuilderFunctionsCallChecker, DslScopeViolationCallChecker, MissingDependencyClassChecker, - CallableReferenceCompatibilityChecker(), + CallableReferenceCompatibilityChecker(), LateinitIntrinsicApplicabilityChecker, UnderscoreUsageChecker ) private val DEFAULT_TYPE_CHECKERS = emptyList() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/LateinitIntrinsicApplicabilityChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/LateinitIntrinsicApplicabilityChecker.kt new file mode 100644 index 00000000000..c0f430ff06f --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/LateinitIntrinsicApplicabilityChecker.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.resolve.calls.checkers + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor +import org.jetbrains.kotlin.diagnostics.Errors.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtCallableReferenceExpression +import org.jetbrains.kotlin.psi.KtPsiUtil +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.OverridingUtil +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver + +object LateinitIntrinsicApplicabilityChecker : CallChecker { + private val ACCESSIBLE_LATEINIT_PROPERTY_LITERAL = FqName("kotlin.internal.AccessibleLateinitPropertyLiteral") + + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { + val descriptor = resolvedCall.resultingDescriptor + + // An optimization + if (descriptor.name.asString() != "isInitialized") return + + // TODO: store "@receiver:..." annotations in ReceiverParameterDescriptor + val annotations = descriptor.extensionReceiverParameter?.value?.type?.annotations?.getUseSiteTargetedAnnotations() ?: return + if (annotations.none { it.annotation.fqName == ACCESSIBLE_LATEINIT_PROPERTY_LITERAL }) return + + val expression = (resolvedCall.extensionReceiver as? ExpressionReceiver)?.expression?.let(KtPsiUtil::safeDeparenthesize) + if (expression !is KtCallableReferenceExpression) { + context.trace.report(LATEINIT_INTRINSIC_CALL_ON_NON_LITERAL.on(reportOn)) + } + else { + val propertyReferenceResolvedCall = expression.callableReference.getResolvedCall(context.trace.bindingContext) ?: return + val referencedProperty = propertyReferenceResolvedCall.resultingDescriptor + if (referencedProperty !is PropertyDescriptor) { + error("Lateinit intrinsic is incorrectly resolved not to a property: $referencedProperty") + } + + if (!referencedProperty.isLateInit) { + context.trace.report(LATEINIT_INTRINSIC_CALL_ON_NON_LATEINIT.on(reportOn)) + } + else if (!isBackingFieldAccessible(referencedProperty, context)) { + context.trace.report(LATEINIT_INTRINSIC_CALL_ON_NON_ACCESSIBLE_PROPERTY.on(reportOn, referencedProperty)) + } + else if ((context.scope.ownerDescriptor as? FunctionDescriptor)?.isInline == true) { + context.trace.report(LATEINIT_INTRINSIC_CALL_IN_INLINE_FUNCTION.on(reportOn)) + } + } + } + + private fun isBackingFieldAccessible(descriptor: PropertyDescriptor, context: CallCheckerContext): Boolean { + // We can generate direct access to the backing field only if the property is defined in the same source file, + // and the property is originally declared in a scope that is a parent of the usage scope + val declaration = + OverridingUtil.filterOutOverridden(OverridingUtil.getOverriddenDeclarations(descriptor)).singleOrNull() ?: return false + val declarationSourceFile = DescriptorToSourceUtils.getContainingFile(declaration) ?: return false + val usageSourceFile = DescriptorToSourceUtils.getContainingFile(context.scope.ownerDescriptor) ?: return false + if (declarationSourceFile != usageSourceFile) return false + + return declaration.containingDeclaration in + generateSequence(context.scope.ownerDescriptor, DeclarationDescriptor::getContainingDeclaration) + } +} diff --git a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt new file mode 100644 index 00000000000..78947e1a378 --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt @@ -0,0 +1,35 @@ +// TARGET_BACKEND: JVM +// LANGUAGE_VERSION: 1.2 +// WITH_RUNTIME + +open class Foo { + lateinit var bar: String + + private lateinit var baz: String + + fun test(): String { + val isBarInitialized: () -> Boolean = { this::bar.isInitialized } + if (isBarInitialized()) return "Fail 1" + bar = "bar" + if (!isBarInitialized()) return "Fail 2" + baz = "baz" + return InnerSubclass().testInner() + } + + inner class InnerSubclass : Foo() { + fun testInner(): String { + // This is access to InnerSubclass.bar which is inherited from Foo.bar + if (this::bar.isInitialized) return "Fail 3" + bar = "OK" + if (!this::bar.isInitialized) return "Fail 4" + + // This is access to Foo.bar declared lexically above + if (!this@Foo::bar.isInitialized) return "Fail 5" + return "OK" + } + } +} + +fun box(): String { + return Foo().test() +} diff --git a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt new file mode 100644 index 00000000000..e6d1e9f8a4c --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt @@ -0,0 +1,21 @@ +// TARGET_BACKEND: JVM +// LANGUAGE_VERSION: 1.2 +// WITH_RUNTIME + +class Foo { + lateinit var bar: String + + fun test(): String { + var state = 0 + if (run { state++; this }::bar.isInitialized) return "Fail 1" + + bar = "A" + if (!run { state++; this }::bar.isInitialized) return "Fail 3" + + return if (state == 2) "OK" else "Fail: state=$state" + } +} + +fun box(): String { + return Foo().test() +} diff --git a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/simpleIsInitialized.kt b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/simpleIsInitialized.kt new file mode 100644 index 00000000000..a91306584e4 --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/simpleIsInitialized.kt @@ -0,0 +1,36 @@ +// TARGET_BACKEND: JVM +// LANGUAGE_VERSION: 1.2 +// WITH_RUNTIME + +// FILE: J.java + +public class J { + public static void deinitialize(Foo foo) { + foo.bar = null; + } +} + +// FILE: main.kt + +class Foo { + lateinit var bar: String + + fun test(): String { + if (this::bar.isInitialized) return "Fail 1" + J.deinitialize(this) + if (this::bar.isInitialized) return "Fail 2" + + bar = "A" + if (!this::bar.isInitialized) return "Fail 3" + J.deinitialize(this) + if (this::bar.isInitialized) return "Fail 4" + + bar = "OK" + if (!this::bar.isInitialized) return "Fail 5" + return bar + } +} + +fun box(): String { + return Foo().test() +} diff --git a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt new file mode 100644 index 00000000000..4be6e0b48bf --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt @@ -0,0 +1,12 @@ +// TARGET_BACKEND: JVM +// LANGUAGE_VERSION: 1.2 +// WITH_RUNTIME + +lateinit var bar: String + +fun box(): String { + if (::bar.isInitialized) return "Fail 1" + bar = "OK" + if (!::bar.isInitialized) return "Fail 2" + return bar +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/lateinit/isInitialized.kt b/compiler/testData/diagnostics/testsWithStdLib/lateinit/isInitialized.kt new file mode 100644 index 00000000000..9264c2b8174 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/lateinit/isInitialized.kt @@ -0,0 +1,88 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE -NOTHING_TO_INLINE +// !API_VERSION: 1.2 +// FILE: test.kt + +interface Base { + var x: String +} + +open class Foo : Base { + override lateinit var x: String + private lateinit var y: String + + var nonLateInit: Int = 1 + + fun ok() { + val b: Boolean = this::x.isInitialized + + val otherInstance = Foo() + otherInstance::x.isInitialized + + (this::x).isInitialized + (@Suppress("ALL") (this::x)).isInitialized + + object { + fun local() { + class Local { + val xx = this@Foo::x.isInitialized + val yy = this@Foo::y.isInitialized + } + } + } + } + + fun onLiteral() { + val p = this::x + p.isInitialized + } + + fun onNonLateinit() { + this::nonLateInit.isInitialized + } + + inline fun inlineFun() { + this::x.isInitialized + + object { + val z = this@Foo::x.isInitialized + } + } + + inner class InnerSubclass : Foo() { + fun innerOk() { + // This is access to Foo.x declared lexically above + this@Foo::x.isInitialized + + // This is access to InnerSubclass.x which is inherited from Foo.x + this::x.isInitialized + } + } +} + +fun onNonAccessible() { + Foo()::x.isInitialized +} + +fun onNonLateinit() { + Foo()::nonLateInit.isInitialized +} + +object Unrelated { + fun onNonAccessible() { + Foo()::x.isInitialized + } +} + +class FooImpl : Foo() { + fun onNonAccessible() { + this::x.isInitialized + } +} + +// FILE: other.kt + +class OtherFooImpl : Foo() { + fun onNonAccessible() { + this::x.isInitialized + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/lateinit/isInitialized.txt b/compiler/testData/diagnostics/testsWithStdLib/lateinit/isInitialized.txt new file mode 100644 index 00000000000..5dac30967cc --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/lateinit/isInitialized.txt @@ -0,0 +1,78 @@ +package + +public fun onNonAccessible(): kotlin.Unit +public fun onNonLateinit(): kotlin.Unit + +public interface Base { + public abstract var x: kotlin.String + 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 Foo : Base { + public constructor Foo() + public final var nonLateInit: kotlin.Int + public open override /*1*/ lateinit var x: kotlin.String + private final lateinit var y: kotlin.String + 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 final inline fun inlineFun(): kotlin.Unit + public final fun ok(): kotlin.Unit + public final fun onLiteral(): kotlin.Unit + public final fun onNonLateinit(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final inner class InnerSubclass : Foo { + public constructor InnerSubclass() + public final override /*1*/ /*fake_override*/ var nonLateInit: kotlin.Int + public open override /*1*/ lateinit /*fake_override*/ var x: kotlin.String + invisible_fake final override /*1*/ lateinit /*fake_override*/ var y: kotlin.String + 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 final inline override /*1*/ /*fake_override*/ fun inlineFun(): kotlin.Unit + public final fun innerOk(): kotlin.Unit + public final override /*1*/ /*fake_override*/ fun ok(): kotlin.Unit + public final override /*1*/ /*fake_override*/ fun onLiteral(): kotlin.Unit + public final override /*1*/ /*fake_override*/ fun onNonLateinit(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public final class FooImpl : Foo { + public constructor FooImpl() + public final override /*1*/ /*fake_override*/ var nonLateInit: kotlin.Int + public open override /*1*/ lateinit /*fake_override*/ var x: kotlin.String + invisible_fake final override /*1*/ lateinit /*fake_override*/ var y: kotlin.String + 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 final inline override /*1*/ /*fake_override*/ fun inlineFun(): kotlin.Unit + public final override /*1*/ /*fake_override*/ fun ok(): kotlin.Unit + public final override /*1*/ /*fake_override*/ fun onLiteral(): kotlin.Unit + public final fun onNonAccessible(): kotlin.Unit + public final override /*1*/ /*fake_override*/ fun onNonLateinit(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class OtherFooImpl : Foo { + public constructor OtherFooImpl() + public final override /*1*/ /*fake_override*/ var nonLateInit: kotlin.Int + public open override /*1*/ lateinit /*fake_override*/ var x: kotlin.String + invisible_fake final override /*1*/ lateinit /*fake_override*/ var y: kotlin.String + 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 final inline override /*1*/ /*fake_override*/ fun inlineFun(): kotlin.Unit + public final override /*1*/ /*fake_override*/ fun ok(): kotlin.Unit + public final override /*1*/ /*fake_override*/ fun onLiteral(): kotlin.Unit + public final fun onNonAccessible(): kotlin.Unit + public final override /*1*/ /*fake_override*/ fun onNonLateinit(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public object Unrelated { + private constructor Unrelated() + 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 final fun onNonAccessible(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 39f93d4f039..1ac75fa94b9 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -13576,6 +13576,39 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class IsInitializedAndDeinitialize extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInIsInitializedAndDeinitialize() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("innerSubclass.kt") + public void testInnerSubclass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt"); + doTest(fileName); + } + + @TestMetadata("sideEffects.kt") + public void testSideEffects() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt"); + doTest(fileName); + } + + @TestMetadata("simpleIsInitialized.kt") + public void testSimpleIsInitialized() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/simpleIsInitialized.kt"); + doTest(fileName); + } + + @TestMetadata("topLevelProperty.kt") + public void testTopLevelProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/properties/lateinit/local") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 42de2143933..b1481a4af0e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -1356,6 +1356,21 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/lateinit") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Lateinit extends AbstractDiagnosticsTestWithStdLib { + public void testAllFilesPresentInLateinit() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/lateinit"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("isInitialized.kt") + public void testIsInitialized() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/lateinit/isInitialized.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/native") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index e8eb40d96a3..fe0be12ead3 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -1356,6 +1356,21 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno } } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/lateinit") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Lateinit extends AbstractDiagnosticsTestWithStdLibUsingJavac { + public void testAllFilesPresentInLateinit() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/lateinit"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("isInitialized.kt") + public void testIsInitialized() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/lateinit/isInitialized.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/native") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index c694233e40e..3f32b889244 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -13576,6 +13576,39 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class IsInitializedAndDeinitialize extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInIsInitializedAndDeinitialize() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("innerSubclass.kt") + public void testInnerSubclass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt"); + doTest(fileName); + } + + @TestMetadata("sideEffects.kt") + public void testSideEffects() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt"); + doTest(fileName); + } + + @TestMetadata("simpleIsInitialized.kt") + public void testSimpleIsInitialized() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/simpleIsInitialized.kt"); + doTest(fileName); + } + + @TestMetadata("topLevelProperty.kt") + public void testTopLevelProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/properties/lateinit/local") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 5e21e39462f..a23c999ccc7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -13576,6 +13576,39 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class IsInitializedAndDeinitialize extends AbstractLightAnalysisModeTest { + public void testAllFilesPresentInIsInitializedAndDeinitialize() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("innerSubclass.kt") + public void testInnerSubclass() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt"); + doTest(fileName); + } + + @TestMetadata("sideEffects.kt") + public void testSideEffects() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt"); + doTest(fileName); + } + + @TestMetadata("simpleIsInitialized.kt") + public void testSimpleIsInitialized() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/simpleIsInitialized.kt"); + doTest(fileName); + } + + @TestMetadata("topLevelProperty.kt") + public void testTopLevelProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/properties/lateinit/local") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 693f91abac1..941d67293c4 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -15052,6 +15052,15 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class IsInitializedAndDeinitialize extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInIsInitializedAndDeinitialize() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + } + @TestMetadata("compiler/testData/codegen/box/properties/lateinit/local") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/libraries/stdlib/src/kotlin/internal/Annotations.kt b/libraries/stdlib/src/kotlin/internal/Annotations.kt index a422b260654..8b7071bedb3 100644 --- a/libraries/stdlib/src/kotlin/internal/Annotations.kt +++ b/libraries/stdlib/src/kotlin/internal/Annotations.kt @@ -64,4 +64,13 @@ internal annotation class InlineOnly */ @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY) @Retention(AnnotationRetention.BINARY) -internal annotation class DynamicExtension \ No newline at end of file +internal annotation class DynamicExtension + +/** + * The value of this parameter should be a property reference expression (`this::foo`), referencing a `lateinit` property, + * the backing field of which is accessible at the point where the corresponding argument is passed. + */ +@Target(AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.BINARY) +@SinceKotlin("1.2") +internal annotation class AccessibleLateinitPropertyLiteral diff --git a/libraries/stdlib/src/kotlin/util/Lateinit.kt b/libraries/stdlib/src/kotlin/util/Lateinit.kt new file mode 100644 index 00000000000..a7f9602b3a6 --- /dev/null +++ b/libraries/stdlib/src/kotlin/util/Lateinit.kt @@ -0,0 +1,19 @@ +@file:kotlin.jvm.JvmName("LateinitKt") +@file:kotlin.jvm.JvmVersion +@file:Suppress("unused") + +package kotlin + +import kotlin.internal.InlineOnly +import kotlin.internal.AccessibleLateinitPropertyLiteral +import kotlin.reflect.KProperty0 + +/** + * Returns `true` if this lateinit property has been assigned a value, and `false` otherwise. + * + * Cannot be used in an inline function, to avoid binary compatibility issues. + */ +@SinceKotlin("1.2") +@InlineOnly +inline val @receiver:AccessibleLateinitPropertyLiteral KProperty0<*>.isInitialized: Boolean + get() = throw NotImplementedError("Implementation is intrinsic")