diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java index fc52e2a080d..31eda1f4dac 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 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. @@ -34,7 +34,9 @@ import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt; import org.jetbrains.org.objectweb.asm.Type; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; public class PackageCodegen { private final GenerationState state; @@ -76,6 +78,12 @@ public class PackageCodegen { } } + private void generateClassesAndObjectsInFile(@NotNull List classOrObjects, @NotNull PackageContext packagePartContext) { + for (KtClassOrObject classOrObject : CodegenUtilKt.sortTopLevelClassesAndPrepareContextForSealedClasses(classOrObjects, packagePartContext, state)) { + generateClassOrObject(classOrObject, packagePartContext); + } + } + private void generateFile(@NotNull KtFile file) { JvmFileClassInfo fileClassInfo = state.getFileClassesProvider().getFileClassInfo(file); @@ -88,6 +96,8 @@ public class PackageCodegen { boolean generatePackagePart = false; + List classOrObjects = new ArrayList(); + for (KtDeclaration declaration : file.getDeclarations()) { if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction) { generatePackagePart = true; @@ -95,7 +105,7 @@ public class PackageCodegen { else if (declaration instanceof KtClassOrObject) { KtClassOrObject classOrObject = (KtClassOrObject) declaration; if (state.getGenerateDeclaredClassFilter().shouldGenerateClass(classOrObject)) { - generateClassOrObject(classOrObject, packagePartContext); + classOrObjects.add(classOrObject); } } else if (declaration instanceof KtScript) { @@ -106,6 +116,7 @@ public class PackageCodegen { } } } + generateClassesAndObjectsInFile(classOrObjects, packagePartContext); if (!generatePackagePart || !state.getGenerateDeclaredClassFilter().shouldGeneratePackagePart(file)) return; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index 5144bba7074..689fc7b5f56 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 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. @@ -18,21 +18,27 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.codegen.context.FieldOwnerContext +import org.jetbrains.kotlin.codegen.context.PackageContext import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.utils.DFS import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter +import java.util.* fun generateIsCheck( v: InstructionAdapter, @@ -125,4 +131,48 @@ fun populateCompanionBackingFieldNamesToOuterContextIfNeeded(companion: KtObject } } +} + +// Top level subclasses of a sealed class should be generated before that sealed class, +// so that we'd generate the necessary accessor for its constructor afterwards +fun sortTopLevelClassesAndPrepareContextForSealedClasses( + classOrObjects: List, + packagePartContext: PackageContext, + state: GenerationState +): List { + fun prepareContextIfNeeded(descriptor: ClassDescriptor?) { + if (DescriptorUtils.isSealedClass(descriptor)) { + // save context for sealed class + packagePartContext.intoClass(descriptor!!, OwnerKind.IMPLEMENTATION, state) + } + } + + // optimization + when (classOrObjects.size) { + 0 -> return emptyList() + 1 -> { + prepareContextIfNeeded(state.bindingContext.get(BindingContext.CLASS, classOrObjects.first())) + return classOrObjects + } + } + + val result = ArrayList(classOrObjects.size) + val descriptorToPsi = LinkedHashMap() + for (classOrObject in classOrObjects) { + val descriptor = state.bindingContext.get(BindingContext.CLASS, classOrObject) + if (descriptor == null) { + result.add(classOrObject) + } + else { + prepareContextIfNeeded(descriptor) + descriptorToPsi[descriptor] = classOrObject + } + } + + // topologicalOrder(listOf(1, 2, 3)) { emptyList() } = listOf(3, 2, 1). Because of this used keys.reversed(). + val sortedDescriptors = DFS.topologicalOrder(descriptorToPsi.keys.reversed()) { + it.typeConstructor.supertypes.map { it.constructor.declarationDescriptor as? ClassDescriptor }.filter { it in descriptorToPsi.keys } + } + sortedDescriptors.mapTo(result) { descriptorToPsi[it]!! } + return result } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java index d72aca11cb5..0a3ac13d535 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 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. @@ -274,15 +274,16 @@ public abstract class CodegenContext { } @NotNull - public ClassContext intoClass(ClassDescriptor descriptor, OwnerKind kind, GenerationState state) { - if (descriptor.isCompanionObject()) { - CodegenContext companionContext = this.findChildContext(descriptor); - if (companionContext != null) { - assert companionContext.getContextKind() == kind : "Kinds should be same, but: " + - companionContext.getContextKind() + "!= " + kind; - return (ClassContext) companionContext; + public ClassContext intoClass(@NotNull ClassDescriptor descriptor, @NotNull OwnerKind kind, @NotNull GenerationState state) { + if (shouldAddChild(descriptor)) { + CodegenContext savedContext = this.findChildContext(descriptor); + if (savedContext != null) { + assert savedContext.getContextKind() == kind : "Kinds should be same, but: " + + savedContext.getContextKind() + "!= " + kind; + return (ClassContext) savedContext; } } + ClassContext classContext = new ClassContext(state.getTypeMapper(), descriptor, kind, this, null); if (descriptor.getCompanionObjectDescriptor() != null) { @@ -571,6 +572,17 @@ public abstract class CodegenContext { descriptorContext = ExpressionCodegen.getParentContextSubclassOf((ClassDescriptor) enclosed, this); } + if (descriptorContext == null && descriptor instanceof ConstructorDescriptor) { + ClassDescriptor classDescriptor = ((ConstructorDescriptor) descriptor).getContainingDeclaration(); + if (DescriptorUtils.isSealedClass(classDescriptor)) { + CodegenContext parentContextForClass = findParentContextWithDescriptor(classDescriptor.getContainingDeclaration()); + if (parentContextForClass != null) { + //generate super constructor calls for top-level sealed classes from top level child + descriptorContext = parentContextForClass.findChildContext(classDescriptor); + } + } + } + if (descriptorContext == null) { return descriptor; } @@ -630,7 +642,7 @@ public abstract class CodegenContext { } private void addChild(@NotNull CodegenContext child) { - if (shouldAddChild(child)) { + if (shouldAddChild(child.contextDescriptor)) { if (childContexts == null) { childContexts = new HashMap(); } @@ -639,8 +651,8 @@ public abstract class CodegenContext { } } - protected boolean shouldAddChild(@NotNull CodegenContext child) { - return DescriptorUtils.isCompanionObject(child.contextDescriptor); + private static boolean shouldAddChild(@NotNull DeclarationDescriptor childContextDescriptor) { + return DescriptorUtils.isCompanionObject(childContextDescriptor) || DescriptorUtils.isSealedClass(childContextDescriptor); } @Nullable diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt index 77c2dccd961..ba409d221f4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 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. @@ -16,29 +16,30 @@ package org.jetbrains.kotlin.cfg -import org.jetbrains.kotlin.psi.* import com.intellij.psi.PsiElement import com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.checkReservedPrefixWord import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeUtils - import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass import org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.isFlexible import java.util.* interface WhenMissingCase { @@ -229,9 +230,8 @@ private object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChec subjectDescriptor: ClassDescriptor?, nullable: Boolean ): List { - assert(subjectDescriptor != null) { "isWhenOnSealedClassExhaustive should be called with not-null subject class descriptor" } - assert(subjectDescriptor!!.modality === Modality.SEALED) { - "isWhenOnSealedClassExhaustive should be called with a sealed class descriptor" + assert(DescriptorUtils.isSealedClass(subjectDescriptor)) { + "isWhenOnSealedClassExhaustive should be called with a sealed class descriptor: $subjectDescriptor" } val memberClassDescriptors = LinkedHashSet() collectNestedSubclasses(subjectDescriptor!!, subjectDescriptor, memberClassDescriptors) @@ -241,21 +241,27 @@ private object WhenOnSealedExhaustivenessChecker : WhenOnClassExhaustivenessChec } override fun isApplicable(subjectType: KotlinType): Boolean { - return TypeUtils.getClassDescriptor(subjectType)?.modality == Modality.SEALED + return DescriptorUtils.isSealedClass(TypeUtils.getClassDescriptor(subjectType)) } private fun collectNestedSubclasses( baseDescriptor: ClassDescriptor, currentDescriptor: ClassDescriptor, - subclasses: MutableSet) { - for (descriptor in DescriptorUtils.getAllDescriptors(currentDescriptor.unsubstitutedInnerClassesScope)) { - if (descriptor is ClassDescriptor) { - if (DescriptorUtils.isDirectSubclass(descriptor, baseDescriptor)) { - subclasses.add(descriptor) + subclasses: MutableSet + ) { + fun collectSubclasses(scope: MemberScope, collectNested: Boolean) { + for (descriptor in scope.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS)) { + if (descriptor is ClassDescriptor) { + if (DescriptorUtils.isDirectSubclass(descriptor, baseDescriptor)) subclasses.add(descriptor) + + if (collectNested) collectNestedSubclasses(baseDescriptor, descriptor, subclasses) } - collectNestedSubclasses(baseDescriptor, descriptor, subclasses) } } + if (currentDescriptor == baseDescriptor && DescriptorUtils.isTopLevelDeclaration(currentDescriptor)) { + collectSubclasses((currentDescriptor.containingDeclaration as PackageFragmentDescriptor).getMemberScope(), collectNested = false) + } + collectSubclasses(currentDescriptor.unsubstitutedInnerClassesScope, collectNested = true) } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java index faf005ff855..8b24914b3af 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BodyResolver.java @@ -55,6 +55,8 @@ import static org.jetbrains.kotlin.resolve.BindingContext.*; import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE; public class BodyResolver { + private static final boolean ALLOW_TOP_LEVEL_SEALED_INHERITANCE = true; + @NotNull private final AnnotationChecker annotationChecker; @NotNull private final ExpressionTypingServices expressionTypingServices; @NotNull private final CallResolver callResolver; @@ -384,18 +386,26 @@ public class BodyResolver { @NotNull private static Set getAllowedFinalSupertypes( @NotNull ClassDescriptor descriptor, - @NotNull KtClassOrObject jetClass + @NotNull Map supertypes, + @NotNull KtClassOrObject ktClassOrObject ) { - Set parentEnumOrSealed; - if (jetClass instanceof KtEnumEntry) { + Set parentEnumOrSealed = Collections.emptySet(); + if (ktClassOrObject instanceof KtEnumEntry) { parentEnumOrSealed = Collections.singleton(((ClassDescriptor) descriptor.getContainingDeclaration()).getTypeConstructor()); } + else if (ALLOW_TOP_LEVEL_SEALED_INHERITANCE && DescriptorUtils.isTopLevelDeclaration(descriptor)) { + for (KotlinType superType : supertypes.values()) { + ClassifierDescriptor classifierDescriptor = superType.getConstructor().getDeclarationDescriptor(); + if (DescriptorUtils.isSealedClass(classifierDescriptor) && DescriptorUtils.isTopLevelDeclaration(classifierDescriptor)) { + parentEnumOrSealed = Collections.singleton(classifierDescriptor.getTypeConstructor()); + } + } + } else { - parentEnumOrSealed = Collections.emptySet(); ClassDescriptor currentDescriptor = descriptor; while (currentDescriptor.getContainingDeclaration() instanceof ClassDescriptor) { currentDescriptor = (ClassDescriptor) currentDescriptor.getContainingDeclaration(); - if (currentDescriptor.getModality() == Modality.SEALED) { + if (DescriptorUtils.isSealedClass(currentDescriptor)) { if (parentEnumOrSealed.isEmpty()) { parentEnumOrSealed = new HashSet(); } @@ -418,9 +428,9 @@ public class BodyResolver { private void checkSupertypeList( @NotNull ClassDescriptor supertypeOwner, @NotNull Map supertypes, - @NotNull KtClassOrObject jetClass + @NotNull KtClassOrObject ktClassOrObject ) { - Set allowedFinalSupertypes = getAllowedFinalSupertypes(supertypeOwner, jetClass); + Set allowedFinalSupertypes = getAllowedFinalSupertypes(supertypeOwner, supertypes, ktClassOrObject); Set typeConstructors = Sets.newHashSet(); boolean classAppeared = false; for (Map.Entry entry : supertypes.entrySet()) { @@ -458,13 +468,13 @@ public class BodyResolver { trace.report(INTERFACE_WITH_SUPERCLASS.on(typeReference)); addSupertype = false; } - else if (jetClass.hasModifier(KtTokens.DATA_KEYWORD)) { + else if (ktClassOrObject.hasModifier(KtTokens.DATA_KEYWORD)) { trace.report(DATA_CLASS_CANNOT_HAVE_CLASS_SUPERTYPES.on(typeReference)); addSupertype = false; } else if (DescriptorUtils.isSubclass(classDescriptor, builtIns.getThrowable()) && !supertypeOwner.getDeclaredTypeParameters().isEmpty()) { - trace.report(GENERIC_THROWABLE_SUBCLASS.on(jetClass.getTypeParameterList())); + trace.report(GENERIC_THROWABLE_SUBCLASS.on(ktClassOrObject.getTypeParameterList())); addSupertype = false; } @@ -492,7 +502,7 @@ public class BodyResolver { } } else if (!allowedFinalSupertypes.contains(constructor)) { - if (classDescriptor.getModality() == Modality.SEALED) { + if (DescriptorUtils.isSealedClass(classDescriptor)) { DeclarationDescriptor containingDescriptor = supertypeOwner.getContainingDeclaration(); while (containingDescriptor != null && containingDescriptor != classDescriptor) { containingDescriptor = containingDescriptor.getContainingDeclaration(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt index 334a392da87..6ebbf8b1b43 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallExpressionResolver.kt @@ -20,7 +20,9 @@ import com.intellij.lang.ASTNode import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType @@ -214,7 +216,7 @@ class CallExpressionResolver( if (DescriptorUtils.isEnumClass(containingDescriptor)) { context.trace.report(ENUM_CLASS_CONSTRUCTOR_CALL.on(callExpression)) } - if (containingDescriptor is ClassDescriptor && containingDescriptor.modality === Modality.SEALED) { + if (DescriptorUtils.isSealedClass(containingDescriptor)) { context.trace.report(SEALED_CLASS_CONSTRUCTOR_CALL.on(callExpression)) } } diff --git a/compiler/testData/codegen/box/classes/sealedInSameFile.kt b/compiler/testData/codegen/box/classes/sealedInSameFile.kt new file mode 100644 index 00000000000..673afef28ef --- /dev/null +++ b/compiler/testData/codegen/box/classes/sealedInSameFile.kt @@ -0,0 +1,38 @@ +class B : A() + +sealed class A() { + constructor(i: Int): this() + + class C: A() +} + +object T : Y() + +class D : A(4) + +class E : A { + constructor(i: Int): super(i) + constructor(): super() +} + +object S : Z() + +sealed class Y : X() + +sealed class Z : Y() + +sealed class X : A() + +class Q : Y() + +fun box() : String { + B() + A.C() + D() + E() + E(4) + T + S + Q() + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/sealed/DerivedTopLevel.kt b/compiler/testData/diagnostics/tests/sealed/DerivedTopLevel.kt new file mode 100644 index 00000000000..f7a662f468b --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/DerivedTopLevel.kt @@ -0,0 +1,10 @@ +sealed class Base + +class Derived: Base() { + class Derived2: Base() +} + +fun test() { + class Local: Base() +} + diff --git a/compiler/testData/diagnostics/tests/sealed/NeverDerived.txt b/compiler/testData/diagnostics/tests/sealed/DerivedTopLevel.txt similarity index 62% rename from compiler/testData/diagnostics/tests/sealed/NeverDerived.txt rename to compiler/testData/diagnostics/tests/sealed/DerivedTopLevel.txt index 6e259fee7a3..8c7dbf3d5aa 100644 --- a/compiler/testData/diagnostics/tests/sealed/NeverDerived.txt +++ b/compiler/testData/diagnostics/tests/sealed/DerivedTopLevel.txt @@ -1,5 +1,7 @@ package +public fun test(): kotlin.Unit + public sealed class Base { private constructor Base() public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean @@ -12,4 +14,11 @@ public final class Derived : Base { public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final class Derived2 : Base { + public constructor Derived2() + 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/testData/diagnostics/tests/sealed/NeverDerived.kt b/compiler/testData/diagnostics/tests/sealed/NeverDerived.kt deleted file mode 100644 index fcb38bcfa18..00000000000 --- a/compiler/testData/diagnostics/tests/sealed/NeverDerived.kt +++ /dev/null @@ -1,7 +0,0 @@ -sealed class Base { - -} - -class Derived: Base() { - -} diff --git a/compiler/testData/diagnostics/tests/sealed/NeverDerivedFromNested.kt b/compiler/testData/diagnostics/tests/sealed/NeverDerivedFromNested.kt new file mode 100644 index 00000000000..f3a378e55a3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NeverDerivedFromNested.kt @@ -0,0 +1,9 @@ +class A { + sealed class Base +} + +class Derived : A.Base() + +fun test() { + class DerivedLocal : A.Base() +} diff --git a/compiler/testData/diagnostics/tests/sealed/NeverDerivedFromNested.txt b/compiler/testData/diagnostics/tests/sealed/NeverDerivedFromNested.txt new file mode 100644 index 00000000000..06081addd8b --- /dev/null +++ b/compiler/testData/diagnostics/tests/sealed/NeverDerivedFromNested.txt @@ -0,0 +1,24 @@ +package + +public fun test(): kotlin.Unit + +public final class A { + public constructor A() + 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 sealed class Base { + private constructor Base() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } +} + +public final class Derived : A.Base { + public constructor Derived() + 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/testData/diagnostics/tests/when/TopLevelSealed.kt b/compiler/testData/diagnostics/tests/when/TopLevelSealed.kt new file mode 100644 index 00000000000..6f75a142283 --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/TopLevelSealed.kt @@ -0,0 +1,22 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +sealed class A { + class B: A() { + class C: A() + } +} + +class D: A() + +fun test(a: A) { + val nonExhaustive = when (a) { + is A.B -> "B" + is A.B.C -> "C" + } + + val exhaustive = when (a) { + is A.B -> "B" + is A.B.C -> "C" + is D -> "D" + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/when/TopLevelSealed.txt b/compiler/testData/diagnostics/tests/when/TopLevelSealed.txt new file mode 100644 index 00000000000..36836d7ba57 --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/TopLevelSealed.txt @@ -0,0 +1,31 @@ +package + +public fun test(/*0*/ a: A): kotlin.Unit + +public sealed class A { + private constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final class B : A { + public constructor B() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + + public final class C : A { + public constructor C() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String + } + } +} + +public final class D : A { + public constructor D() + 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 715cbe64527..2125a385832 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -15657,6 +15657,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/sealed"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("DerivedTopLevel.kt") + public void testDerivedTopLevel() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/DerivedTopLevel.kt"); + doTest(fileName); + } + @TestMetadata("DoubleInner.kt") public void testDoubleInner() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/DoubleInner.kt"); @@ -15735,9 +15741,9 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } - @TestMetadata("NeverDerived.kt") - public void testNeverDerived() throws Exception { - String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/NeverDerived.kt"); + @TestMetadata("NeverDerivedFromNested.kt") + public void testNeverDerivedFromNested() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/sealed/NeverDerivedFromNested.kt"); doTest(fileName); } @@ -19584,6 +19590,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("TopLevelSealed.kt") + public void testTopLevelSealed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/TopLevelSealed.kt"); + doTest(fileName); + } + @TestMetadata("When.kt") public void testWhen() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/when/When.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 97b3b9744db..e0874ff1fca 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -2902,6 +2902,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("sealedInSameFile.kt") + public void testSealedInSameFile() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/sealedInSameFile.kt"); + doTest(fileName); + } + @TestMetadata("selfcreate.kt") public void testSelfcreate() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/selfcreate.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java index 8527dad174c..a305a06b8ec 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 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. @@ -36,12 +36,28 @@ public class Visibilities { return true; } + private boolean inSameFile(@NotNull DeclarationDescriptor what, @NotNull DeclarationDescriptor from) { + SourceFile fromContainingFile = DescriptorUtils.getContainingSourceFile(from); + if (fromContainingFile != SourceFile.NO_SOURCE_FILE) { + return fromContainingFile.equals(DescriptorUtils.getContainingSourceFile(what)); + } + return false; + } + @Override public boolean isVisible(@Nullable ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { if (DescriptorUtils.isTopLevelDeclaration(what)) { - SourceFile fromContainingFile = DescriptorUtils.getContainingSourceFile(from); - if (fromContainingFile != SourceFile.NO_SOURCE_FILE) { - return fromContainingFile.equals(DescriptorUtils.getContainingSourceFile(what)); + return inSameFile(what, from); + } + + if (what instanceof ConstructorDescriptor) { + ClassDescriptor classDescriptor = ((ConstructorDescriptor) what).getContainingDeclaration(); + if (DescriptorUtils.isSealedClass(classDescriptor) + && DescriptorUtils.isTopLevelDeclaration(classDescriptor) + && from instanceof ConstructorDescriptor + && DescriptorUtils.isTopLevelDeclaration(from.getContainingDeclaration()) + && inSameFile(what, from)) { + return true; } } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java index f4e88e98af2..29fa27de045 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.java @@ -129,8 +129,8 @@ public class DescriptorUtils { return getFqNameFromTopLevelClass(containingDeclaration).child(name); } - public static boolean isTopLevelDeclaration(@NotNull DeclarationDescriptor descriptor) { - return descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor; + public static boolean isTopLevelDeclaration(@Nullable DeclarationDescriptor descriptor) { + return descriptor != null && descriptor.getContainingDeclaration() instanceof PackageFragmentDescriptor; } public static boolean isExtension(@NotNull CallableDescriptor descriptor) { @@ -276,6 +276,10 @@ public class DescriptorUtils { return isKindOf(descriptor, ClassKind.OBJECT) && ((ClassDescriptor) descriptor).isCompanionObject(); } + public static boolean isSealedClass(@Nullable DeclarationDescriptor descriptor) { + return isKindOf(descriptor, ClassKind.CLASS) && ((ClassDescriptor) descriptor).getModality() == Modality.SEALED; + } + public static boolean isAnonymousObject(@NotNull DeclarationDescriptor descriptor) { return isClass(descriptor) && descriptor.getName().equals(SpecialNames.NO_NAME_PROVIDED); } @@ -369,7 +373,7 @@ public class DescriptorUtils { @NotNull public static Visibility getDefaultConstructorVisibility(@NotNull ClassDescriptor classDescriptor) { ClassKind classKind = classDescriptor.getKind(); - if (classKind == ClassKind.ENUM_CLASS || classKind.isSingleton() || classDescriptor.getModality() == Modality.SEALED) { + if (classKind == ClassKind.ENUM_CLASS || classKind.isSingleton() || isSealedClass(classDescriptor)) { return Visibilities.PRIVATE; } if (isAnonymousObject(classDescriptor)) { @@ -456,7 +460,7 @@ public class DescriptorUtils { public static boolean classCanHaveAbstractMembers(@NotNull ClassDescriptor classDescriptor) { return classDescriptor.getModality() == Modality.ABSTRACT - || classDescriptor.getModality() == Modality.SEALED + || isSealedClass(classDescriptor) || classDescriptor.getKind() == ClassKind.ENUM_CLASS; } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClassesTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClassesTestGenerated.java index c729c0d6dbc..77f4bf5b732 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClassesTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClassesTestGenerated.java @@ -683,6 +683,12 @@ public class ClassesTestGenerated extends AbstractClassesTest { doTest(fileName); } + @TestMetadata("sealedInSameFile.kt") + public void testSealedInSameFile() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/sealedInSameFile.kt"); + doTest(fileName); + } + @TestMetadata("selfcreate.kt") public void testSelfcreate() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/classes/selfcreate.kt"); diff --git a/plugins/annotation-collector/testData/collectToFile/inheritedComplex/annotations.txt b/plugins/annotation-collector/testData/collectToFile/inheritedComplex/annotations.txt index 9f30e337af5..20de30e0dcb 100644 --- a/plugins/annotation-collector/testData/collectToFile/inheritedComplex/annotations.txt +++ b/plugins/annotation-collector/testData/collectToFile/inheritedComplex/annotations.txt @@ -7,12 +7,12 @@ c 1 0/Ann1 d 0/Ann2 c 0 0/Ann2 c 1 0/Ann2 -d 0/A -a org.test.Ann1 2 -c 2 0/A -d 0/A$DefaultImpls -d 0/B -a org.test.Ann2 3 -c 3 0/B -d 0/C d 0/D +d 0/C +d 0/B +a org.test.Ann2 2 +c 2 0/B +d 0/A +a org.test.Ann1 3 +c 3 0/A +d 0/A$DefaultImpls diff --git a/plugins/annotation-collector/testData/collectToFile/inheritedSimple/annotations.txt b/plugins/annotation-collector/testData/collectToFile/inheritedSimple/annotations.txt index cb693ad85f5..1e5f33cf8ea 100644 --- a/plugins/annotation-collector/testData/collectToFile/inheritedSimple/annotations.txt +++ b/plugins/annotation-collector/testData/collectToFile/inheritedSimple/annotations.txt @@ -4,8 +4,8 @@ a java.lang.annotation.Inherited 0 c 0 0/Ann a java.lang.annotation.Retention 1 c 1 0/Ann +d 0/B d 0/A a org.test.Ann 2 c 2 0/A d 0/A$DefaultImpls -d 0/B diff --git a/plugins/annotation-collector/testData/collectToFile/methodAnnotations/annotations.txt b/plugins/annotation-collector/testData/collectToFile/methodAnnotations/annotations.txt index 605acbe21b8..a7322269e02 100644 --- a/plugins/annotation-collector/testData/collectToFile/methodAnnotations/annotations.txt +++ b/plugins/annotation-collector/testData/collectToFile/methodAnnotations/annotations.txt @@ -2,7 +2,7 @@ a java.lang.annotation.Retention 0 p org.test 0 c 0 0/Ann a org.test.Ann 1 -m 1 0/Parent overridenWithoutAnnotation -m 1 0/Parent notOverriden m 1 0/Child overridenWithAnnotation m 1 0/Child childMethod +m 1 0/Parent overridenWithoutAnnotation +m 1 0/Parent notOverriden