dokka icon indicating copy to clipboard operation
dokka copied to clipboard

StackOverflowError in dokkaHtml for a multiplatform project when using actual typealiases

Open karelp opened this issue 2 years ago • 2 comments

Describe the bug I’m using Dokka 1.6.0 in a Kotlin multiplatform project and it crashes with the following exception:

Execution failed for task ':dokkaHtml'.
> java.lang.StackOverflowError (no error message)

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':dokkaHtml'.
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:188)
	at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:282)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:186)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:174)
	at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:109)
	at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
	at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
	at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:79)
	at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:79)
	at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
	at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:402)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:389)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:382)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:368)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
	at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:61)
Caused by: java.lang.StackOverflowError
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:384)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyTypeAliasDescriptor.getUnderlyingType(LazyTypeAliasDescriptor.kt:54)
	at org.jetbrains.kotlin.descriptors.impl.AbstractTypeAliasDescriptor$typeConstructor$1.getSupertypes(AbstractTypeAliasDescriptor.kt:108)
	at org.jetbrains.kotlin.descriptors.impl.AbstractTypeAliasDescriptor$typeConstructor$1.getSupertypes(AbstractTypeAliasDescriptor.kt:108)
	at org.jetbrains.kotlin.descriptors.impl.AbstractTypeAliasDescriptor$typeConstructor$1.getSupertypes(AbstractTypeAliasDescriptor.kt:108)
	at org.jetbrains.kotlin.descriptors.impl.AbstractTypeAliasDescriptor$typeConstructor$1.getSupertypes(AbstractTypeAliasDescriptor.kt:108)
	at org.jetbrains.kotlin.descriptors.impl.AbstractTypeAliasDescriptor$typeConstructor$1.getSupertypes(AbstractTypeAliasDescriptor.kt:108)
	at org.jetbrains.kotlin.descriptors.impl.AbstractTypeAliasDescriptor$typeConstructor$1.getSupertypes(AbstractTypeAliasDescriptor.kt:108)
	at org.jetbrains.kotlin.descriptors.impl.AbstractTypeAliasDescriptor$typeConstructor$1.getSupertypes(AbstractTypeAliasDescriptor.kt:108)
	at org.jetbrains.kotlin.descriptors.impl.AbstractTypeAliasDescriptor$typeConstructor$1.getSupertypes(AbstractTypeAliasDescriptor.kt:108)
	at org.jetbrains.kotlin.descriptors.impl.AbstractTypeAliasDescriptor$typeConstructor$1.getSupertypes(AbstractTypeAliasDescriptor.kt:108)
...

Expected behaviour No exception is thrown.

To Reproduce I tracked it down to our expect/actual typealias for WeakReference on iOS: Common:

public expect class WeakReference<T: Any>(referred: T) {
    public fun get(): T?
}

iOS Implementation:

import kotlin.native.ref.WeakReference

internal actual typealias WeakReference<T> = WeakReference<T>

It seems that Dokka gets confused by the WeakReference<T> = WeakReference<T> part and ends up in infinite recursion. The following workaround fixes the problem:

internal actual typealias WeakReference<T> = kotlin.native.ref.WeakReference<T>

Dokka configuration Just running ./gradlew dokkaHtml

Installation

  • Operating system: macOS
  • Build tool: Gradle v7.2
  • Dokka version: 1.6.0, also reproduced with current master (1.6.10-SNAPSHOT)

Are you willing to provide a PR? Possibly - need a hint where to start though.

karelp avatar Dec 20 '21 14:12 karelp

Unfortunately, I can not reproduce this with your configuration. My sample project is here to test this. It works fine. Can you provide your sample project to reproduce this?

I suppose you have ambiguous WeakReference<T> in your code despite import kotlin.native.ref.WeakReference. Have you a warning in IDE for actual typealias WeakReference<T> = WeakReference<T>?

vmishenev avatar May 18 '22 17:05 vmishenev

I tried your version and it is indeed not happening there. However, I still get the same problem in our project even with the newest Dokka version. I’ll investigate further and try to reproduce it within a smaller project.

karelp avatar May 21 '22 12:05 karelp

I have exactly the same issue and same as in the original post it was with WeakReference type aliases.

  • Operating system: masOS 12.5.1
  • Gradle 7.4.2
  • Dokka 1.6.12
  • Kotlin 1.7.0

The suggested workaround to use a fully qualified name instead of using imports works like a charm. The other workaround that came to my mind is to use import aliases

import kotlin.native.ref.WeakReference as NativeWeakReference

actual typealias WeakReference<T> = NativeWeakReference<T>

Import aliases mitigate the ambiguity in a pretty much same way as fully qualified names though may look a bit nicer.

anthony-novikov avatar Sep 08 '22 13:09 anthony-novikov

@anthony-novikov Can you provide your sample project to reproduce this?

vmishenev avatar Sep 08 '22 16:09 vmishenev

@vmishenev I experienced this issue on one of our internal projects that I can't share, but I'll try to extract the minimal configuration to a demo project and if it's still reproducible I'll share it to you tomorrow.

anthony-novikov avatar Sep 08 '22 16:09 anthony-novikov

It appears extracting the code to a demo app didn't take that much time as I initially thought, so here it is: https://github.com/anthony-novikov/dokka-weak-ref-demo. Hopefully this will help.

anthony-novikov avatar Sep 08 '22 18:09 anthony-novikov

Thank you for the reproducer. It helped to detect a problem.

The fix will be in Dokka 1.7.20.

vmishenev avatar Sep 12 '22 10:09 vmishenev