Resolve lateinit variables

Do not report UNINITIALIZED_VARIABLE on lateinit variables
Provide delegating constructors for descriptors for compatibility.
This commit is contained in:
Dmitry Petrov
2017-05-31 13:52:07 +03:00
parent f88cd5ed3d
commit 3bae430d49
19 changed files with 86 additions and 13 deletions
@@ -396,8 +396,6 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
Annotations.Companion.getEMPTY(), Annotations.Companion.getEMPTY(),
Name.identifier(variableDescriptor.getName().asString() + "$metadata"), Name.identifier(variableDescriptor.getName().asString() + "$metadata"),
ReflectionTypes.Companion.createKPropertyStarType(DescriptorUtilsKt.getModule(variableDescriptor)), ReflectionTypes.Companion.createKPropertyStarType(DescriptorUtilsKt.getModule(variableDescriptor)),
false,
false,
SourceElement.NO_SOURCE SourceElement.NO_SOURCE
); );
bindingTrace.record(LOCAL_VARIABLE_PROPERTY_METADATA, variableDescriptor, metadataVariableDescriptor); bindingTrace.record(LOCAL_VARIABLE_PROPERTY_METADATA, variableDescriptor, metadataVariableDescriptor);
@@ -354,7 +354,9 @@ class ControlFlowInformationProvider private constructor(
} }
} }
is VariableDescriptor -> is VariableDescriptor ->
report(Errors.UNINITIALIZED_VARIABLE.on(element, variableDescriptor), ctxt) if (!variableDescriptor.isLateInit) {
report(Errors.UNINITIALIZED_VARIABLE.on(element, variableDescriptor), ctxt)
}
} }
} }
} }
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.types.TypeSubstitutor;
public class LocalVariableDescriptor extends VariableDescriptorWithInitializerImpl implements VariableDescriptorWithAccessors { public class LocalVariableDescriptor extends VariableDescriptorWithInitializerImpl implements VariableDescriptorWithAccessors {
private final boolean isDelegated; private final boolean isDelegated;
private final boolean isLateInit;
private LocalVariableAccessorDescriptor.Getter getter; private LocalVariableAccessorDescriptor.Getter getter;
private LocalVariableAccessorDescriptor.Setter setter; private LocalVariableAccessorDescriptor.Setter setter;
@@ -36,10 +37,34 @@ public class LocalVariableDescriptor extends VariableDescriptorWithInitializerIm
@Nullable KotlinType type, @Nullable KotlinType type,
boolean mutable, boolean mutable,
boolean isDelegated, boolean isDelegated,
boolean isLateInit,
@NotNull SourceElement source @NotNull SourceElement source
) { ) {
super(containingDeclaration, annotations, name, type, mutable, source); super(containingDeclaration, annotations, name, type, mutable, source);
this.isDelegated = isDelegated; this.isDelegated = isDelegated;
this.isLateInit = isLateInit;
}
public LocalVariableDescriptor(
@NotNull DeclarationDescriptor containingDeclaration,
@NotNull Annotations annotations,
@NotNull Name name,
@Nullable KotlinType type,
boolean mutable,
boolean isDelegated,
@NotNull SourceElement source
) {
this(containingDeclaration, annotations, name, type, mutable, isDelegated, false, source);
}
public LocalVariableDescriptor(
@NotNull DeclarationDescriptor containingDeclaration,
@NotNull Annotations annotations,
@NotNull Name name,
@Nullable KotlinType type,
@NotNull SourceElement source
) {
this(containingDeclaration, annotations, name, type, false, false, false, source);
} }
@Override @Override
@@ -89,4 +114,9 @@ public class LocalVariableDescriptor extends VariableDescriptorWithInitializerIm
public boolean isDelegated() { public boolean isDelegated() {
return isDelegated; return isDelegated;
} }
@Override
public boolean isLateInit() {
return isLateInit;
}
} }
@@ -29,7 +29,8 @@ class SyntheticFieldDescriptor private constructor(
accessorDescriptor: PropertyAccessorDescriptor, accessorDescriptor: PropertyAccessorDescriptor,
property: KtProperty property: KtProperty
): LocalVariableDescriptor(accessorDescriptor, Annotations.EMPTY, SyntheticFieldDescriptor.NAME, ): LocalVariableDescriptor(accessorDescriptor, Annotations.EMPTY, SyntheticFieldDescriptor.NAME,
propertyDescriptor.type, propertyDescriptor.isVar, false, property.toSourceElement()) { propertyDescriptor.type, propertyDescriptor.isVar, false, false,
property.toSourceElement()) {
constructor(accessorDescriptor: PropertyAccessorDescriptor, constructor(accessorDescriptor: PropertyAccessorDescriptor,
property: KtProperty): this(accessorDescriptor.correspondingProperty, accessorDescriptor, property) property: KtProperty): this(accessorDescriptor.correspondingProperty, accessorDescriptor, property)
@@ -675,8 +675,6 @@ public class DescriptorResolver {
annotationResolver.resolveAnnotationsWithArguments(scope, parameter.getModifierList(), trace), annotationResolver.resolveAnnotationsWithArguments(scope, parameter.getModifierList(), trace),
KtPsiUtil.safeName(parameter.getName()), KtPsiUtil.safeName(parameter.getName()),
approximatedType, approximatedType,
false,
false,
KotlinSourceElementKt.toSourceElement(parameter) KotlinSourceElementKt.toSourceElement(parameter)
); );
trace.record(BindingContext.VALUE_PARAMETER, parameter, variableDescriptor); trace.record(BindingContext.VALUE_PARAMETER, parameter, variableDescriptor);
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.* import org.jetbrains.kotlin.descriptors.impl.*
import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.KtPsiUtil import org.jetbrains.kotlin.psi.KtPsiUtil
@@ -195,6 +196,7 @@ class LocalVariableResolver(
trace: BindingTrace trace: BindingTrace
): LocalVariableDescriptor { ): LocalVariableDescriptor {
val hasDelegate = variable is KtProperty && variable.hasDelegate() val hasDelegate = variable is KtProperty && variable.hasDelegate()
val hasLateinit = variable.hasModifier(KtTokens.LATEINIT_KEYWORD)
val variableDescriptor = LocalVariableDescriptor( val variableDescriptor = LocalVariableDescriptor(
scope.ownerDescriptor, scope.ownerDescriptor,
annotationResolver.resolveAnnotationsWithArguments(scope, variable.modifierList, trace), annotationResolver.resolveAnnotationsWithArguments(scope, variable.modifierList, trace),
@@ -207,6 +209,7 @@ class LocalVariableResolver(
type, type,
variable.isVar, variable.isVar,
hasDelegate, hasDelegate,
hasLateinit,
variable.toSourceElement() variable.toSourceElement()
) )
trace.record(BindingContext.VARIABLE, variable, variableDescriptor) trace.record(BindingContext.VARIABLE, variable, variableDescriptor)
@@ -275,6 +275,8 @@ class TypeResolver(
override fun isVar() = false override fun isVar() = false
override fun isLateInit() = false
override fun getCompileTimeInitializer() = null override fun getCompileTimeInitializer() = null
override fun <R : Any?, D : Any?> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R { override fun <R : Any?, D : Any?> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R {
@@ -592,8 +592,8 @@ class DoubleColonExpressionResolver(
internal fun bindPropertyReference(expression: KtCallableReferenceExpression, referenceType: KotlinType, context: ResolutionContext<*>) { internal fun bindPropertyReference(expression: KtCallableReferenceExpression, referenceType: KotlinType, context: ResolutionContext<*>) {
val localVariable = LocalVariableDescriptor( val localVariable = LocalVariableDescriptor(
context.scope.ownerDescriptor, Annotations.EMPTY, Name.special("<anonymous>"), referenceType, /* mutable = */ false, context.scope.ownerDescriptor, Annotations.EMPTY, Name.special("<anonymous>"), referenceType,
/* isDelegated = */ false, expression.toSourceElement() expression.toSourceElement()
) )
context.trace.record(BindingContext.VARIABLE, expression, localVariable) context.trace.record(BindingContext.VARIABLE, expression, localVariable)
@@ -111,7 +111,7 @@ class JvmSharedVariablesManager(val builtIns: KotlinBuiltIns) : SharedVariablesM
LocalVariableDescriptor( LocalVariableDescriptor(
variableDescriptor.containingDeclaration, variableDescriptor.annotations, variableDescriptor.name, variableDescriptor.containingDeclaration, variableDescriptor.annotations, variableDescriptor.name,
getSharedVariableType(variableDescriptor.type), getSharedVariableType(variableDescriptor.type),
false, false, variableDescriptor.source false, false, variableDescriptor.isLateInit, variableDescriptor.source
) )
override fun defineSharedValue(sharedVariableDescriptor: VariableDescriptor, originalDeclaration: IrVariable): IrStatement { override fun defineSharedValue(sharedVariableDescriptor: VariableDescriptor, originalDeclaration: IrVariable): IrStatement {
@@ -136,6 +136,7 @@ class IrLocalDelegatedPropertyDelegateDescriptorImpl(
override fun getCompileTimeInitializer(): ConstantValue<*>? = null override fun getCompileTimeInitializer(): ConstantValue<*>? = null
override fun isVar(): Boolean = false override fun isVar(): Boolean = false
override fun isLateInit(): Boolean = false
override fun substitute(substitutor: TypeSubstitutor): VariableDescriptor? = throw UnsupportedOperationException() override fun substitute(substitutor: TypeSubstitutor): VariableDescriptor? = throw UnsupportedOperationException()
override fun getVisibility(): Visibility = Visibilities.LOCAL override fun getVisibility(): Visibility = Visibilities.LOCAL
@@ -44,6 +44,8 @@ class IrTemporaryVariableDescriptorImpl(
override fun isVar(): Boolean = isMutable override fun isVar(): Boolean = isMutable
override fun isLateInit(): Boolean = false
override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R = override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D): R =
visitor.visitVariableDescriptor(this, data) visitor.visitVariableDescriptor(this, data)
} }
@@ -67,6 +67,8 @@ open class FakeCallableDescriptorForObject(
override fun isConst(): Boolean = false override fun isConst(): Boolean = false
override fun isLateInit(): Boolean = false
override fun equals(other: Any?) = other is FakeCallableDescriptorForObject && classDescriptor == other.classDescriptor override fun equals(other: Any?) = other is FakeCallableDescriptorForObject && classDescriptor == other.classDescriptor
override fun hashCode() = classDescriptor.hashCode() override fun hashCode() = classDescriptor.hashCode()
@@ -0,0 +1,22 @@
fun test1() {
lateinit var s: String
s.length
}
fun test2() {
lateinit var s: String
run {
s = ""
}
s.length
}
fun almostAlwaysTrue(): Boolean = true
fun test3() {
lateinit var s: String
if (almostAlwaysTrue()) {
s = ""
}
s.length
}
@@ -0,0 +1,6 @@
package
public fun almostAlwaysTrue(): kotlin.Boolean
public fun test1(): kotlin.Unit
public fun test2(): kotlin.Unit
public fun test3(): kotlin.Unit
@@ -16268,6 +16268,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/properties/localLateinit/localLateinit.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/properties/localLateinit/localLateinit.kt");
doTest(fileName); doTest(fileName);
} }
@TestMetadata("uninitialized.kt")
public void testUninitialized() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/properties/localLateinit/uninitialized.kt");
doTest(fileName);
}
} }
} }
@@ -57,8 +57,6 @@ public interface PropertyDescriptor extends VariableDescriptorWithAccessors, Cal
@Override @Override
PropertyDescriptor substitute(@NotNull TypeSubstitutor substitutor); PropertyDescriptor substitute(@NotNull TypeSubstitutor substitutor);
boolean isLateInit();
@NotNull @NotNull
@Override @Override
CopyBuilder<? extends PropertyDescriptor> newCopyBuilder(); CopyBuilder<? extends PropertyDescriptor> newCopyBuilder();
@@ -54,4 +54,6 @@ interface ValueParameterDescriptor : VariableDescriptor, ParameterDescriptor {
val isCrossinline: Boolean val isCrossinline: Boolean
val isNoinline: Boolean val isNoinline: Boolean
override fun isLateInit(): Boolean = false
} }
@@ -36,4 +36,6 @@ public interface VariableDescriptor extends ValueDescriptor {
* It completely does not means that if isConst then `getCompileTimeInitializer` is not null * It completely does not means that if isConst then `getCompileTimeInitializer` is not null
*/ */
boolean isConst(); boolean isConst();
boolean isLateInit();
} }
@@ -121,8 +121,6 @@ public class TranslationContext {
Annotations.Companion.getEMPTY(), Annotations.Companion.getEMPTY(),
Name.identifier("continuation"), Name.identifier("continuation"),
continuationDescriptor.getDefaultType(), continuationDescriptor.getDefaultType(),
/* mutable = */ false,
/* delegated = */ false,
SourceElement.NO_SOURCE); SourceElement.NO_SOURCE);
} }
} }