diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilder.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilder.kt index d25c7eb7663..2b1067bdf42 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilder.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilder.kt @@ -48,6 +48,8 @@ interface ControlFlowBuilder { fun declareVariable(property: KtVariableDeclaration) fun declareFunction(subroutine: KtElement, pseudocode: Pseudocode) + fun declareEnumEntry(enumEntry: KtEnumEntry) + // Labels fun createUnboundLabel(): Label diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilderAdapter.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilderAdapter.kt index 36c29ef8048..7e757058a01 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilderAdapter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilderAdapter.kt @@ -200,6 +200,10 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder { delegateBuilder.declareFunction(subroutine, pseudocode) } + override fun declareEnumEntry(enumEntry: KtEnumEntry) { + delegateBuilder.declareEnumEntry(enumEntry) + } + override fun repeatPseudocode(startLabel: Label, finishLabel: Label) { delegateBuilder.repeatPseudocode(startLabel, finishLabel) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt index 676596232fc..778611339d1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt @@ -52,6 +52,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getDispatchReceiverWithSmartCast import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.hasThisOrNoDispatchReceiver +import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils.* import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils @@ -325,8 +326,12 @@ class ControlFlowInformationProvider private constructor( variableDescriptor?.let { varWithUninitializedErrorGenerated.add(it) } } when (variableDescriptor) { - is ValueParameterDescriptor -> report(Errors.UNINITIALIZED_PARAMETER.on(element, variableDescriptor), ctxt) - is VariableDescriptor -> report(Errors.UNINITIALIZED_VARIABLE.on(element, variableDescriptor), ctxt) + is ValueParameterDescriptor -> + report(Errors.UNINITIALIZED_PARAMETER.on(element, variableDescriptor), ctxt) + is FakeCallableDescriptorForObject -> + report(Errors.UNINITIALIZED_ENUM_ENTRY.on(element, variableDescriptor.classDescriptor), ctxt) + is VariableDescriptor -> + report(Errors.UNINITIALIZED_VARIABLE.on(element, variableDescriptor), ctxt) } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index 9acf5d1f91d..091a3eb6b85 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -1267,6 +1267,24 @@ class ControlFlowProcessor(private val trace: BindingTrace) { } override fun visitClass(klass: KtClass) { + if (klass.isEnum()) { + klass.declarations.forEach { + when (it) { + is KtEnumEntry -> { + val classDescriptor = trace[BindingContext.DECLARATION_TO_DESCRIPTOR, it] + if (classDescriptor is ClassDescriptor) { + builder.declareEnumEntry(it) + builder.write(it, it, createSyntheticValue(it, MagicKind.FAKE_INITIALIZER), + AccessTarget.Declaration(FakeCallableDescriptorForObject(classDescriptor)), emptyMap()) + generateInstructions(it) + } + } + is KtObjectDeclaration -> { + generateInstructions(it) + } + } + } + } if (klass.hasPrimaryConstructor()) { processParameters(klass.getPrimaryConstructorParameters()) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt index cf8a58c5156..c0b25ac78a3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariableDataCollector.kt @@ -24,8 +24,11 @@ import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder import org.jetbrains.kotlin.cfg.pseudocodeTraverser.collectData import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingContextUtils +import org.jetbrains.kotlin.resolve.calls.tower.getFakeDescriptorForObject import java.util.ArrayList import java.util.HashMap @@ -74,19 +77,15 @@ class PseudocodeVariableDataCollector( pseudocode.traverse(TraversalOrder.FORWARD, { instruction -> if (instruction is VariableDeclarationInstruction) { val variableDeclarationElement = instruction.variableDeclarationElement - val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement) - if (descriptor != null) { - // TODO: investigate why tests fail without this eager computation here - descriptor.toString() - - assert(descriptor is VariableDescriptor) { - "Variable descriptor should correspond to the instruction for ${instruction.element.text}.\n" + - "Descriptor: $descriptor" - } - blockScopeVariableInfo.registerVariableDeclaredInScope( - descriptor as VariableDescriptor, instruction.blockScope - ) - } + val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement) ?: return@traverse + // TODO: investigate why tests fail without this eager computation here + // TODO: https://youtrack.jetbrains.com/issue/KT-13354 + descriptor.toString() + val variableDescriptor = BindingContextUtils.variableDescriptorForDeclaration(descriptor) + ?: throw AssertionError("Variable or class descriptor should correspond to " + + "the instruction for ${instruction.element.text}.\n" + + "Descriptor: $descriptor") + blockScopeVariableInfo.registerVariableDeclaredInScope(variableDescriptor, instruction.blockScope) } }) return blockScopeVariableInfo diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt index 15b8ac72035..c63b97469c0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt @@ -25,9 +25,15 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingContextUtils +import org.jetbrains.kotlin.resolve.BindingContextUtils.variableDescriptorForDeclaration +import org.jetbrains.kotlin.resolve.calls.tower.getFakeDescriptorForObject +import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import java.util.Collections class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingContext: BindingContext) { @@ -75,9 +81,8 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon if (instruction is VariableDeclarationInstruction) { val variableDeclarationElement = instruction.variableDeclarationElement val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement) - if (descriptor != null) { - assert(descriptor is VariableDescriptor) - declaredVariables.add(descriptor as VariableDescriptor?) + variableDescriptorForDeclaration(descriptor)?.let { + declaredVariables.add(it) } } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt index e22fb3a407a..1dff2d6eb8d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt @@ -274,6 +274,10 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() { add(LocalFunctionDeclarationInstruction(subroutine, pseudocode, currentScope)) } + override fun declareEnumEntry(enumEntry: KtEnumEntry) { + add(VariableDeclarationInstruction(enumEntry, currentScope)) + } + override fun loadUnit(expression: KtExpression) { add(LoadUnitValueInstruction(expression, currentScope)) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/special/VariableDeclarationInstruction.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/special/VariableDeclarationInstruction.kt index 78bf67f9e98..682d9c0d89b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/special/VariableDeclarationInstruction.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/instructions/special/VariableDeclarationInstruction.kt @@ -24,13 +24,16 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl +import org.jetbrains.kotlin.psi.KtEnumEntry class VariableDeclarationInstruction( element: KtDeclaration, blockScope: BlockScope ) : InstructionWithNext(element, blockScope) { init { - assert(element is KtVariableDeclaration || element is KtParameter) { "Invalid element: ${render(element)}}" } + assert(element is KtVariableDeclaration || element is KtParameter || element is KtEnumEntry) { + "Invalid element: ${render(element)}}" + } } val variableDeclarationElement: KtDeclaration diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt index d3ea7aca91f..857a94ff84b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/pseudocodeUtils.kt @@ -171,9 +171,9 @@ fun getExpectedTypePredicate( else { val expectedType = when (accessTarget) { is AccessTarget.Call -> - (accessTarget.resolvedCall.getResultingDescriptor() as? VariableDescriptor)?.getType() + (accessTarget.resolvedCall.resultingDescriptor as? VariableDescriptor)?.type is AccessTarget.Declaration -> - accessTarget.descriptor.getType() + accessTarget.descriptor.type else -> null } @@ -239,7 +239,7 @@ fun getExpectedTypePredicate( fun Instruction.getPrimaryDeclarationDescriptorIfAny(bindingContext: BindingContext): DeclarationDescriptor? { return when (this) { - is CallInstruction -> return resolvedCall.getResultingDescriptor() + is CallInstruction -> return resolvedCall.resultingDescriptor else -> PseudocodeUtil.extractVariableDescriptorIfAny(this, false, bindingContext) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 2e67aeff213..f7febd5f8f6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -644,6 +644,7 @@ public interface Errors { DiagnosticFactory1 UNINITIALIZED_VARIABLE = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 UNINITIALIZED_PARAMETER = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 UNINITIALIZED_ENUM_ENTRY = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 UNUSED_VARIABLE = DiagnosticFactory1.create(WARNING, DECLARATION_NAME); DiagnosticFactory1 UNUSED_PARAMETER = DiagnosticFactory1.create(WARNING, DECLARATION_NAME); 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 109a0371f95..ee0a1fea10d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -264,6 +264,7 @@ public class DefaultErrorMessages { MAP.put(UNINITIALIZED_VARIABLE, "Variable ''{0}'' must be initialized", NAME); MAP.put(UNINITIALIZED_PARAMETER, "Parameter ''{0}'' is uninitialized here", NAME); + MAP.put(UNINITIALIZED_ENUM_ENTRY, "Enum entry ''{0}'' is uninitialized here", NAME); MAP.put(UNUSED_VARIABLE, "Variable ''{0}'' is never used", NAME); MAP.put(UNUSED_PARAMETER, "Parameter ''{0}'' is never used", NAME); MAP.put(ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE, "Variable ''{0}'' is assigned but never accessed", NAME); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java index 9a746c21859..8f60ad51ca5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfoFactory; +import org.jetbrains.kotlin.resolve.calls.tower.TowerLevelsKt; import org.jetbrains.kotlin.resolve.diagnostics.MutableDiagnosticsWithSuppression; import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.TypeUtils; @@ -56,10 +57,21 @@ public class BindingContextUtils { return (VariableDescriptor) resolvedCall.getResultingDescriptor(); } + @Nullable + public static VariableDescriptor variableDescriptorForDeclaration(@Nullable DeclarationDescriptor descriptor) { + if (descriptor instanceof VariableDescriptor) + return (VariableDescriptor) descriptor; + if (descriptor instanceof ClassDescriptor) { + return TowerLevelsKt.getFakeDescriptorForObject((ClassDescriptor) descriptor); + } + return null; + } + @Nullable public static VariableDescriptor extractVariableDescriptorIfAny(@NotNull BindingContext bindingContext, @Nullable KtElement element, boolean onlyReference) { DeclarationDescriptor descriptor = null; - if (!onlyReference && (element instanceof KtVariableDeclaration || element instanceof KtParameter)) { + if (!onlyReference && + (element instanceof KtVariableDeclaration || element instanceof KtParameter || element instanceof KtEnumEntry)) { descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element); } else if (element instanceof KtSimpleNameExpression) { @@ -68,10 +80,7 @@ public class BindingContextUtils { else if (element instanceof KtQualifiedExpression) { descriptor = extractVariableDescriptorIfAny(bindingContext, ((KtQualifiedExpression) element).getSelectorExpression(), onlyReference); } - if (descriptor instanceof VariableDescriptor) { - return (VariableDescriptor) descriptor; - } - return null; + return variableDescriptorForDeclaration(descriptor); } public static void recordFunctionDeclarationToDescriptor(@NotNull BindingTrace trace, diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/FakeCallableDescriptorForObject.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/FakeCallableDescriptorForObject.kt index 321fa39b6b2..a53540cab7f 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/FakeCallableDescriptorForObject.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/util/FakeCallableDescriptorForObject.kt @@ -63,4 +63,8 @@ class FakeCallableDescriptorForObject( override fun getSource(): SourceElement = classDescriptor.source override fun isConst(): Boolean = false + + override fun equals(other: Any?) = other is FakeCallableDescriptorForObject && classDescriptor == other.classDescriptor + + override fun hashCode() = classDescriptor.hashCode() } diff --git a/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.instructions b/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.instructions index 6e532d8d37e..e5a1498f226 100644 --- a/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.instructions +++ b/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.instructions @@ -4,13 +4,25 @@ enum class Direction { } --------------------- L0: - 1 INIT: in: {} out: {} + 1 INIT: in: {} out: {} + v(NORTH,) INIT: in: {} out: {NORTH=D} + magic[FAKE_INITIALIZER](NORTH,) -> INIT: in: {NORTH=D} out: {NORTH=D} + w(NORTH|) INIT: in: {NORTH=D} out: {NORTH=ID} + v(SOUTH,) INIT: in: {NORTH=ID} out: {NORTH=ID, SOUTH=D} + magic[FAKE_INITIALIZER](SOUTH,) -> INIT: in: {NORTH=ID, SOUTH=D} out: {NORTH=ID, SOUTH=D} + w(SOUTH|) INIT: in: {NORTH=ID, SOUTH=D} out: {NORTH=ID, SOUTH=ID} + v(WEST,) INIT: in: {NORTH=ID, SOUTH=ID} out: {NORTH=ID, SOUTH=ID, WEST=D} + magic[FAKE_INITIALIZER](WEST,) -> INIT: in: {NORTH=ID, SOUTH=ID, WEST=D} out: {NORTH=ID, SOUTH=ID, WEST=D} + w(WEST|) INIT: in: {NORTH=ID, SOUTH=ID, WEST=D} out: {NORTH=ID, SOUTH=ID, WEST=ID} + v(EAST) INIT: in: {NORTH=ID, SOUTH=ID, WEST=ID} out: {EAST=D, NORTH=ID, SOUTH=ID, WEST=ID} + magic[FAKE_INITIALIZER](EAST) -> INIT: in: {EAST=D, NORTH=ID, SOUTH=ID, WEST=ID} out: {EAST=D, NORTH=ID, SOUTH=ID, WEST=ID} + w(EAST|) INIT: in: {EAST=D, NORTH=ID, SOUTH=ID, WEST=ID} out: {EAST=ID, NORTH=ID, SOUTH=ID, WEST=ID} L1: - + INIT: in: {EAST=ID, NORTH=ID, SOUTH=ID, WEST=ID} out: {EAST=ID, NORTH=ID, SOUTH=ID, WEST=ID} error: - + INIT: in: {} out: {} sink: - USE: in: {} out: {} + INIT: in: {EAST=ID, NORTH=ID, SOUTH=ID, WEST=ID} out: {EAST=ID, NORTH=ID, SOUTH=ID, WEST=ID} USE: in: {} out: {} ===================== == foo == fun foo(dir: Direction): Int { @@ -28,48 +40,48 @@ L0: 1 INIT: in: {} out: {} v(dir: Direction) INIT: in: {} out: {dir=D} magic[FAKE_INITIALIZER](dir: Direction) -> INIT: in: {dir=D} out: {dir=D} - w(dir|) INIT: in: {dir=D} out: {dir=ID} USE: in: {dir=READ} out: {dir=READ} + w(dir|) INIT: in: {dir=D} out: {dir=ID} USE: in: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, dir=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, dir=READ} 2 mark({ val res: Int when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 } return res }) INIT: in: {dir=ID} out: {dir=ID} v(val res: Int) INIT: in: {dir=ID} out: {dir=ID, res=D} - mark(when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D} USE: in: {dir=READ, res=READ} out: {dir=READ, res=READ} - r(dir) -> USE: in: {res=READ} out: {dir=READ, res=READ} + mark(when (dir) { Direction.NORTH -> res = 1 Direction.SOUTH -> res = 2 Direction.WEST -> res = 3 Direction.EAST -> res = 4 }) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D} USE: in: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, dir=READ, res=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, dir=READ, res=READ} + r(dir) -> USE: in: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, dir=READ, res=READ} mark(Direction.NORTH -> res = 1) mark(Direction.NORTH) - mark(Direction.NORTH) - r(NORTH) -> + mark(Direction.NORTH) USE: in: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, res=READ} + r(NORTH) -> USE: in: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, NORTH=READ, SOUTH=READ, WEST=READ, res=READ} magic[EQUALS_IN_WHEN_CONDITION](Direction.NORTH|, ) -> - jmp?(L4|) USE: in: {res=READ} out: {res=READ} + jmp?(L4|) USE: in: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} L3 ['when' entry body]: r(1) -> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ} w(res|) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ} - jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} + jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ} L4 [next 'when' entry]: mark(Direction.SOUTH -> res = 2) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D} mark(Direction.SOUTH) - mark(Direction.SOUTH) - r(SOUTH) -> + mark(Direction.SOUTH) USE: in: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} out: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} + r(SOUTH) -> USE: in: {EAST=READ, WEST=READ, res=READ} out: {EAST=READ, SOUTH=READ, WEST=READ, res=READ} magic[EQUALS_IN_WHEN_CONDITION](Direction.SOUTH|, ) -> - jmp?(L6|) USE: in: {res=READ} out: {res=READ} + jmp?(L6|) USE: in: {EAST=READ, WEST=READ, res=READ} out: {EAST=READ, WEST=READ, res=READ} L5 ['when' entry body]: r(2) -> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ} w(res|) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ} - jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} + jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ} L6 [next 'when' entry]: mark(Direction.WEST -> res = 3) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D} mark(Direction.WEST) - mark(Direction.WEST) - r(WEST) -> + mark(Direction.WEST) USE: in: {EAST=READ, WEST=READ, res=READ} out: {EAST=READ, WEST=READ, res=READ} + r(WEST) -> USE: in: {EAST=READ, res=READ} out: {EAST=READ, WEST=READ, res=READ} magic[EQUALS_IN_WHEN_CONDITION](Direction.WEST|, ) -> - jmp?(L8|) USE: in: {res=READ} out: {res=READ} + jmp?(L8|) USE: in: {EAST=READ, res=READ} out: {EAST=READ, res=READ} L7 ['when' entry body]: r(3) -> USE: in: {res=WRITTEN_AFTER_READ} out: {res=WRITTEN_AFTER_READ} w(res|) INIT: in: {dir=ID, res=D} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=WRITTEN_AFTER_READ} - jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} + jmp(L2) INIT: in: {dir=ID, res=ID} out: {dir=ID, res=ID} USE: in: {res=READ} out: {res=READ} L8 [next 'when' entry]: mark(Direction.EAST -> res = 4) INIT: in: {dir=ID, res=D} out: {dir=ID, res=D} mark(Direction.EAST) - mark(Direction.EAST) - r(EAST) -> + mark(Direction.EAST) USE: in: {EAST=READ, res=READ} out: {EAST=READ, res=READ} + r(EAST) -> USE: in: {res=READ} out: {EAST=READ, res=READ} magic[EQUALS_IN_WHEN_CONDITION](Direction.EAST|, ) -> jmp?(L10|) USE: in: {res=READ} out: {res=READ} L9 ['when' entry body]: diff --git a/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.values b/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.values index b6d61eddcab..41056bfa8e2 100644 --- a/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.values +++ b/compiler/testData/cfg-variables/basic/ExhaustiveInitialization.values @@ -3,6 +3,10 @@ enum class Direction { NORTH, SOUTH, WEST, EAST } --------------------- + : Direction NEW: magic[FAKE_INITIALIZER](NORTH,) -> + : Direction NEW: magic[FAKE_INITIALIZER](SOUTH,) -> + : Direction NEW: magic[FAKE_INITIALIZER](WEST,) -> + : Direction NEW: magic[FAKE_INITIALIZER](EAST) -> ===================== == foo == fun foo(dir: Direction): Int { diff --git a/compiler/testData/cfg/declarations/classesAndObjects/ObjectEnumQualifiers.instructions b/compiler/testData/cfg/declarations/classesAndObjects/ObjectEnumQualifiers.instructions index c00c6cdc16d..1113535e1cb 100644 --- a/compiler/testData/cfg/declarations/classesAndObjects/ObjectEnumQualifiers.instructions +++ b/compiler/testData/cfg/declarations/classesAndObjects/ObjectEnumQualifiers.instructions @@ -22,9 +22,15 @@ enum class E(val x: Int) { --------------------- L0: 1 + v(E1(0)) + magic[FAKE_INITIALIZER](E1(0)) -> + w(E1|) + r(0) -> + mark((0)) + call((0), |) -> v(val x: Int) - magic[FAKE_INITIALIZER](val x: Int) -> - w(x|) + magic[FAKE_INITIALIZER](val x: Int) -> + w(x|) L1: NEXT:[] error: diff --git a/compiler/testData/cfg/declarations/classesAndObjects/ObjectEnumQualifiers.values b/compiler/testData/cfg/declarations/classesAndObjects/ObjectEnumQualifiers.values index 546e8026cab..c0d97248c62 100644 --- a/compiler/testData/cfg/declarations/classesAndObjects/ObjectEnumQualifiers.values +++ b/compiler/testData/cfg/declarations/classesAndObjects/ObjectEnumQualifiers.values @@ -10,7 +10,10 @@ enum class E(val x: Int) { E1(0) } --------------------- - : Int NEW: magic[FAKE_INITIALIZER](val x: Int) -> + : E NEW: magic[FAKE_INITIALIZER](E1(0)) -> + : Int NEW: magic[FAKE_INITIALIZER](val x: Int) -> +0 : Int NEW: r(0) -> +(0) : * NEW: call((0), |) -> ===================== == C == class C { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/enumInterdependence.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/enumInterdependence.kt new file mode 100644 index 00000000000..bfee3d40374 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/enumInterdependence.kt @@ -0,0 +1,44 @@ +enum class A(val v: A) { + A1(A2), + A2(A1) +} + +enum class B(val x: Int) { + B1(1), + B2(2); + + companion object { + val SUM = B1.x + B2.x + val COPY = B1 + } +} + +enum class C(val x: Int) { + C1(SUM), + C2(1); + + companion object { + val COPY = C2 + val SUM = C1.x + COPY.x + } +} + +enum class D(val x: Int) { + D1(D2.x), + D2(D1.x) +} + +enum class E(val v: Int) { + E1(Nested.COPY); + + object Nested { + val COPY = E1.v + } +} +// From KT-13322 +object Object1 { + val y: Any = Object2.z // z is not yet initialized (?!) + object Object2 { + val z: Any = Object1.y + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/enumInterdependence.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/enumInterdependence.txt new file mode 100644 index 00000000000..bfbf9e13816 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/enumInterdependence.txt @@ -0,0 +1,141 @@ +package + +public final enum class A : kotlin.Enum { + enum entry A1 + + enum entry A2 + + private constructor A(/*0*/ v: A) + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + public final val v: A + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: A): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): A + public final /*synthesized*/ fun values(): kotlin.Array +} + +public final enum class B : kotlin.Enum { + enum entry B1 + + enum entry B2 + + private constructor B(/*0*/ x: kotlin.Int) + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + public final val x: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: B): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public companion object Companion { + private constructor Companion() + public final val COPY: B + public final val SUM: 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 + } + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): B + public final /*synthesized*/ fun values(): kotlin.Array +} + +public final enum class C : kotlin.Enum { + enum entry C1 + + enum entry C2 + + private constructor C(/*0*/ x: kotlin.Int) + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + public final val x: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: C): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public companion object Companion { + private constructor Companion() + public final val COPY: C + public final val SUM: 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 + } + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): C + public final /*synthesized*/ fun values(): kotlin.Array +} + +public final enum class D : kotlin.Enum { + enum entry D1 + + enum entry D2 + + private constructor D(/*0*/ x: kotlin.Int) + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + public final val x: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: D): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): D + public final /*synthesized*/ fun values(): kotlin.Array +} + +public final enum class E : kotlin.Enum { + enum entry E1 + + private constructor E(/*0*/ v: kotlin.Int) + public final override /*1*/ /*fake_override*/ val name: kotlin.String + public final override /*1*/ /*fake_override*/ val ordinal: kotlin.Int + public final val v: kotlin.Int + protected final override /*1*/ /*fake_override*/ fun clone(): kotlin.Any + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: E): kotlin.Int + public final override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public object Nested { + private constructor Nested() + public final val COPY: 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 + } + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): E + public final /*synthesized*/ fun values(): kotlin.Array +} + +public object Object1 { + private constructor Object1() + public final val y: kotlin.Any + 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 object Object2 { + private constructor Object2() + public final val z: kotlin.Any + 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 13ccff5e43c..2ccf89542c7 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3270,6 +3270,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("enumInterdependence.kt") + public void testEnumInterdependence() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/enumInterdependence.kt"); + doTest(fileName); + } + @TestMetadata("infiniteLoops.kt") public void testInfiniteLoops() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.kt");