Do not try to generate 'expect' declarations in multifile classes

Similarly to PackageCodegenImpl#generateFile

 #KT-15754 Fixed
 #KT-17478 Fixed
This commit is contained in:
Alexander Udalov
2017-09-19 17:24:47 +03:00
parent 3f1ee74475
commit 09f36927a5
13 changed files with 70 additions and 22 deletions
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
@@ -161,4 +162,8 @@ object CodegenUtil {
(1..arity).joinToString(prefix = "callableReferenceFakeCall(", separator = ", ", postfix = ")") { "p$it" } (1..arity).joinToString(prefix = "callableReferenceFakeCall(", separator = ", ", postfix = ")") { "p$it" }
return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall) as KtCallExpression return KtPsiFactory(project, markGenerated = false).createExpression(fakeFunctionCall) as KtCallExpression
} }
@JvmStatic
fun getActualDeclarations(file: KtFile): List<KtDeclaration> =
file.declarations.filterNot(KtDeclaration::hasExpectModifier)
} }
@@ -103,9 +103,7 @@ import java.util.*;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt;
import static org.jetbrains.kotlin.codegen.AsmUtil.*; import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.extractReificationArgument; import static org.jetbrains.kotlin.codegen.CodegenUtilKt.*;
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isPossiblyUninitializedSingleton;
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.unwrapInitialSignatureDescriptor;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.*; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.*;
@@ -1611,7 +1609,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return null; return null;
} }
public void returnExpression(KtExpression expr) { public void returnExpression(@NotNull KtExpression expr) {
boolean isBlockedNamedFunction = expr instanceof KtBlockExpression && expr.getParent() instanceof KtNamedFunction; boolean isBlockedNamedFunction = expr instanceof KtBlockExpression && expr.getParent() instanceof KtNamedFunction;
FunctionDescriptor originalSuspendLambdaDescriptor = getOriginalSuspendLambdaDescriptorFromContext(context); FunctionDescriptor originalSuspendLambdaDescriptor = getOriginalSuspendLambdaDescriptorFromContext(context);
@@ -1644,7 +1642,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
} }
} }
private static boolean endsWithReturn(KtElement bodyExpression) { private static boolean endsWithReturn(@NotNull KtElement bodyExpression) {
if (bodyExpression instanceof KtBlockExpression) { if (bodyExpression instanceof KtBlockExpression) {
List<KtExpression> statements = ((KtBlockExpression) bodyExpression).getStatements(); List<KtExpression> statements = ((KtBlockExpression) bodyExpression).getStatements();
return statements.size() > 0 && statements.get(statements.size() - 1) instanceof KtReturnExpression; return statements.size() > 0 && statements.get(statements.size() - 1) instanceof KtReturnExpression;
@@ -1653,7 +1651,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return bodyExpression instanceof KtReturnExpression; return bodyExpression instanceof KtReturnExpression;
} }
private static boolean isLambdaVoidBody(KtElement bodyExpression, Type returnType) { private static boolean isLambdaVoidBody(@NotNull KtElement bodyExpression, @NotNull Type returnType) {
if (bodyExpression instanceof KtBlockExpression) { if (bodyExpression instanceof KtBlockExpression) {
PsiElement parent = bodyExpression.getParent(); PsiElement parent = bodyExpression.getParent();
if (parent instanceof KtFunctionLiteral) { if (parent instanceof KtFunctionLiteral) {
@@ -20,6 +20,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.codegen.context.MethodContext; import org.jetbrains.kotlin.codegen.context.MethodContext;
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.psi.KtDeclarationWithBody; import org.jetbrains.kotlin.psi.KtDeclarationWithBody;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
import org.jetbrains.org.objectweb.asm.MethodVisitor; import org.jetbrains.org.objectweb.asm.MethodVisitor;
@@ -49,7 +51,9 @@ public abstract class FunctionGenerationStrategy {
@Override @Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) { public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
codegen.returnExpression(declaration.getBodyExpression()); KtExpression bodyExpression = declaration.getBodyExpression();
assert bodyExpression != null : "Function has no body: " + PsiUtilsKt.getElementTextWithContext(declaration);
codegen.returnExpression(bodyExpression);
} }
} }
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.util.ArrayUtil import com.intellij.util.ArrayUtil
import com.intellij.util.SmartList import com.intellij.util.SmartList
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
import org.jetbrains.kotlin.codegen.context.MethodContext import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
@@ -241,7 +242,7 @@ class MultifileClassCodegenImpl(
private fun addDelegateGenerationTasksForDeclarationsInFile(file: KtFile, packageFragment: PackageFragmentDescriptor, partType: Type) { private fun addDelegateGenerationTasksForDeclarationsInFile(file: KtFile, packageFragment: PackageFragmentDescriptor, partType: Type) {
val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType) val facadeContext = state.rootContext.intoMultifileClass(packageFragment, facadeClassType, partType)
val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext) val memberCodegen = createCodegenForDelegatesInMultifileFacade(facadeContext)
for (declaration in file.declarations) { for (declaration in CodegenUtil.getActualDeclarations(file)) {
if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) { if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) {
val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) val descriptor = state.bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration)
if (descriptor !is MemberDescriptor) { if (descriptor !is MemberDescriptor) {
@@ -367,7 +368,7 @@ class MultifileClassCodegenImpl(
} }
private fun KtFile.hasDeclarationsForPartClass() = private fun KtFile.hasDeclarationsForPartClass() =
declarations.any { it is KtProperty || it is KtFunction } CodegenUtil.getActualDeclarations(this).any { it is KtProperty || it is KtFunction }
private fun getCompiledPackageFragment( private fun getCompiledPackageFragment(
facadeFqName: FqName, state: GenerationState facadeFqName: FqName, state: GenerationState
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen package org.jetbrains.kotlin.codegen
import com.intellij.util.ArrayUtil import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.codegen.context.MultifileClassPartContext import org.jetbrains.kotlin.codegen.context.MultifileClassPartContext
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
@@ -135,7 +136,7 @@ class MultifileClassPartCodegen(
} }
override fun generateBody() { override fun generateBody() {
for (declaration in element.declarations) { for (declaration in CodegenUtil.getActualDeclarations(element)) {
if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) { if (declaration is KtNamedFunction || declaration is KtProperty || declaration is KtTypeAlias) {
genSimpleMember(declaration) genSimpleMember(declaration)
} }
@@ -162,7 +163,7 @@ class MultifileClassPartCodegen(
override fun generateKotlinMetadataAnnotation() { override fun generateKotlinMetadataAnnotation() {
val members = ArrayList<DeclarationDescriptor>() val members = ArrayList<DeclarationDescriptor>()
for (declaration in element.declarations) { for (declaration in CodegenUtil.getActualDeclarations(element)) {
when (declaration) { when (declaration) {
is KtNamedFunction -> { is KtNamedFunction -> {
val functionDescriptor = bindingContext.get(BindingContext.FUNCTION, declaration) val functionDescriptor = bindingContext.get(BindingContext.FUNCTION, declaration)
@@ -22,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.SmartList; import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.codegen.context.PackageContext; import org.jetbrains.kotlin.codegen.context.PackageContext;
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
@@ -31,7 +32,6 @@ import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus; import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.Type;
@@ -98,9 +98,7 @@ public class PackageCodegenImpl implements PackageCodegen {
List<KtClassOrObject> classOrObjects = new ArrayList<>(); List<KtClassOrObject> classOrObjects = new ArrayList<>();
for (KtDeclaration declaration : file.getDeclarations()) { for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(file)) {
if (PsiUtilsKt.hasExpectModifier(declaration)) continue;
if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) { if (declaration instanceof KtProperty || declaration instanceof KtNamedFunction || declaration instanceof KtTypeAlias) {
generatePackagePart = true; generatePackagePart = true;
} }
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen;
import com.intellij.util.ArrayUtil; import com.intellij.util.ArrayUtil;
import kotlin.Unit; import kotlin.Unit;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.codegen.annotation.AnnotatedSimple; import org.jetbrains.kotlin.codegen.annotation.AnnotatedSimple;
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext; import org.jetbrains.kotlin.codegen.context.FieldOwnerContext;
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension; import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension;
@@ -32,7 +33,6 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl; import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl;
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader; import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.serialization.DescriptorSerializer; import org.jetbrains.kotlin.serialization.DescriptorSerializer;
import org.jetbrains.kotlin.serialization.ProtoBuf; import org.jetbrains.kotlin.serialization.ProtoBuf;
@@ -88,9 +88,7 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
@Override @Override
protected void generateBody() { protected void generateBody() {
for (KtDeclaration declaration : element.getDeclarations()) { for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(element)) {
if (PsiUtilsKt.hasExpectModifier(declaration)) continue;
if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty || declaration instanceof KtTypeAlias) { if (declaration instanceof KtNamedFunction || declaration instanceof KtProperty || declaration instanceof KtTypeAlias) {
genSimpleMember(declaration); genSimpleMember(declaration);
} }
@@ -104,7 +102,7 @@ public class PackagePartCodegen extends MemberCodegen<KtFile> {
@Override @Override
protected void generateKotlinMetadataAnnotation() { protected void generateKotlinMetadataAnnotation() {
List<DeclarationDescriptor> members = new ArrayList<>(); List<DeclarationDescriptor> members = new ArrayList<>();
for (KtDeclaration declaration : element.getDeclarations()) { for (KtDeclaration declaration : CodegenUtil.getActualDeclarations(element)) {
if (declaration instanceof KtNamedFunction) { if (declaration instanceof KtNamedFunction) {
SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, declaration); SimpleFunctionDescriptor functionDescriptor = bindingContext.get(BindingContext.FUNCTION, declaration);
members.add(functionDescriptor); members.add(functionDescriptor);
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -70,6 +71,6 @@ class SuspendFunctionGenerationStrategy(
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) { override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
this.codegen = codegen this.codegen = codegen
codegen.returnExpression(declaration.bodyExpression) codegen.returnExpression(declaration.bodyExpression ?: error("Function has no body: " + declaration.getElementTextWithContext()))
} }
} }
@@ -0,0 +1,6 @@
// WITH_RUNTIME
@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("CommonKt")
expect fun foo()
@@ -0,0 +1,5 @@
actual fun foo() {}
fun test() {
foo()
}
@@ -0,0 +1,7 @@
-- Common --
Exit code: OK
Output:
-- JVM --
Exit code: OK
Output:
@@ -26,6 +26,9 @@ import org.jetbrains.kotlin.test.KotlinTestUtils
import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndAddNewlineAtEOF import org.jetbrains.kotlin.test.util.trimTrailingWhitespacesAndAddNewlineAtEOF
import java.io.File import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() { abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() {
fun doTest(directoryPath: String) { fun doTest(directoryPath: String) {
@@ -38,6 +41,11 @@ abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() {
val tmpdir = KotlinTestUtils.tmpDir(getTestName(true)) val tmpdir = KotlinTestUtils.tmpDir(getTestName(true))
val optionalStdlibCommon =
if (InTextDirectivesUtils.isDirectiveDefined(commonSrc.readText(), "WITH_RUNTIME"))
arrayOf("-cp", findStdlibCommon().absolutePath)
else emptyArray()
val commonDest = File(tmpdir, "common").absolutePath val commonDest = File(tmpdir, "common").absolutePath
val jvmDest = File(tmpdir, "jvm").absolutePath val jvmDest = File(tmpdir, "jvm").absolutePath
val jsDest = File(File(tmpdir, "js"), "output.js").absolutePath val jsDest = File(File(tmpdir, "js"), "output.js").absolutePath
@@ -45,7 +53,7 @@ abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() {
val result = buildString { val result = buildString {
appendln("-- Common --") appendln("-- Common --")
appendln(K2MetadataCompiler().compile(listOf(commonSrc), "-d", commonDest)) appendln(K2MetadataCompiler().compile(listOf(commonSrc), "-d", commonDest, *optionalStdlibCommon))
if (jvmSrc.exists()) { if (jvmSrc.exists()) {
appendln() appendln()
@@ -69,6 +77,16 @@ abstract class AbstractMultiPlatformIntegrationTest : KtUsefulTestCase() {
KotlinTestUtils.assertEqualsToFile(File(root, "output.txt"), result.replace('\\', '/')) KotlinTestUtils.assertEqualsToFile(File(root, "output.txt"), result.replace('\\', '/'))
} }
private fun findStdlibCommon(): File {
// Take kotlin-stdlib-common.jar from dist/ when it's there
val stdlibCommonLibsDir = "libraries/stdlib/common/build/libs"
val commonLibs = Files.newDirectoryStream(Paths.get(stdlibCommonLibsDir)).use(Iterable<Path>::toList)
return commonLibs.sorted().findLast {
val name = it.toFile().name
!name.endsWith("-javadoc.jar") && !name.endsWith("-sources.jar")
}?.toFile() ?: error("kotlin-stdlib-common is not found in $stdlibCommonLibsDir")
}
private fun CLICompiler<*>.compileBothWays(commonSource: File, platformSource: File, vararg mainArguments: String): String { private fun CLICompiler<*>.compileBothWays(commonSource: File, platformSource: File, vararg mainArguments: String): String {
val configurations = listOf( val configurations = listOf(
listOf(platformSource, commonSource), listOf(platformSource, commonSource),
@@ -102,6 +102,12 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform
doTest(fileName); doTest(fileName);
} }
@TestMetadata("jvmMultifileClass")
public void testJvmMultifileClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/jvmMultifileClass/");
doTest(fileName);
}
@TestMetadata("missingOverload") @TestMetadata("missingOverload")
public void testMissingOverload() throws Exception { public void testMissingOverload() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/missingOverload/"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/multiplatform/missingOverload/");